0

I have a User Control with a String Collection Editor where I save the list of Tabpage names like so:

    [Editor(@"System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(System.Drawing.Design.UITypeEditor))]
    public List<string> TabPages { 
        get {return tabpages;}
        set {
            tabpages = value;
        }
    }

This feature is serving well as it appears like so in property grid:

enter image description here

I want to know how one can know if a change has happened in the collection.

The problem I am facing is that while the change in collection is retained after saving the change, I dont know how to write an event to check if this collection has changed.

For a property like BackColor, I am able to detect a change like so:

        if (e.ChangedItem != null && e.ChangedItem.Label == "BackColor" && ((Color)e.ChangedItem.Value).ToArgb() == Color.Transparent.ToArgb()) {      
                    e.ChangedItem.PropertyDescriptor.SetValue(propertyGrid1.SelectedObject, e.OldValue);
        }

within the function:

private void propertyGrid1_PropertyValueChanged(object s, PropertyValueChangedEventArgs e);

that is associated with my propertygrid like so:

this.propertyGrid1.PropertyValueChanged += new System.Windows.Forms.PropertyValueChangedEventHandler(this.propertyGrid1_PropertyValueChanged);

However with collection the change in the StringCollectionEditor is not detected with this same process.

enter image description here

Is there a special EventHandler associated with the StringCollecitonEditor, that I need to subscribe to? or perhaps is there some way I can change the declaration.

I will be grateful for any help in this direction.

[Edited]

My constraints:

  1. I use VS 2010 with dot net 3.5. my project needs to stay at dot net 3.5 because all our solutions need backward compatibility to this version.
  • I found [this](https://social.msdn.microsoft.com/Forums/windows/en-US/571fcee9-652b-43a0-b71e-b3ab956f388d/propertygrid-collection-editing-how-can-i-know-when-collection-been-changed?forum=winformsdatacontrols), you can try to use observable collection (then you will have event) or you can always make custom editor. As for `PropertyValueChanged` the reasons it's not fired because you are not changing value of that property (`PropertyGrid` let you edit getter-only property). – Sinatr Feb 02 '17 at 13:43
  • I cannot change it to observable colleciton in my case, it is just a list of strings to which I later associate properties of its own at run time. Not sure if I will be able to make a Custom Editor in Time :( – Ganesh Kamath - 'Code Frenzy' Feb 02 '17 at 13:47
  • Are any events fired at all after closing the StringCollectionEditor? I am looking for a mechanism that can then externally trigger the PropertyGrid's PropertyValue Change Event – Ganesh Kamath - 'Code Frenzy' Feb 02 '17 at 13:55

2 Answers2

1

One possibility is to intercept StringCollectionEditor, for this you have to create custom editor like below:

class MyEditor : UITypeEditor
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.Modal;
    }
    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
    {
        var type = Type.GetType(@"System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
        dynamic editor = Activator.CreateInstance(type, new[] { typeof(UITypeEditor) });
        var result = editor.EditValue(context, provider, value);
        // call your event here
        return result;
    }
}

Use it like this

[Editor(typeof(MyEditor), typeof(UITypeEditor))]
public List<string> TabPages { get; } = new List<string>();

Edit: above code is working for VS2015 (dynamic and auto-property initializer), otherwise you may have to use reflection and full property:

readonly List<string> _tagPages = new List<string>();
[Editor(typeof(MyEditor), typeof(UITypeEditor))]
public List<string> TabPages { get { return _tabPages; } }

// reflection part 
var type = Type.GetType(@"System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
var editor = Activator.CreateInstance(type, new[] { typeof(UITypeEditor) });
var method = type.GetMethod(nameof(EditValue), new[] { typeof(ITypeDescriptorContext), typeof(IServiceProvider), typeof(object) }); // if nameof is also missing then just use "EditValue" in place of it
var result = method.Invoke(editor, new[] { context, provider, value });
Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • the line `var result = editor.EditValue(context, provider, value);` is giving an error message "Predefined type 'Microsoft.CSharp.RuntimeBinder.Binder' is not defined or imported" as i am using VS 2010 and .net 3.5. Need your help to impelement it further. – Ganesh Kamath - 'Code Frenzy' Feb 03 '17 at 05:02
  • Is it because of `dynamic`? I am using it to avoid using reflection explicitly to call `EditValue()` method with parameters, see [this answer](http://stackoverflow.com/a/3110313/1997232). – Sinatr Feb 06 '17 at 08:14
  • To be frank, I am farely new to C# and dont know what reflection is. Let me see if your suggestion helps solve the problem. Thank you. – Ganesh Kamath - 'Code Frenzy' Feb 06 '17 at 09:20
-1

I was unable to add Microsoft.Csharp to my project as it was a .Net 3.5 project.

Finally we had to stick to changing the code without using the dynamic keyword as follows:

class MyEditor : UITypeEditor {
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context) {
        return UITypeEditorEditStyle.Modal;
    }
    public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
        var type = Type.GetType(@"System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
        var editor = Activator.CreateInstance(type, new[] { typeof(UITypeEditor) });
        var result = ((UITypeEditor)editor).EditValue(context, provider, value);
        // call your event here
        return result;
    }
}

I beieve that the following code works because of Liskov Substituition Principle. But I don't have complete clarity.