|
-
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.
-
Nov 18th, 2014, 12:20 AM
#2
Junior Member
Re: Custom property editor (UITypeEditor) not working properly with class
I came here hoping to find an answer, as it seemed the most promising for 2 reasons:
1 - This is an excellent site with superb resources.
2 - The question was an exact match to my issue. As the question yielded minimal related results.
After some thought, messing around and looking closely at the code, i had a facepalm moment.
I see the same error in this code, and all the references i looked up also. It gives the illusion the control is updated, but if you look at the myControl.designer.vb file, you will see it was never in fact updated.
You have to simply return the base implementation. So in case anyone else out there (as it is too late for op) is having trouble with this also, here is some code:
Code:
Imports System.Drawing.Design
Imports System.Windows.Forms.Design
Imports System.ComponentModel.Design
Public Class MyControlEditor
Inherits UITypeEditor
Private WithEvents propEditor As New MyControlPropertyGrid ' A Windows Form with a PropertyGrid to edit MyControl
Private oldVal As New MyControl ' So we can save a copy of the values in MyControl as they were before user went in to edit them
Public Overrides Function EditValue(context As System.ComponentModel.ITypeDescriptorContext, provider As System.IServiceProvider, value As Object) As Object
If (context IsNot Nothing) And (context.Instance IsNot Nothing) And (provider IsNot Nothing) Then
If Not IsNothing(CType(value, MyControl)) Then
oldVal = CType(CType(value, MyControl).Clone, MyControl) ' Copy the values in case user decides to cancel the property editing operation
propEditor.MyControl_PropGrid.SelectedObject = CType(value, MyControl) ' Set the PropertyGrid in our propEditor form to work with MyControl
End If
Dim result As Windows.Forms.DialogResult = propEditor.ShowDialog()
If result = Windows.Forms.DialogResult.OK Then
value = propEditor.MyControl_PropGrid.SelectedObject
Else
value = oldVal
End If
End If
Return MyBase.EditValue(context, provider, value) ' This was the DOH! moment
End Function
Public Overrides Function GetEditStyle(context As System.ComponentModel.ITypeDescriptorContext) As System.Drawing.Design.UITypeEditorEditStyle
If (Not IsNothing(context)) And (Not IsNothing(context.Instance)) Then
Return UITypeEditorEditStyle.Modal
End If
Return MyBase.GetEditStyle(context)
End Function
End Class
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
|