/* ======================================================================== * Copyright (c) 2005-2020 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 { /// /// Allows the user to edit a complex value. /// public partial class EditComplexValueCtrl : UserControl { /// /// Constructs the object. /// 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 /// /// The maximum length of a value string displayed in a column. /// [DefaultValue(100)] public int MaxDisplayTextLength { get { return m_maxDisplayTextLength; } set { if (value < 20) { m_maxDisplayTextLength = 20; } m_maxDisplayTextLength = value; } } /// /// Returns true if the Back command can be called. /// public bool CanGoBack { get { return (NavigationMENU.Items.Count > 1); } } /// /// Returns true if the ArraySize can be changed. /// 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; } } /// /// Returns true if the data type can be changed. /// 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; } } /// /// Returns the current data type. /// 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; } } /// /// Raised when the value is changed. /// public event EventHandler ValueChanged { add { m_ValueChanged += value; } remove { m_ValueChanged -= value; } } /// /// Changes the session used for editing the value. /// public void ChangeSession(Session session) { m_session = session; } /// /// Moves the displayed value back. /// public void Back() { if (!CanGoBack) { return; } NavigationMENU_Click(NavigationMENU.Items[NavigationMENU.Items.Count - 2], null); } /// /// Changes the array size. /// 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); } /// /// Changes the data type. /// 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); } /// /// Converts the old type to the new type. /// 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; } /// /// Displays the value in the control. /// 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); } /// /// Displays the value in the control. /// 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); } /// /// Displays the value in the control. /// 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); } /// /// Returns the edited value. /// public object GetValue() { return m_value.Value; } /// /// Validates the value currently being edited. /// 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); } /// /// Displays the value in the control. /// private void ShowValue(AccessInfo parent) { ShowValueNoNotify(parent); if (m_ValueChanged != null) { m_ValueChanged(this, null); } } /// /// Displays the value in the control. /// 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 /// /// Returns the index based on the current count. /// 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; } /// /// Adds the value at an array index to the control. /// 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); } /// /// Returns the element type for a list. /// 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); } /// /// Returns the data type of the value. /// 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); } /// /// Returns the data type of the value. /// private string GetDataTypeString(AccessInfo accessInfo) { Type type = GetDataType(accessInfo); if (type == null) { return accessInfo.TypeInfo.ToString(); } return type.Name; } /// /// Adds the value with the specified name to the control. /// 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); } /// /// Displays a value in the control. /// 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; } } } /// /// Displays a string in the control. /// 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; } /// /// Displays a complete byte string in the control. /// 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(); } /// /// Displays a complete XML element in the control. /// 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(); } /// /// Converts a value to a string for display in the grid. /// 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); } /// /// Whether the value can be edited in the grid view. /// 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); } } /// /// Recusivesly updates the parent values. /// 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); } } } }