locked
Windows forms dynamic binding at runtime RRS feed

  • Question

  • Hello,

    I am trying to write a utility classes that will enabled the user to dynamicly bind an object TextBox and other components,  

    I have a few requirements that need to be met:

    1. Binding must occur on runtime, cannot be done in designer or during Creation of the form.

    2. Binded property name may be changed at anytime and so the value in the textbox must be changed as well.

    3. Binding source cannot define static properties beforehand, all properties are created during runtime

    However, 

    I am getting an exception when attempting to bind a component to such Dynamic Property,

    If I am binding the component to static property there is no problem.

    What am I missing here?

    Thanks in advance, Idan

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Dynamic;
    using System.Windows.Forms;
    
    namespace DynamicBinding
    {
        public partial class TestDataBinding : Form
        {
            private dynamic SampleDictionary;
    
            public TestDataBinding()
            {
                InitializeComponent();
                SampleDictionary = new DynamicDictionary();
                SampleDictionary.DynamicProperty = "dynamic property value";
            }
    
            private void btLinkProperty_Click(object sender, EventArgs e)
            {
                tbText.DataBindings.Clear();
                // throws an exception:
                //      Cannot bind to the property or column DynamicProperty on the DataSource.
                //      Parameter name: dataMember
                tbText.DataBindings.Add(new Binding("Text", SampleDictionary, "DynamicProperty", true, DataSourceUpdateMode.OnPropertyChanged));
            }
    
            public class DynamicDictionary : DynamicObject, INotifyPropertyChanged
            {
                private Dictionary<string, object> Data;
    
                public DynamicDictionary()
                {
                    Data = new Dictionary<string, object>();
                }
    
                public override bool TrySetMember(SetMemberBinder binder, object value)
                {
                    if (Data.ContainsKey(binder.Name))
                    {
                        Data.Remove(binder.Name);
                    }
                    Data.Add(binder.Name, value);
                    return true;
                }
    
                public override bool TryGetMember(GetMemberBinder binder, out object result)
                {
                    if (!Data.ContainsKey(binder.Name))
                    {
                        throw new Exception("no such key:" + binder.Name);
                    }
                    result = Data[binder.Name];
                    return true;
                }
    
                public event PropertyChangedEventHandler PropertyChanged;
                protected void NotifyPropertyChanged(string propertyName)
                {
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                    }
                }
            }
        }
    }
    


    Sunday, July 8, 2012 9:35 AM

