|
-
Dec 25th, 2009, 07:03 PM
#1
Custom property editor (UITypeEditor) not working properly with class
Hi,
I want to give some property of a custom control I created a custom property editor window. What I mean by that is that, in the property grid, the property is not directly editable but has a "[...]" button next to it. Clicking that button will open an editor form which allows you to edit the value.

In the screenshot you can see a very simple example, where 'MyProperty' is just a String property on a custom TextBox class:
vb.net Code:
Imports System.ComponentModel Imports System.Drawing.Design Public Class CustomTextBox Inherits TextBox Private _MyProperty As String <Editor(GetType(CustomPropertyEditor), GetType(UITypeEditor))> _ Public Property MyProperty() As String Get Return _MyProperty End Get Set(ByVal value As String) _MyProperty = value End Set End Property End Class
The CustomPropertyEditor class, which is set as this property's EditorAttribute, is slightly involved, so I left out some of the irrelevant parts. It inherits from UITypeEditor and takes care of showing a modal form when the [...] button is clicked.
vb.net Code:
Imports System.Drawing Imports System.Drawing.Design Imports System.Windows.Forms Imports System.Windows.Forms.Design Imports System.ComponentModel Public Class CustomPropertyEditor Inherits System.Drawing.Design.UITypeEditor Private m_editorForm As TextEditorForm Private Function GetEditControl(ByVal PropertyName As String, ByVal CurrentValue As Object) As System.Windows.Forms.Control Dim str As String = CStr(CurrentValue) m_editorForm = New TextEditorForm() m_editorForm.Value = str Return m_editorForm End Function Private Function GetEditedValue(ByVal EditControl As System.Windows.Forms.Control, ByVal PropertyName As String, ByVal OldValue As Object) As Object If m_editorForm Is Nothing _ OrElse m_editorForm.DialogResult = DialogResult.Cancel Then Return OldValue Else Return m_editorForm.Value End If End Function Public Overrides Function GetEditStyle(ByVal context As System.ComponentModel.ITypeDescriptorContext) As UITypeEditorEditStyle Return UITypeEditorEditStyle.Modal End Function 'some stuff omitted End Class
Basically, the GetEditorControl function returns a new instance of the TextEditorForm (which is the modal form you can see on the screenshot, used to edit the property), but first sets its Value property (the text in its TextBox) to the current value of the MyProperty property.
The GetEditedValue function is used to return the result of the edit (the text in the textbox, which is the Value property) back to the property.
This works just fine. I can edit the String property and the result is stored and shown forever. Great.
Then, I wanted something more involved. Instead of just a String property, I have a property that returns an instance of some nested class, which in turn carries a whole load of other properties.
So now the CustomTextbox class becomes a little more involved. It has one nested class (called NestedClass), with two string properties (as an example). It exposes an instance of this class via a NestedClassInstance property.
vb.net Code:
Imports System.ComponentModel Imports System.Drawing.Design Public Class CustomTextBox Inherits TextBox Public Sub New() _NestedClassInstance = New NestedClass() End Sub Private _NestedClassInstance As NestedClass <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _ <Editor(GetType(CustomPropertyEditor), GetType(UITypeEditor))> _ Public Property NestedClassInstance() As NestedClass Get Return _NestedClassInstance End Get Set(ByVal value As NestedClass) _NestedClassInstance = value End Set End Property Public Class NestedClass Private _MyProperty1 As String Public Property MyProperty1() As String Get Return _MyProperty1 End Get Set(ByVal value As String) _MyProperty1 = value End Set End Property Private _MyProperty2 As String Public Property MyProperty2() As String Get Return _MyProperty2 End Get Set(ByVal value As String) _MyProperty2 = value End Set End Property End Class End Class
This time, the Editor attribute is set on the NestedClassInstance property, because I want to edit that property directly via the editor form.
(Please note that, if I get rid of the Editor attribute, and replace it with an ExpandableObject TypeConverter attribute, (so that the property is expandable in the property grid), I can get and set the two string properties just fine, simply using the property grid, and they are stored like they should be.)
Now, in order for my editor form to be able to change this NestedClassInstance property, I gave it a PropertyGrid control instead of the TextBox. I also had it take an instance of the CustomTextBox.NestedClass in its constructor, so that the PropertyGrids SelectedObject can be set to that instance, so that it's editable. Finally, there is now a NestedClassInstance on the editor form as well, which simply returns the instance of the NestedClass which was passed again.
vb.net Code:
Imports System.Windows.Forms Public Class TextEditorForm Private _NestedClassInstance As CustomTextBox.NestedClass Public Sub New(ByVal nc As CustomTextBox.NestedClass) ' This call is required by the Windows Form Designer. InitializeComponent() ' Add any initialization after the InitializeComponent() call. _NestedClassInstance = nc PropertyGrid1.SelectedObject = nc End Sub Public ReadOnly Property NestedClassInstance() As CustomTextBox.NestedClass Get Return _NestedClassInstance End Get End Property Private Sub OK_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles OK_Button.Click Me.DialogResult = System.Windows.Forms.DialogResult.OK Me.Close() End Sub Private Sub Cancel_Button_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Cancel_Button.Click Me.DialogResult = System.Windows.Forms.DialogResult.Cancel Me.Close() End Sub End Class
Simple enough I hope.
Now finally, I also needed a slight change in the CustomPropertyEditor UITypeEditor, mainly just to pass the CurrentValue (cast to a NestedClass class) to the editor form, and to get it back again:
vb.net Code:
Imports System.Drawing Imports System.Drawing.Design Imports System.Windows.Forms Imports System.Windows.Forms.Design Imports System.ComponentModel Public Class CustomPropertyEditor Inherits System.Drawing.Design.UITypeEditor Private m_editorForm As TextEditorForm Private Function GetEditControl(ByVal PropertyName As String, ByVal CurrentValue As Object) As System.Windows.Forms.Control Dim nestedClass = TryCast(CurrentValue, CustomTextBox.NestedClass) If nestedClass IsNot Nothing Then m_editorForm = New TextEditorForm(nestedClass) Return m_editorForm Else Return Nothing End If End Function Private Function GetEditedValue(ByVal EditControl As System.Windows.Forms.Control, ByVal PropertyName As String, ByVal OldValue As Object) As Object If m_editorForm Is Nothing _ OrElse m_editorForm.DialogResult = DialogResult.Cancel Then Return OldValue Else Return m_editorForm.NestedClassInstance End If End Function Public Overrides Function GetEditStyle(ByVal context As System.ComponentModel.ITypeDescriptorContext) As UITypeEditorEditStyle Return UITypeEditorEditStyle.Modal End Function End Class
Now when I click the button in the property grid, I get the new editor form with PropertyGrid control, which shows my two properties:

So far so good. I can change the properties on the editor form, press the OK button, and they seem to be stored. When I open the editor form again, the changes were persisted.
However...
When I build the solution, close the Form1 designer file, and then re-open it (so that it's reloaded), then the changes are gone! It seems they are not persisted after all, at least not in the form designer file I guess.
Also, when I make a change, Visual Studio doesn't seem to 'notice' it, as it does not tell me that the Form1.vb file has changed, so I can't even save it before closing it. (It doesn't get the asterisk * after it's name indicating an unsaved change).
As I said before: if I get rid of the whole custom UITypeEditor thingy, and just edit the properties from the property grid, it works fine. Then the changes are persisted even after building.
Am I missing something? Am I doing something fundamentally wrong? I can't really see the difference in using a String as the type to edit, or some custom class which in turn hosts some other properties. A String is just another class, right? So where's the difference?
Note also that I am already setting the DesignerSerializationVisibility attribute of the NestedClassInstance to Content, which, in my understanding, should ensure that any changes to that property are stored in the designer file. Still, it doesn't work.
Anyone? Thanks for any help!
Last edited by NickThissen; Dec 25th, 2009 at 07:07 PM.
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|