mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-22 09:37:12 +08:00
2901d8e5bb
2. Removed client cert validation from HTTPS client and server as this is not supported on all platforms, e.g. .Net4.6. 3. Reverted back to XmlDocument from XDocument as this is now supported via a NuGet package on UWP and XDocument is not supported on .Net4.6. 4. Baseline .Net4.6 samples and assoc. controls project check-in, not working yet. 5. Moved UWP client controls from SDK to Samples directly.
1559 lines
47 KiB
C#
1559 lines
47 KiB
C#
/* ========================================================================
|
|
* Copyright (c) 2005-2013 The OPC Foundation, Inc. All rights reserved.
|
|
*
|
|
* OPC Foundation MIT License 1.00
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use,
|
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following
|
|
* conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* The complete license agreement can be found here:
|
|
* http://opcfoundation.org/License/MIT/1.00/
|
|
* ======================================================================*/
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Drawing;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.Xml;
|
|
using System.Reflection;
|
|
|
|
namespace Opc.Ua.Client.Controls.Common
|
|
{
|
|
/// <summary>
|
|
/// Allows the user to edit a complex value.
|
|
/// </summary>
|
|
public partial class EditComplexValueCtrl : UserControl
|
|
{
|
|
/// <summary>
|
|
/// Constructs the object.
|
|
/// </summary>
|
|
public EditComplexValueCtrl()
|
|
{
|
|
InitializeComponent();
|
|
MaxDisplayTextLength = 100;
|
|
ValuesDV.AutoGenerateColumns = false;
|
|
ImageList = new ClientUtils().ImageList;
|
|
|
|
m_dataset = new DataSet();
|
|
m_dataset.Tables.Add("Values");
|
|
|
|
m_dataset.Tables[0].Columns.Add("AccessInfo", typeof(AccessInfo));
|
|
m_dataset.Tables[0].Columns.Add("Name", typeof(string));
|
|
m_dataset.Tables[0].Columns.Add("DataType", typeof(string));
|
|
m_dataset.Tables[0].Columns.Add("Value", typeof(string));
|
|
m_dataset.Tables[0].Columns.Add("Icon", typeof(Image));
|
|
|
|
ValuesDV.DataSource = m_dataset.Tables[0];
|
|
}
|
|
|
|
#region Private Fields
|
|
private DataSet m_dataset;
|
|
private Session m_session;
|
|
private AccessInfo m_value;
|
|
private bool m_readOnly;
|
|
private int m_maxDisplayTextLength;
|
|
private event EventHandler m_ValueChanged;
|
|
#endregion
|
|
|
|
private class AccessInfo
|
|
{
|
|
public AccessInfo Parent { get; set; }
|
|
public PropertyInfo PropertyInfo { get; set; }
|
|
public int[] Indexes;
|
|
public TypeInfo TypeInfo;
|
|
public object Value;
|
|
public string Name;
|
|
}
|
|
|
|
#region Public Members
|
|
/// <summary>
|
|
/// The maximum length of a value string displayed in a column.
|
|
/// </summary>
|
|
[DefaultValue(100)]
|
|
public int MaxDisplayTextLength
|
|
{
|
|
get
|
|
{
|
|
return m_maxDisplayTextLength;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (value < 20)
|
|
{
|
|
m_maxDisplayTextLength = 20;
|
|
}
|
|
|
|
m_maxDisplayTextLength = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the Back command can be called.
|
|
/// </summary>
|
|
public bool CanGoBack
|
|
{
|
|
get
|
|
{
|
|
return (NavigationMENU.Items.Count > 1);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the ArraySize can be changed.
|
|
/// </summary>
|
|
public bool CanSetArraySize
|
|
{
|
|
get
|
|
{
|
|
if (m_readOnly)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
AccessInfo info = NavigationMENU.Items[NavigationMENU.Items.Count - 1].Tag as AccessInfo;
|
|
|
|
if (info != null)
|
|
{
|
|
return info.TypeInfo.ValueRank >= 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the data type can be changed.
|
|
/// </summary>
|
|
public bool CanChangeType
|
|
{
|
|
get
|
|
{
|
|
if (m_readOnly)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (NavigationMENU.Items.Count > 0)
|
|
{
|
|
AccessInfo info = NavigationMENU.Items[NavigationMENU.Items.Count - 1].Tag as AccessInfo;
|
|
|
|
if (info != null)
|
|
{
|
|
return info.Parent != null && info.Parent.TypeInfo != null && info.Parent.TypeInfo.BuiltInType == BuiltInType.Variant;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the current data type.
|
|
/// </summary>
|
|
public BuiltInType CurrentType
|
|
{
|
|
get
|
|
{
|
|
if (NavigationMENU.Items.Count > 0)
|
|
{
|
|
AccessInfo info = NavigationMENU.Items[NavigationMENU.Items.Count - 1].Tag as AccessInfo;
|
|
|
|
if (info != null)
|
|
{
|
|
Variant? value = info.Value as Variant?;
|
|
|
|
if (value != null && value.Value.TypeInfo != null)
|
|
{
|
|
return value.Value.TypeInfo.BuiltInType;
|
|
}
|
|
|
|
return info.TypeInfo.BuiltInType;
|
|
}
|
|
}
|
|
|
|
return BuiltInType.Variant;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Raised when the value is changed.
|
|
/// </summary>
|
|
public event EventHandler ValueChanged
|
|
{
|
|
add { m_ValueChanged += value; }
|
|
remove { m_ValueChanged -= value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes the session used for editing the value.
|
|
/// </summary>
|
|
public void ChangeSession(Session session)
|
|
{
|
|
m_session = session;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Moves the displayed value back.
|
|
/// </summary>
|
|
public void Back()
|
|
{
|
|
if (!CanGoBack)
|
|
{
|
|
return;
|
|
}
|
|
|
|
NavigationMENU_Click(NavigationMENU.Items[NavigationMENU.Items.Count - 2], null);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Changes the array size.
|
|
/// </summary>
|
|
public void SetArraySize()
|
|
{
|
|
if (!CanSetArraySize)
|
|
{
|
|
return;
|
|
}
|
|
|
|
EndEdit();
|
|
|
|
AccessInfo info = NavigationMENU.Items[NavigationMENU.Items.Count - 1].Tag as AccessInfo;
|
|
|
|
TypeInfo currentType = info.TypeInfo;
|
|
object currentValue = info.Value;
|
|
|
|
if (info.Value is Variant)
|
|
{
|
|
Variant variant = (Variant)info.Value;
|
|
currentValue = variant.Value;
|
|
|
|
if (currentValue != null)
|
|
{
|
|
currentType = variant.TypeInfo;
|
|
|
|
if (currentType == null)
|
|
{
|
|
currentType = TypeInfo.Construct(currentValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
int[] dimensions = null;
|
|
|
|
Array array = currentValue as Array;
|
|
|
|
if (array != null)
|
|
{
|
|
dimensions = new int[array.Rank];
|
|
|
|
for (int ii = 0; ii < array.Rank; ii++)
|
|
{
|
|
dimensions[ii] = array.GetLength(ii);
|
|
}
|
|
}
|
|
|
|
IList list = currentValue as IList;
|
|
|
|
if (array == null && list != null)
|
|
{
|
|
dimensions = new int[1];
|
|
dimensions[0] = list.Count;
|
|
}
|
|
|
|
Matrix matrix = currentValue as Matrix;
|
|
|
|
if (matrix != null)
|
|
{
|
|
dimensions = matrix.Dimensions;
|
|
array = matrix.ToArray();
|
|
}
|
|
|
|
SetTypeDlg.SetTypeResult result = new SetTypeDlg().ShowDialog(currentType, dimensions);
|
|
|
|
if (result == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// convert to new type.
|
|
object newValue = currentValue;
|
|
|
|
if (result.ArrayDimensions == null || result.ArrayDimensions.Length < 1)
|
|
{
|
|
newValue = Convert(currentValue, currentType, result.TypeInfo, result.UseDefaultOnError);
|
|
}
|
|
else
|
|
{
|
|
if (array == null && list != null)
|
|
{
|
|
Type elementType = GetListElementType(list);
|
|
|
|
for (int ii = result.ArrayDimensions[0]; ii < list.Count; ii++)
|
|
{
|
|
list.RemoveAt(ii);
|
|
}
|
|
|
|
for (int ii = list.Count; ii < result.ArrayDimensions[0]; ii++)
|
|
{
|
|
list.Add(Activator.CreateInstance(elementType));
|
|
}
|
|
|
|
newValue = list;
|
|
}
|
|
|
|
if (array != null)
|
|
{
|
|
Array newArray = null;
|
|
|
|
if (currentValue is Array)
|
|
{
|
|
newArray = Array.CreateInstance(currentValue.GetType().GetElementType(), result.ArrayDimensions);
|
|
}
|
|
else
|
|
{
|
|
newArray = TypeInfo.CreateArray(result.TypeInfo.BuiltInType, result.ArrayDimensions);
|
|
}
|
|
|
|
int maxCount = result.ArrayDimensions[0];
|
|
|
|
for (int ii = 1; ii < result.ArrayDimensions.Length; ii++)
|
|
{
|
|
maxCount *= result.ArrayDimensions[ii];
|
|
}
|
|
|
|
int count = 0;
|
|
|
|
foreach (object element in array)
|
|
{
|
|
if (maxCount <= count)
|
|
{
|
|
break;
|
|
}
|
|
|
|
object newElement = Convert(element, currentType, result.TypeInfo, result.UseDefaultOnError);
|
|
int[] indexes = GetIndexFromCount(count++, result.ArrayDimensions);
|
|
newArray.SetValue(newElement, indexes);
|
|
}
|
|
|
|
newValue = newArray;
|
|
}
|
|
}
|
|
|
|
NavigationMENU.Items.RemoveAt(NavigationMENU.Items.Count - 1);
|
|
|
|
info.TypeInfo = result.TypeInfo;
|
|
info.Value = newValue;
|
|
ShowValue(info);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes the data type.
|
|
/// </summary>
|
|
public void SetType(BuiltInType builtInType)
|
|
{
|
|
if (!CanChangeType)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AccessInfo info = NavigationMENU.Items[NavigationMENU.Items.Count - 1].Tag as AccessInfo;
|
|
|
|
TypeInfo currentType = info.TypeInfo;
|
|
object currentValue = info.Value;
|
|
|
|
try
|
|
{
|
|
EndEdit();
|
|
currentValue = info.Value;
|
|
}
|
|
catch (Exception)
|
|
{
|
|
currentValue = TypeInfo.GetDefaultValue(currentType.BuiltInType);
|
|
}
|
|
|
|
if (info.Value is Variant)
|
|
{
|
|
Variant variant = (Variant)info.Value;
|
|
currentValue = variant.Value;
|
|
|
|
if (currentValue != null)
|
|
{
|
|
currentType = variant.TypeInfo;
|
|
|
|
if (currentType == null)
|
|
{
|
|
currentType = TypeInfo.Construct(currentValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
TypeInfo targetType = new TypeInfo(builtInType, currentType.ValueRank);
|
|
object newValue = Convert(currentValue, currentType, targetType, true);
|
|
|
|
NavigationMENU.Items.RemoveAt(NavigationMENU.Items.Count - 1);
|
|
|
|
info.TypeInfo = targetType;
|
|
info.Value = newValue;
|
|
ShowValueNoNotify(info);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts the old type to the new type.
|
|
/// </summary>
|
|
private object Convert(object oldValue, TypeInfo oldType, TypeInfo newType, bool useDefaultOnError)
|
|
{
|
|
object newValue = oldValue;
|
|
|
|
if (oldType.BuiltInType != newType.BuiltInType)
|
|
{
|
|
try
|
|
{
|
|
newValue = TypeInfo.Cast(oldValue, oldType, newType.BuiltInType);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (!useDefaultOnError)
|
|
{
|
|
throw new FormatException("Could not cast value to requested type.", e);
|
|
}
|
|
|
|
newValue = TypeInfo.GetDefaultValue(newType.BuiltInType);
|
|
}
|
|
}
|
|
|
|
return newValue;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays the value in the control.
|
|
/// </summary>
|
|
public void ShowValue(
|
|
NodeId nodeId,
|
|
uint attributeId,
|
|
string name,
|
|
object value,
|
|
bool readOnly)
|
|
{
|
|
m_readOnly = readOnly;
|
|
NavigationMENU.Items.Clear();
|
|
|
|
if (m_readOnly)
|
|
{
|
|
ValuesDV.EditMode = DataGridViewEditMode.EditProgrammatically;
|
|
TextValueTB.ReadOnly = true;
|
|
}
|
|
|
|
Type type = null;
|
|
|
|
// determine the expected data type for non-value attributes.
|
|
if (attributeId != 0 && attributeId != Attributes.Value)
|
|
{
|
|
BuiltInType builtInType = TypeInfo.GetBuiltInType(Attributes.GetDataTypeId(attributeId));
|
|
int valueRank = Attributes.GetValueRank(attributeId);
|
|
type = TypeInfo.GetSystemType(builtInType, valueRank);
|
|
}
|
|
|
|
// determine the expected data type for value attributes.
|
|
else if (!NodeId.IsNull(nodeId))
|
|
{
|
|
IVariableBase variable = m_session.NodeCache.Find(nodeId) as IVariableBase;
|
|
|
|
if (variable != null)
|
|
{
|
|
BuiltInType builtInType = TypeInfo.GetBuiltInType(variable.DataType, m_session.TypeTree);
|
|
int valueRank = variable.ValueRank;
|
|
type = TypeInfo.GetSystemType(builtInType, valueRank);
|
|
|
|
if (builtInType == BuiltInType.ExtensionObject && valueRank < 0)
|
|
{
|
|
type = TypeInfo.GetSystemType(variable.DataType, m_session.Factory);
|
|
}
|
|
}
|
|
}
|
|
|
|
// use the value.
|
|
else if (value != null)
|
|
{
|
|
type = value.GetType();
|
|
}
|
|
|
|
// go with default.
|
|
else
|
|
{
|
|
type = typeof(string);
|
|
}
|
|
|
|
// assign a name.
|
|
if (String.IsNullOrEmpty(name))
|
|
{
|
|
if (attributeId != 0)
|
|
{
|
|
name = Attributes.GetBrowseName(attributeId);
|
|
}
|
|
else
|
|
{
|
|
name = type.Name;
|
|
}
|
|
}
|
|
|
|
AccessInfo info = new AccessInfo();
|
|
info.Value = Utils.Clone(value);
|
|
info.TypeInfo = TypeInfo.Construct(type);
|
|
|
|
if (value == null && info.TypeInfo.ValueRank < 0)
|
|
{
|
|
info.Value = TypeInfo.GetDefaultValue(info.TypeInfo.BuiltInType);
|
|
}
|
|
|
|
info.Name = name;
|
|
m_value = info;
|
|
|
|
ShowValue(info);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays the value in the control.
|
|
/// </summary>
|
|
public void ShowValue(
|
|
string name,
|
|
NodeId dataType,
|
|
int valueRank,
|
|
object value)
|
|
{
|
|
if (value == null && m_session != null)
|
|
{
|
|
BuiltInType builtInType = TypeInfo.GetBuiltInType(dataType, m_session.TypeTree);
|
|
|
|
if (builtInType == BuiltInType.ExtensionObject)
|
|
{
|
|
Type type = m_session.Factory.GetSystemType(dataType);
|
|
|
|
if (type != null)
|
|
{
|
|
if (valueRank < 0)
|
|
{
|
|
value = Activator.CreateInstance(type);
|
|
}
|
|
else
|
|
{
|
|
value = Array.CreateInstance(type, new int[valueRank]);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
value = TypeInfo.GetDefaultValue(dataType, valueRank, m_session.TypeTree);
|
|
}
|
|
}
|
|
|
|
ShowValue(null, name, value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays the value in the control.
|
|
/// </summary>
|
|
public void ShowValue(
|
|
TypeInfo expectedType,
|
|
string name,
|
|
object value)
|
|
{
|
|
m_readOnly = false;
|
|
NavigationMENU.Items.Clear();
|
|
|
|
// assign a type.
|
|
if (expectedType == null)
|
|
{
|
|
if (value == null)
|
|
{
|
|
expectedType = TypeInfo.Scalars.String;
|
|
}
|
|
else
|
|
{
|
|
expectedType = TypeInfo.Construct(value);
|
|
}
|
|
}
|
|
|
|
// assign a name.
|
|
if (String.IsNullOrEmpty(name))
|
|
{
|
|
name = expectedType.ToString();
|
|
}
|
|
|
|
AccessInfo info = new AccessInfo();
|
|
info.Value = Utils.Clone(value);
|
|
info.TypeInfo = expectedType;
|
|
|
|
if (value == null && info.TypeInfo.ValueRank < 0)
|
|
{
|
|
info.Value = TypeInfo.GetDefaultValue(info.TypeInfo.BuiltInType);
|
|
}
|
|
|
|
// ensure value is the target type.
|
|
info.Value = TypeInfo.Cast(info.Value, expectedType.BuiltInType);
|
|
|
|
info.Name = name;
|
|
m_value = info;
|
|
|
|
ShowValue(info);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the edited value.
|
|
/// </summary>
|
|
public object GetValue()
|
|
{
|
|
return m_value.Value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Validates the value currently being edited.
|
|
/// </summary>
|
|
public void EndEdit()
|
|
{
|
|
if (NavigationMENU.Items.Count < 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!TextValueTB.Visible)
|
|
{
|
|
ValuesDV.EndEdit();
|
|
return;
|
|
}
|
|
|
|
AccessInfo info = NavigationMENU.Items[NavigationMENU.Items.Count - 1].Tag as AccessInfo;
|
|
object newValue = TypeInfo.Cast(TextValueTB.Text, info.TypeInfo.BuiltInType);
|
|
info.Value = newValue;
|
|
UpdateParent(info);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays the value in the control.
|
|
/// </summary>
|
|
private void ShowValue(AccessInfo parent)
|
|
{
|
|
ShowValueNoNotify(parent);
|
|
|
|
if (m_ValueChanged != null)
|
|
{
|
|
m_ValueChanged(this, null);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays the value in the control.
|
|
/// </summary>
|
|
private void ShowValueNoNotify(AccessInfo parent)
|
|
{
|
|
m_dataset.Tables[0].Clear();
|
|
ValuesDV.Visible = true;
|
|
TextValueTB.Visible = false;
|
|
|
|
ToolStripItem item = NavigationMENU.Items.Add(parent.Name);
|
|
item.Click += new EventHandler(NavigationMENU_Click);
|
|
item.Tag = parent;
|
|
|
|
TypeInfo typeInfo = parent.TypeInfo;
|
|
object value = parent.Value;
|
|
|
|
if (value is Variant)
|
|
{
|
|
Variant variant = (Variant)value;
|
|
value = variant.Value;
|
|
|
|
if (value != null)
|
|
{
|
|
parent.TypeInfo = typeInfo = variant.TypeInfo;
|
|
|
|
if (typeInfo == null)
|
|
{
|
|
parent.TypeInfo = typeInfo = TypeInfo.Construct(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (typeInfo.ValueRank >= 0)
|
|
{
|
|
Matrix matrix = value as Matrix;
|
|
|
|
if (matrix != null)
|
|
{
|
|
value = matrix.ToArray();
|
|
}
|
|
|
|
System.Collections.IEnumerable enumerable = value as System.Collections.IEnumerable;
|
|
|
|
if (enumerable != null)
|
|
{
|
|
// get the dimensions of any array.
|
|
int[] dimensions = null;
|
|
|
|
// calculate them.
|
|
if (matrix == null)
|
|
{
|
|
Array array = enumerable as Array;
|
|
|
|
if (array != null)
|
|
{
|
|
dimensions = new int[array.Rank];
|
|
|
|
for (int ii = 0; ii < array.Rank; ii++)
|
|
{
|
|
dimensions[ii] = array.GetLength(ii);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dimensions = new int[1];
|
|
System.Collections.IList list = enumerable as System.Collections.IList;
|
|
|
|
if (list != null)
|
|
{
|
|
dimensions[0] = list.Count;
|
|
}
|
|
}
|
|
}
|
|
|
|
// get them from the matrix.
|
|
else
|
|
{
|
|
dimensions = matrix.Dimensions;
|
|
}
|
|
|
|
// display the array elements.
|
|
int count = 0;
|
|
TypeInfo elementType = new TypeInfo(typeInfo.BuiltInType, ValueRanks.Scalar);
|
|
|
|
ValuesDV.Visible = true;
|
|
TextValueTB.Visible = false;
|
|
|
|
foreach (object element in enumerable)
|
|
{
|
|
int[] indexes = GetIndexFromCount(count++, dimensions);
|
|
|
|
AccessInfo info = new AccessInfo();
|
|
info.Parent = parent;
|
|
info.Indexes = indexes;
|
|
info.TypeInfo = elementType;
|
|
info.Value = element;
|
|
|
|
ShowIndexedValue(info);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// check for null.
|
|
if (value == null)
|
|
{
|
|
if (parent.Parent != null && parent.Parent.Value is Array)
|
|
{
|
|
parent.Value = value = Activator.CreateInstance(parent.Parent.Value.GetType().GetElementType());
|
|
}
|
|
else if (parent.Parent != null && parent.PropertyInfo != null)
|
|
{
|
|
parent.Value = value = Activator.CreateInstance(parent.PropertyInfo.PropertyType);
|
|
}
|
|
else
|
|
{
|
|
ShowTextValue(value, parent.TypeInfo);
|
|
return;
|
|
}
|
|
}
|
|
|
|
object structure = value;
|
|
|
|
// check for extension object.
|
|
ExtensionObject extension = structure as ExtensionObject;
|
|
|
|
if (extension != null)
|
|
{
|
|
structure = extension.Body;
|
|
}
|
|
|
|
// check for XmlElements.
|
|
if (structure is XmlElement)
|
|
{
|
|
ShowTextValue((XmlElement)structure);
|
|
return;
|
|
}
|
|
|
|
// check for ByteString.
|
|
if (structure is byte[])
|
|
{
|
|
ShowTextValue((byte[])structure);
|
|
return;
|
|
}
|
|
|
|
// check for NodeId.
|
|
if (structure is NodeId)
|
|
{
|
|
ShowTextValue(((NodeId)structure).ToString());
|
|
return;
|
|
}
|
|
|
|
// check for ExpandedNodeId.
|
|
if (structure is ExpandedNodeId)
|
|
{
|
|
ShowTextValue(((ExpandedNodeId)structure).ToString());
|
|
return;
|
|
}
|
|
|
|
// check for QualifiedName.
|
|
if (structure is QualifiedName)
|
|
{
|
|
ShowTextValue(((QualifiedName)structure).ToString());
|
|
return;
|
|
}
|
|
|
|
// check for Guid.
|
|
if (structure is Guid)
|
|
{
|
|
ShowTextValue(((Guid)structure).ToString());
|
|
return;
|
|
}
|
|
|
|
// check for Uuid.
|
|
if (structure is Uuid)
|
|
{
|
|
ShowTextValue(((Uuid)structure).ToString());
|
|
return;
|
|
}
|
|
|
|
// check for StatusCode.
|
|
if (structure is StatusCode)
|
|
{
|
|
ShowTextValue(Utils.Format("0x{0:X8}", ((StatusCode)structure).Code));
|
|
return;
|
|
}
|
|
|
|
ValuesDV.Visible = true;
|
|
TextValueTB.Visible = false;
|
|
|
|
// use reflection to display the properties of the structure.
|
|
bool isStructure = false;
|
|
PropertyInfo[] properties = structure.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
|
|
|
foreach (PropertyInfo property in properties)
|
|
{
|
|
if (property.GetIndexParameters().Length > 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
object element = property.GetValue(structure, null);
|
|
|
|
string name = null;
|
|
|
|
foreach (object attribute in property.GetCustomAttributes(true))
|
|
{
|
|
if (typeof(System.Runtime.Serialization.DataMemberAttribute).IsInstanceOfType(attribute))
|
|
{
|
|
name = ((System.Runtime.Serialization.DataMemberAttribute)attribute).Name;
|
|
|
|
if (name == null)
|
|
{
|
|
name = property.Name;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (name == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
AccessInfo info = new AccessInfo();
|
|
info.Parent = parent;
|
|
info.PropertyInfo = property;
|
|
info.TypeInfo = TypeInfo.Construct(property.PropertyType);
|
|
info.Value = element;
|
|
info.Name = name;
|
|
|
|
ShowNamedValue(info);
|
|
isStructure = true;
|
|
}
|
|
|
|
if (!isStructure)
|
|
{
|
|
ShowTextValue(parent.Value, parent.TypeInfo);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
/// <summary>
|
|
/// Returns the index based on the current count.
|
|
/// </summary>
|
|
private int[] GetIndexFromCount(int count, int[] dimensions)
|
|
{
|
|
int[] indexes = new int[(dimensions != null) ? dimensions.Length : 1];
|
|
|
|
for (int ii = indexes.Length - 1; ii >= 0; ii--)
|
|
{
|
|
indexes[ii] = count % dimensions[ii];
|
|
count /= dimensions[ii];
|
|
}
|
|
|
|
return indexes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the value at an array index to the control.
|
|
/// </summary>
|
|
private void ShowIndexedValue(AccessInfo info)
|
|
{
|
|
DataRow row = m_dataset.Tables[0].NewRow();
|
|
|
|
StringBuilder buffer = new StringBuilder();
|
|
buffer.Append("[");
|
|
|
|
if (info.Indexes != null)
|
|
{
|
|
for (int ii = 0; ii < info.Indexes.Length; ii++)
|
|
{
|
|
if (ii > 0)
|
|
{
|
|
buffer.Append(",");
|
|
}
|
|
|
|
buffer.Append(info.Indexes[ii]);
|
|
}
|
|
}
|
|
|
|
buffer.Append("]");
|
|
info.Name = buffer.ToString();
|
|
|
|
row[0] = info;
|
|
row[1] = info.Name;
|
|
row[2] = GetDataTypeString(info);
|
|
row[3] = ValueToString(info.Value, info.TypeInfo);
|
|
row[4] = ImageList.Images[ClientUtils.GetImageIndex(Attributes.Value, info.Value)];
|
|
|
|
m_dataset.Tables[0].Rows.Add(row);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the element type for a list.
|
|
/// </summary>
|
|
private Type GetListElementType(IList list)
|
|
{
|
|
if (list != null)
|
|
{
|
|
for (Type type = list.GetType(); type != null; type = type.BaseType)
|
|
{
|
|
if (type.IsGenericType)
|
|
{
|
|
Type[] argTypes = type.GetGenericArguments();
|
|
|
|
if (argTypes.Length > 0)
|
|
{
|
|
return argTypes[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return typeof(object);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the data type of the value.
|
|
/// </summary>
|
|
private Type GetDataType(AccessInfo accessInfo)
|
|
{
|
|
if (accessInfo == null || accessInfo.TypeInfo == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (accessInfo.TypeInfo.BuiltInType == BuiltInType.ExtensionObject)
|
|
{
|
|
if (accessInfo.Value != null)
|
|
{
|
|
return accessInfo.Value.GetType();
|
|
}
|
|
|
|
if (accessInfo.PropertyInfo != null)
|
|
{
|
|
return accessInfo.PropertyInfo.PropertyType;
|
|
}
|
|
|
|
if (accessInfo.Parent != null)
|
|
{
|
|
if (accessInfo.Parent.Value is Array)
|
|
{
|
|
Array array = (Array)accessInfo.Parent.Value;
|
|
return array.GetType().GetElementType();
|
|
}
|
|
|
|
if (accessInfo.Parent.Value is IList)
|
|
{
|
|
IList list = (IList)accessInfo.Parent.Value;
|
|
return GetListElementType(list);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TypeInfo.GetSystemType(accessInfo.TypeInfo.BuiltInType, accessInfo.TypeInfo.ValueRank);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the data type of the value.
|
|
/// </summary>
|
|
private string GetDataTypeString(AccessInfo accessInfo)
|
|
{
|
|
Type type = GetDataType(accessInfo);
|
|
|
|
if (type == null)
|
|
{
|
|
return accessInfo.TypeInfo.ToString();
|
|
}
|
|
|
|
return type.Name;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds the value with the specified name to the control.
|
|
/// </summary>
|
|
private void ShowNamedValue(AccessInfo info)
|
|
{
|
|
DataRow row = m_dataset.Tables[0].NewRow();
|
|
|
|
row[0] = info;
|
|
row[1] = (info.Name != null) ? info.Name : "unknown";
|
|
row[2] = GetDataTypeString(info);
|
|
row[3] = ValueToString(info.Value, info.TypeInfo);
|
|
row[4] = ImageList.Images[ClientUtils.GetImageIndex(Attributes.Value, info.Value)];
|
|
|
|
m_dataset.Tables[0].Rows.Add(row);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays a value in the control.
|
|
/// </summary>
|
|
private void ShowTextValue(object value, TypeInfo typeInfo)
|
|
{
|
|
switch (typeInfo.BuiltInType)
|
|
{
|
|
case BuiltInType.ByteString:
|
|
{
|
|
ShowTextValue((byte[])value);
|
|
break;
|
|
}
|
|
|
|
case BuiltInType.XmlElement:
|
|
{
|
|
ShowTextValue((XmlElement)value);
|
|
break;
|
|
}
|
|
|
|
case BuiltInType.String:
|
|
{
|
|
ShowTextValue((string)value);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
ShowTextValue(ValueToString(value, typeInfo));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays a string in the control.
|
|
/// </summary>
|
|
private void ShowTextValue(string value)
|
|
{
|
|
ValuesDV.Visible = false;
|
|
TextValueTB.Visible = true;
|
|
|
|
if (value != null && value.Length > MaxDisplayTextLength)
|
|
{
|
|
TextValueTB.ScrollBars = ScrollBars.Both;
|
|
}
|
|
else
|
|
{
|
|
TextValueTB.ScrollBars = ScrollBars.None;
|
|
}
|
|
|
|
TextValueTB.Font = new Font("Segoe UI", TextValueTB.Font.Size);
|
|
TextValueTB.Text = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays a complete byte string in the control.
|
|
/// </summary>
|
|
private void ShowTextValue(byte[] value)
|
|
{
|
|
ValuesDV.Visible = false;
|
|
TextValueTB.Visible = true;
|
|
|
|
StringBuilder buffer = new StringBuilder();
|
|
|
|
if (value != null)
|
|
{
|
|
for (int ii = 0; ii < value.Length; ii++)
|
|
{
|
|
if (buffer.Length > 0 && (ii % 30) == 0)
|
|
{
|
|
buffer.Append("\r\n");
|
|
}
|
|
|
|
buffer.AppendFormat("{0:X2} ", value[ii]);
|
|
}
|
|
}
|
|
|
|
TextValueTB.Font = new Font("Courier New", TextValueTB.Font.Size);
|
|
TextValueTB.Text = buffer.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Displays a complete XML element in the control.
|
|
/// </summary>
|
|
private void ShowTextValue(XmlElement value)
|
|
{
|
|
ValuesDV.Visible = false;
|
|
TextValueTB.Visible = true;
|
|
|
|
StringBuilder buffer = new StringBuilder();
|
|
|
|
if (value != null)
|
|
{
|
|
XmlWriterSettings settings = new XmlWriterSettings();
|
|
settings.Indent = true;
|
|
settings.OmitXmlDeclaration = true;
|
|
settings.NewLineHandling = NewLineHandling.Replace;
|
|
settings.NewLineChars = "\r\n";
|
|
settings.IndentChars = " ";
|
|
|
|
using (XmlWriter writer = XmlWriter.Create(buffer, settings))
|
|
{
|
|
using (XmlNodeReader reader = new XmlNodeReader(value))
|
|
{
|
|
writer.WriteNode(reader, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
TextValueTB.Font = new Font("Courier New", TextValueTB.Font.Size);
|
|
TextValueTB.Text = buffer.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Converts a value to a string for display in the grid.
|
|
/// </summary>
|
|
private string ValueToString(object value, TypeInfo typeInfo)
|
|
{
|
|
if (value == null)
|
|
{
|
|
return String.Empty;
|
|
}
|
|
|
|
if (value is Variant)
|
|
{
|
|
Variant variant = (Variant)value;
|
|
value = variant.Value;
|
|
|
|
if (value != null)
|
|
{
|
|
typeInfo = variant.TypeInfo;
|
|
|
|
if (typeInfo == null)
|
|
{
|
|
typeInfo = TypeInfo.Construct(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (typeInfo.ValueRank >= 0)
|
|
{
|
|
StringBuilder buffer = new StringBuilder();
|
|
|
|
System.Collections.IEnumerable enumerable = value as System.Collections.IEnumerable;
|
|
|
|
if (enumerable != null)
|
|
{
|
|
buffer.Append("{ ");
|
|
|
|
foreach (object element in enumerable)
|
|
{
|
|
if (buffer.Length > 2)
|
|
{
|
|
buffer.Append(" | ");
|
|
}
|
|
|
|
if (buffer.Length > MaxDisplayTextLength)
|
|
{
|
|
buffer.Append("...");
|
|
break;
|
|
}
|
|
|
|
buffer.Append(ValueToString(element, new TypeInfo(typeInfo.BuiltInType, ValueRanks.Scalar)));
|
|
}
|
|
|
|
buffer.Append(" }");
|
|
}
|
|
|
|
return buffer.ToString();
|
|
}
|
|
|
|
switch (typeInfo.BuiltInType)
|
|
{
|
|
case BuiltInType.String:
|
|
{
|
|
string text = (string)value;
|
|
|
|
if (text != null && text.Length > MaxDisplayTextLength)
|
|
{
|
|
return text.Substring(0, MaxDisplayTextLength) + "...";
|
|
}
|
|
|
|
return text;
|
|
}
|
|
|
|
case BuiltInType.ByteString:
|
|
{
|
|
StringBuilder buffer = new StringBuilder();
|
|
|
|
byte[] bytes = (byte[])value;
|
|
|
|
for (int ii = 0; ii < bytes.Length; ii++)
|
|
{
|
|
if (buffer.Length > MaxDisplayTextLength)
|
|
{
|
|
buffer.Append("...");
|
|
break;
|
|
}
|
|
|
|
buffer.AppendFormat("{0:X2}", bytes[ii]);
|
|
}
|
|
|
|
return buffer.ToString();
|
|
}
|
|
|
|
case BuiltInType.Enumeration:
|
|
{
|
|
return ((int)value).ToString();
|
|
}
|
|
|
|
case BuiltInType.ExtensionObject:
|
|
{
|
|
string text = null;
|
|
|
|
ExtensionObject extension = value as ExtensionObject;
|
|
|
|
if (extension != null)
|
|
{
|
|
if (extension.Body is byte[])
|
|
{
|
|
return ValueToString(extension.Body, new TypeInfo(BuiltInType.ByteString, ValueRanks.Scalar));
|
|
}
|
|
|
|
if (extension.Body is XmlElement)
|
|
{
|
|
return ValueToString(extension.Body, new TypeInfo(BuiltInType.XmlElement, ValueRanks.Scalar));
|
|
}
|
|
|
|
if (extension.Body is IEncodeable)
|
|
{
|
|
text = new Variant(extension).ToString();
|
|
}
|
|
}
|
|
|
|
if (text == null)
|
|
{
|
|
IEncodeable encodeable = value as IEncodeable;
|
|
|
|
if (encodeable != null)
|
|
{
|
|
text = new Variant(encodeable).ToString();
|
|
}
|
|
}
|
|
|
|
if (text != null && text.Length > MaxDisplayTextLength)
|
|
{
|
|
return text.Substring(0, MaxDisplayTextLength) + "...";
|
|
}
|
|
|
|
return text;
|
|
}
|
|
}
|
|
|
|
return (string)TypeInfo.Cast(value, BuiltInType.String);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether the value can be edited in the grid view.
|
|
/// </summary>
|
|
private bool IsSimpleValue(AccessInfo info)
|
|
{
|
|
if (info == null || info.TypeInfo == null)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
TypeInfo typeInfo = info.TypeInfo;
|
|
object value = info.Value;
|
|
|
|
if (value is Variant)
|
|
{
|
|
Variant variant = (Variant)info.Value;
|
|
typeInfo = variant.TypeInfo;
|
|
value = variant.Value;
|
|
|
|
if (typeInfo == null)
|
|
{
|
|
typeInfo = TypeInfo.Construct(value);
|
|
}
|
|
}
|
|
|
|
if (typeInfo.ValueRank >= 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
switch (typeInfo.BuiltInType)
|
|
{
|
|
case BuiltInType.String:
|
|
{
|
|
string text = value as string;
|
|
|
|
if (text != null && text.Length >= MaxDisplayTextLength)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
case BuiltInType.ByteString:
|
|
case BuiltInType.XmlElement:
|
|
case BuiltInType.QualifiedName:
|
|
case BuiltInType.LocalizedText:
|
|
case BuiltInType.DataValue:
|
|
case BuiltInType.ExtensionObject:
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endregion
|
|
|
|
private void NavigationMENU_Click(object sender, EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
EndEdit();
|
|
|
|
ToolStripItem item = sender as ToolStripItem;
|
|
|
|
if (item != null)
|
|
{
|
|
// remove all menu items appearing after the selected item.
|
|
for (int ii = NavigationMENU.Items.Count-1; ii >= 0; ii--)
|
|
{
|
|
ToolStripItem target = NavigationMENU.Items[ii];
|
|
NavigationMENU.Items.Remove(target);
|
|
|
|
if (Object.ReferenceEquals(target, item))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// show the current value.
|
|
AccessInfo info = (AccessInfo)item.Tag;
|
|
ShowValue(info);
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
ClientUtils.HandleException(this.Text, exception);
|
|
}
|
|
}
|
|
|
|
private void ValuesDV_DoubleClick(object sender, EventArgs e)
|
|
{
|
|
try
|
|
{
|
|
foreach (DataGridViewCell cell in ValuesDV.SelectedCells)
|
|
{
|
|
DataRowView source = ValuesDV.Rows[cell.RowIndex].DataBoundItem as DataRowView;
|
|
|
|
if (cell.ColumnIndex == 3)
|
|
{
|
|
AccessInfo info = (AccessInfo)source.Row[0];
|
|
ShowValue(info);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
ClientUtils.HandleException(this.Text, exception);
|
|
}
|
|
}
|
|
|
|
private void ValuesDV_CellValidating(object sender, DataGridViewCellValidatingEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
if (this.Visible && e.ColumnIndex == 3)
|
|
{
|
|
DataRowView source = ValuesDV.Rows[e.RowIndex].DataBoundItem as DataRowView;
|
|
AccessInfo info = (AccessInfo)source.Row[0];
|
|
|
|
if (IsSimpleValue(info))
|
|
{
|
|
TypeInfo.Cast(e.FormattedValue, info.TypeInfo.BuiltInType);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
ClientUtils.HandleException(this.Text, exception);
|
|
e.Cancel = true;
|
|
}
|
|
}
|
|
|
|
private void ValuesDV_CellValueChanged(object sender, DataGridViewCellEventArgs e)
|
|
{
|
|
try
|
|
{
|
|
if (this.Visible && e.RowIndex >= 0 && e.ColumnIndex == 3)
|
|
{
|
|
DataRowView source = ValuesDV.Rows[e.RowIndex].DataBoundItem as DataRowView;
|
|
AccessInfo info = (AccessInfo)source.Row[0];
|
|
|
|
if (IsSimpleValue(info))
|
|
{
|
|
object newValue = TypeInfo.Cast((string)source.Row[3], info.TypeInfo.BuiltInType);
|
|
info.Value = newValue;
|
|
UpdateParent(info);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
ClientUtils.HandleException(this.Text, exception);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Recusivesly updates the parent values.
|
|
/// </summary>
|
|
private void UpdateParent(AccessInfo info)
|
|
{
|
|
if (info.Parent == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
object parentValue = info.Parent.Value;
|
|
|
|
if (info.Parent.TypeInfo.BuiltInType == BuiltInType.Variant && info.Parent.TypeInfo.ValueRank < 0)
|
|
{
|
|
parentValue = ((Variant)info.Parent.Value).Value;
|
|
}
|
|
|
|
if (info.PropertyInfo != null && info.Parent.TypeInfo.ValueRank < 0)
|
|
{
|
|
ExtensionObject extension = parentValue as ExtensionObject;
|
|
|
|
if (extension != null)
|
|
{
|
|
parentValue = extension.Body;
|
|
}
|
|
|
|
info.PropertyInfo.SetValue(parentValue, info.Value, null);
|
|
}
|
|
|
|
else if (info.Indexes != null)
|
|
{
|
|
int[] indexes = info.Indexes;
|
|
Array array = parentValue as Array;
|
|
|
|
Matrix matrix = parentValue as Matrix;
|
|
|
|
if (matrix != null)
|
|
{
|
|
int count = 0;
|
|
int block = 1;
|
|
|
|
for (int ii = info.Indexes.Length-1; ii >= 0 ; ii--)
|
|
{
|
|
count += info.Indexes[ii] * block;
|
|
block *= matrix.Dimensions[ii];
|
|
}
|
|
|
|
array = matrix.Elements;
|
|
indexes = new int[] { count };
|
|
}
|
|
|
|
if (array != null)
|
|
{
|
|
if (info.Parent.TypeInfo.BuiltInType == BuiltInType.Variant && info.Parent.TypeInfo.ValueRank >= 0)
|
|
{
|
|
array.SetValue(new Variant(info.Value), indexes);
|
|
}
|
|
else
|
|
{
|
|
array.SetValue(info.Value, indexes);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IList list = parentValue as IList;
|
|
|
|
if (info.Parent.TypeInfo.BuiltInType == BuiltInType.Variant && info.Parent.TypeInfo.ValueRank >= 0)
|
|
{
|
|
list[indexes[0]] = new Variant(info.Value);
|
|
}
|
|
else
|
|
{
|
|
list[indexes[0]] = info.Value;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (info.Parent != null)
|
|
{
|
|
UpdateParent(info.Parent);
|
|
}
|
|
}
|
|
}
|
|
}
|