Answers

  • If I were you, I would simply create a class that implements ICustomTypeDescriptor.  In its GetProperties() method you can return property descriptors that list any properties you want exposed.  You could create property descriptors for each item in your dictionary.  I know for a fact that this method works.

    Jose R. MCP
    Code Samples

    • Proposed as answer by Bob Wu-MT Friday, July 13, 2012 5:42 AM
    • Marked as answer by Idan Mor Sunday, July 15, 2012 6:14 AM
    Thursday, July 12, 2012 3:35 PM
  • Hello,

    I have tried what you suggested and it worked!

    thanks!!!

    I'll post my code example here in case anyone looks something like this

    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Dynamic;
    using System.Windows.Forms;
    
    namespace ItypeDescriptorBinding
    {
        public partial class ItypeDescriptorBinding : Form
        {
            private dynamic dynamicDictionary;
    
            public ItypeDescriptorBinding()
            {
                InitializeComponent();
                dynamicDictionary = new DynamicDictionary(new Hashtable());
                dynamicDictionary.DynamicProperty = "this is a text";
    
            }
    
            private void btLinkProperty_Click(object sender, EventArgs e)
            {
                tbText.DataBindings.Add(new Binding("Text", dynamicDictionary, "DynamicProperty"));
            }
    
            private void btQueryProperty_Click(object sender, EventArgs e)
            {
                MessageBox.Show(dynamicDictionary.DynamicProperty);
            }
        }
    
        public class DynamicDictionary : DynamicObject, ICustomTypeDescriptor
        {
            IDictionary dictionary;
    
            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                if (dictionary.Contains(binder.Name))
                {
                    dictionary.Remove(binder.Name);
                }
                dictionary.Add(binder.Name, value);
    
                return true;
            }
    
            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                bool HasProperty = false;
    
                if (dictionary.Contains(binder.Name))
                {
                    result = dictionary[binder.Name];
                    HasProperty = true;
                }
                else
                {
                    result = null;
                }
    
                return HasProperty;
            }
    
            public DynamicDictionary(IDictionary d)
            {
                dictionary = d;
            }
    
            public string GetComponentName()
            {
                return TypeDescriptor.GetComponentName(this, true);
            }
    
            public EventDescriptor GetDefaultEvent()
            {
                return TypeDescriptor.GetDefaultEvent(this, true);
            }
    
            public string GetClassName()
            {
                return TypeDescriptor.GetClassName(this, true);
            }
    
            public EventDescriptorCollection GetEvents(Attribute[] attributes)
            {
                return TypeDescriptor.GetEvents(this, attributes, true);
            }
    
            EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
            {
                return TypeDescriptor.GetEvents(this, true);
            }
    
            public TypeConverter GetConverter()
            {
                return TypeDescriptor.GetConverter(this, true);
            }
    
            public object GetPropertyOwner(PropertyDescriptor pd)
            {
                return dictionary;
            }
    
            public AttributeCollection GetAttributes()
            {
                return TypeDescriptor.GetAttributes(this, true);
            }
    
            public object GetEditor(Type editorBaseType)
            {
                return TypeDescriptor.GetEditor(this, editorBaseType, true);
            }
    
            public PropertyDescriptor GetDefaultProperty()
            {
                return null;
            }
    
            PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties()
            {
                return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]);
            }
    
            public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
            {
                ArrayList properties = new ArrayList();
                foreach (DictionaryEntry e in dictionary)
                {
                    properties.Add(new DictionaryPropertyDescriptor(dictionary, e.Key));
                }
    
                PropertyDescriptor[] props = (PropertyDescriptor[])properties.ToArray(typeof(PropertyDescriptor));
    
                return new PropertyDescriptorCollection(props);
            }
    
            class DictionaryPropertyDescriptor : PropertyDescriptor
            {
                IDictionary dictionary;
                object key;
    
                internal DictionaryPropertyDescriptor(IDictionary Dictionary, object Key)
                    : base(Key.ToString(), null)
                {
                    dictionary = Dictionary;
                    key = Key;
                }
    
                public override Type PropertyType
                {
                    get 
                    { 
                        return dictionary[key].GetType(); 
                    }
                }
    
                public override void SetValue(object component, object value)
                {
                    dictionary[key] = value;
                }
    
                public override object GetValue(object component)
                {
                    return dictionary[key];
                }
    
                public override bool IsReadOnly
                {
                    get { return false; }
                }
    
                public override Type ComponentType
                {
                    get { return null; }
                }
    
                public override bool CanResetValue(object component)
                {
                    return false;
                }
    
                public override void ResetValue(object component)
                {
                }
    
                public override bool ShouldSerializeValue(object component)
                {
                    return false;
                }
            }
        }  
    }
    

    • Marked as answer by Bob Wu-MT Monday, July 16, 2012 3:11 AM
    Sunday, July 15, 2012 6:16 AM

