Reputation: 309
How can I override just one EditValue function of a control while keeping all the other properties of that control available? When I assign a single control to the SelectedObject property of a PropertyGrid I get all the properties of the control. In order to override one property I would like to create a property wrapper class that will show me all the controls as before, including the custom EditValue function. But I find that I must define all properties in the wrapper class, else I only get to see the property with the custom EditValue function. This seems very impractical, there must be an easier way. I need to do this because I want to catch the file name of a BackgroundImage that the user can specify. The code to catch the name works fine, here it is for good measure:
Friend Class CatchFileName : Inherits UITypeEditor
Public Overrides Function GetEditStyle(context As ITypeDescriptorContext) As UITypeEditorEditStyle
Return UITypeEditorEditStyle.Modal
End Function
Public Overrides Function EditValue(context As ITypeDescriptorContext,
provider As IServiceProvider, value As Object) As Object
Dim ofd As New OpenFileDialog() With {.Filter = "Image Files|*.bmp;*.jpg;*.jpeg,*.gif;*.png;*.ewf;*.wmf;*.ico"}
If (ofd.ShowDialog() = DialogResult.OK) Then
DirectCast(context.Instance, FormPropertiesWrapper)._BackgroundImageName =
Path.GetFileName(ofd.FileName) ' Strip path
Return Image.FromFile(ofd.FileName)
End If
Return MyBase.EditValue(context, provider, value)
End Function
End Class
<Description("Defines a form's background image."), Category("Display")>
<Editor(GetType(CatchFileName), GetType(System.Drawing.Design.UITypeEditor))>
Public Property BackgroundImage() As Image
Get
Return _Form.BackgroundImage
End Get
Set(ByVal Value As Image)
_Form.BackgroundImage = Value
End Set
End Property
_Form is declared as Form in the wrapper class FormPropertiesWrapper
Upvotes: 3
Views: 282
Reputation: 11801
The PropertyGrid uses a component's TypeDescriptor to determine what is shown in the grid via the TypeDescriptor.GetProperties Method. You can create a wrapper class that derives from CustomTypeDescriptor Class and override the GetProperties method to provide a new PropertyDescriptor that includes the EditorAttribute that points to your CatchFileName
class.
I modified your CatchFileName
class to work against an interface called IBackgroundImageName
that needs to implement in the wrapped control. This wrapper will work with any control that implements BackgroundImageProxyWrapper.IBackgroundImageName
. This way the the image name is stored in the control instead of the wrapper.
Imports System.ComponentModel
Imports System.Drawing.Design
Imports System.IO
Friend Class BackgroundImageProxyWrapper : Inherits CustomTypeDescriptor
Private Sub New(source As IBackgroundImageName)
MyBase.New(TypeDescriptor.GetProvider(source).GetTypeDescriptor(source))
SourceControl = source
End Sub
Public Shared Function Wrap(Of T As {Control, IBackgroundImageName})(source As T) As BackgroundImageProxyWrapper
Return New BackgroundImageProxyWrapper(source)
End Function
Public ReadOnly Property SourceControl As IBackgroundImageName
Public Overrides Function GetProperties() As PropertyDescriptorCollection
Return GetProperties(Nothing)
End Function
Public Overrides Function GetProperties(attributes() As Attribute) As PropertyDescriptorCollection
Dim ret As PropertyDescriptorCollection
ret = New PropertyDescriptorCollection(New PropertyDescriptor() {}, False)
For Each pd As PropertyDescriptor In MyBase.GetProperties(attributes)
If pd.Name.Equals("BackgroundImage") Then
' substitute a PropertyDescriptor that includes the EditorAttribute
Dim attribs As New List(Of Attribute)(pd.Attributes.Count + 1)
Dim editorType As Type = GetType(EditorAttribute)
' 1st remove any previous editor attribute
For Each attrib As Attribute In pd.Attributes
If attrib.GetType IsNot editorType Then
attribs.Add(attrib)
End If
Next
attribs.Add(New EditorAttribute(GetType(CatchFileName), GetType(UITypeEditor)))
pd = TypeDescriptor.CreateProperty(pd.ComponentType, pd, attribs.ToArray())
End If
ret.Add(pd)
Next
Return ret
End Function
Private Class CatchFileName : Inherits UITypeEditor
Public Overrides Function GetEditStyle(context As ITypeDescriptorContext) As UITypeEditorEditStyle
Return UITypeEditorEditStyle.Modal
End Function
Public Overrides Function EditValue(context As ITypeDescriptorContext,
provider As IServiceProvider, value As Object) As Object
Dim ofd As New OpenFileDialog() With {.Filter = "Image Files|*.bmp;*.jpg;*.jpeg,*.gif;*.png;*.ewf;*.wmf;*.ico"}
If (ofd.ShowDialog() = DialogResult.OK) Then
DirectCast(context.Instance, BackgroundImageProxyWrapper).SourceControl.BackgroundImageName =
Path.GetFileName(ofd.FileName) ' Strip path
Return Image.FromFile(ofd.FileName)
End If
Return MyBase.EditValue(context, provider, value)
End Function
End Class
Public Interface IBackgroundImageName
Property BackgroundImageName As String
End Interface
End Class
Example Usage:
Public Class Form1
Implements BackgroundImageProxyWrapper.IBackgroundImageName
Private Property BackgroundImageName As String Implements BackgroundImageProxyWrapper.IBackgroundImageName.BackgroundImageName
Private Sub BtnSetForm_Click(sender As Object, e As EventArgs) Handles BtnSetForm.Click
PropertyGrid1.SelectedObject = BackgroundImageProxyWrapper.Wrap(Me)
End Sub
End Class
Edit: A more generic version that will work on any control. The file name is stored in the control's Tag property.
Friend Class BackgroundImageProxyWrapper : Inherits CustomTypeDescriptor
Public Sub New(source As Control)
MyBase.New(TypeDescriptor.GetProvider(source).GetTypeDescriptor(source))
SourceControl = source
End Sub
Public ReadOnly Property SourceControl As Control
Public Overrides Function GetProperties() As PropertyDescriptorCollection
Return GetProperties(Nothing)
End Function
Public Overrides Function GetProperties(attributes() As Attribute) As PropertyDescriptorCollection
Dim ret As PropertyDescriptorCollection
ret = New PropertyDescriptorCollection(New PropertyDescriptor() {}, False)
For Each pd As PropertyDescriptor In MyBase.GetProperties(attributes)
If pd.Name.Equals("BackgroundImage") Then
' substitute a PropertyDescriptor that includes the EditorAttribute
Dim attribs As New List(Of Attribute)(pd.Attributes.Count + 1)
Dim editorType As Type = GetType(EditorAttribute)
' 1st remove any previous editor attribute
For Each attrib As Attribute In pd.Attributes
If attrib.GetType IsNot editorType Then
attribs.Add(attrib)
End If
Next
attribs.Add(New EditorAttribute(GetType(CatchFileName), GetType(UITypeEditor)))
pd = TypeDescriptor.CreateProperty(pd.ComponentType, pd, attribs.ToArray())
End If
ret.Add(pd)
Next
Return ret
End Function
Private Class CatchFileName : Inherits UITypeEditor
Public Overrides Function GetEditStyle(context As ITypeDescriptorContext) As UITypeEditorEditStyle
Return UITypeEditorEditStyle.Modal
End Function
Public Overrides Function EditValue(context As ITypeDescriptorContext,
provider As IServiceProvider, value As Object) As Object
Dim ofd As New OpenFileDialog() With {.Filter = "Image Files|*.bmp;*.jpg;*.jpeg,*.gif;*.png;*.ewf;*.wmf;*.ico"}
If (ofd.ShowDialog() = DialogResult.OK) Then
DirectCast(context.Instance, BackgroundImageProxyWrapper).SourceControl.Tag =
Path.GetFileName(ofd.FileName) ' Strip path
Return Image.FromFile(ofd.FileName)
End If
Return MyBase.EditValue(context, provider, value)
End Function
End Class
End Class
Upvotes: 2