All replies

  • I am getting an exception when attempting to bind a component to such Dynamic Property,

    What is the exception that you get?  And where do you get it (line of code)?


    Jose R. MCP
    Code Samples

    Sunday, July 8, 2012 5:21 PM
  • Hello,

    The exception is in this method on the second code line - 

    this is the description inside the exception:

    Cannot bind to the property or column DynamicProperty on the DataSource.
    Parameter name: dataMember

            private void btLinkProperty_Click(object sender, EventArgs e)
            {
                tbText.DataBindings.Clear();
                // throws an exception:
                //      Cannot bind to the property or column DynamicProperty on the DataSource.
                //      Parameter name: dataMember
                tbText.DataBindings.Add(new Binding("Text", SampleDictionary, "DynamicProperty", true, DataSourceUpdateMode.OnPropertyChanged));
            }

    Monday, July 9, 2012 5:04 AM
  • Hi Idan,

    The issue occur because DynamicProperty is not a property of DynamicDictionary, it just a key in Data Dictionary.

    You can't use it in this way.

    Best regards,


    Bob Wu [MSFT]
    MSDN Community Support | Feedback to us


    • Edited by Bob Wu-MT Wednesday, July 11, 2012 9:22 AM
    Wednesday, July 11, 2012 9:22 AM
  • Hello Bob,

    Thank you for the reply,

    This is quite intersting since I was able to do this kind of binding with a datatable. since DynamicDictionary is  a DynamicObject I can access the Dictionary items  with TrySetMember/TryGetMember whenever I want using "SampleDictionay.DynamicProperty".

    Is there an interface that I can implement to make this kind of dynamic binding work with something other then datatable?

    My end goal is to bind windows forms components to an object whose values are created in run-time.


    Thursday, July 12, 2012 7:52 AM
  • Hello again,

    Here is an exmple for the same line of binding but with DataTable, the binding works fine here:

    using System;
    using System.Data;
    using System.Windows.Forms;
    
    namespace DatatableBinding
    {
        public partial class DatatableBinding : Form
        {
            private DataTable dt;
    
            public DatatableBinding()
            {
                InitializeComponent();
                dt = new DataTable();
                dt.Columns.Add("DynamicProperty", typeof(string));
                dt.Rows.Add("DynamicProperty value");
            }
    
            private void btLinkProperties_Click(object sender, EventArgs e)
            {
                tbText.DataBindings.Clear();
                tbText.DataBindings.Add(new Binding("Text", dt, "DynamicProperty", true, DataSourceUpdateMode.OnPropertyChanged));
            }
        }
    }


    Thursday, July 12, 2012 11:33 AM
  • That's different.  The DataTable is programmed to expose the columns as properties for data binding purposes.  You must use the TrySetMember() base implementation to achieve the same.  You don't call base.TrySetMember(), so the underlying object doesn't expose the new property as a property.  At least is what I think it is based of whatever I know about data binding.  Try it out.

    Jose R. MCP
    Code Samples

    Thursday, July 12, 2012 12:59 PM
  • Hello webJose,

    I tried using the base implementaions and It didnt work, but I think you caught what I am trying to achieve.

    I want to mimic the way the columns are exposed as properties in the DataTable.

    Thursday, July 12, 2012 2:41 PM
  • If I were you, I would simply create a class that implements ICustomTypeDescriptor.  In its GetProperties() method you can return property descriptors that list any properties you want exposed.  You could create property descriptors for each item in your dictionary.  I know for a fact that this method works.

    Jose R. MCP
    Code Samples

    • Proposed as answer by Bob Wu-MT Friday, July 13, 2012 5:42 AM
    • Marked as answer by Idan Mor Sunday, July 15, 2012 6:14 AM
    Thursday, July 12, 2012 3:35 PM
  • Thanks!

    I'll try it now.

    Sunday, July 15, 2012 4:49 AM
  • Hello,

    I have tried what you suggested and it worked!

    thanks!!!

    I'll post my code example here in case anyone looks something like this

    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Dynamic;
    using System.Windows.Forms;
    
    namespace ItypeDescriptorBinding
    {
        public partial class ItypeDescriptorBinding : Form
        {
            private dynamic dynamicDictionary;
    
            public ItypeDescriptorBinding()
            {
                InitializeComponent();
                dynamicDictionary = new DynamicDictionary(new Hashtable());
                dynamicDictionary.DynamicProperty = "this is a text";
    
            }
    
            private void btLinkProperty_Click(object sender, EventArgs e)
            {
                tbText.DataBindings.Add(new Binding("Text", dynamicDictionary, "DynamicProperty"));
            }
    
            private void btQueryProperty_Click(object sender, EventArgs e)
            {
                MessageBox.Show(dynamicDictionary.DynamicProperty);
            }
        }
    
        public class DynamicDictionary : DynamicObject, ICustomTypeDescriptor
        {
            IDictionary dictionary;
    
            public override bool TrySetMember(SetMemberBinder binder, object value)
            {
                if (dictionary.Contains(binder.Name))
                {
                    dictionary.Remove(binder.Name);
                }
                dictionary.Add(binder.Name, value);
    
                return true;
            }
    
            public override bool TryGetMember(GetMemberBinder binder, out object result)
            {
                bool HasProperty = false;
    
                if (dictionary.Contains(binder.Name))
                {
                    result = dictionary[binder.Name];
                    HasProperty = true;
                }
                else
                {
                    result = null;
                }
    
                return HasProperty;
            }
    
            public DynamicDictionary(IDictionary d)
            {
                dictionary = d;
            }
    
            public string GetComponentName()
            {
                return TypeDescriptor.GetComponentName(this, true);
            }
    
            public EventDescriptor GetDefaultEvent()
            {
                return TypeDescriptor.GetDefaultEvent(this, true);
            }
    
            public string GetClassName()
            {
                return TypeDescriptor.GetClassName(this, true);
            }
    
            public EventDescriptorCollection GetEvents(Attribute[] attributes)
            {
                return TypeDescriptor.GetEvents(this, attributes, true);
            }
    
            EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
            {
                return TypeDescriptor.GetEvents(this, true);
            }
    
            public TypeConverter GetConverter()
            {
                return TypeDescriptor.GetConverter(this, true);
            }
    
            public object GetPropertyOwner(PropertyDescriptor pd)
            {
                return dictionary;
            }
    
            public AttributeCollection GetAttributes()
            {
                return TypeDescriptor.GetAttributes(this, true);
            }
    
            public object GetEditor(Type editorBaseType)
            {
                return TypeDescriptor.GetEditor(this, editorBaseType, true);
            }
    
            public PropertyDescriptor GetDefaultProperty()
            {
                return null;
            }
    
            PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties()
            {
                return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]);
            }
    
            public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
            {
                ArrayList properties = new ArrayList();
                foreach (DictionaryEntry e in dictionary)
                {
                    properties.Add(new DictionaryPropertyDescriptor(dictionary, e.Key));
                }
    
                PropertyDescriptor[] props = (PropertyDescriptor[])properties.ToArray(typeof(PropertyDescriptor));
    
                return new PropertyDescriptorCollection(props);
            }
    
            class DictionaryPropertyDescriptor : PropertyDescriptor
            {
                IDictionary dictionary;
                object key;
    
                internal DictionaryPropertyDescriptor(IDictionary Dictionary, object Key)
                    : base(Key.ToString(), null)
                {
                    dictionary = Dictionary;
                    key = Key;
                }
    
                public override Type PropertyType
                {
                    get 
                    { 
                        return dictionary[key].GetType(); 
                    }
                }
    
                public override void SetValue(object component, object value)
                {
                    dictionary[key] = value;
                }
    
                public override object GetValue(object component)
                {
                    return dictionary[key];
                }
    
                public override bool IsReadOnly
                {
                    get { return false; }
                }
    
                public override Type ComponentType
                {
                    get { return null; }
                }
    
                public override bool CanResetValue(object component)
                {
                    return false;
                }
    
                public override void ResetValue(object component)
                {
                }
    
                public override bool ShouldSerializeValue(object component)
                {
                    return false;
                }
            }
        }  
    }
    

    • Marked as answer by Bob Wu-MT Monday, July 16, 2012 3:11 AM
    Sunday, July 15, 2012 6:16 AM
  • Glad to hear that and thank you for sharing the sample with us.

    Best Regards,


    Bob Wu [MSFT]
    MSDN Community Support | Feedback to us

    Monday, July 16, 2012 3:11 AM