Custom property editors

Grids for WPF Forum

Posted 10 years ago by Rick Edwards - UK
Version: 9.1.0501
Avatar
Hi all,

I'm trying to create a custom property editor and apply it to a property via the "Editor" attribute. I've created a class CustomEditor.cs that extends the base PropertyEditor and have tried to associate it to a DataTemplate in a resource directory thus:

 <DataTemplate DataType="{x:Type pe:CustomEditor}">
  
  ...

 </DataTemplate>
On my property I add the attribute:

[Editor(typeof(CustomEditor), typeof(PropertyEditor))]
However when I select the object for the PropertyGrid the CustomEditor class is called but the custom DataTemplate is not used and the PropertyGrid simply uses a default editor for the correct property type.

Any ideas where I'm going wrong, is this just me not using WPF correctly or am I trying to associate the CustomEditor to the wrong type of template in the resources? Are there any examples of trying to wire in custom editors in this manner?

Thanks

Rick

Comments (14)

Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Rick,

Your CustomEditor needs override the ValueTemplate, ValueTemplateKey, or ValueTemplateSelector property and return the appropriate value. So in your case, you could probably just return typeof(CustomEditor) from the ValueTemplateKey override.

That's actually a pretty convenient way to define the property editor that we had not thought of before. I've marked down to look into building support for that on our TODO list.


Actipro Software Support

Posted 10 years ago by Rick Edwards - UK
Avatar
Hi thanks for your help.

I've overridden the ValueTemplateKey as suggested:


    public class CustomEditor : PropertyEditor
    {
        private ResourceKey customEditorKey = new ComponentResourceKey(typeof (CustomEditor), "CustomEditor");
        public override ResourceKey ValueTemplateKey
        {
            get
            {
                return this.customEditorKey;
            }
            set
            {
                this.customEditorKey = value;
            }
        }
    }

Unfortunately the PropertyGrid throws a resource not found exception. Where (in which element) does the PropertyGrid programatically seek resources? I've declared my custom editor DataTemplate in the resources for the PropertyGrid:


  <propgrid:PropertyGrid
    Name ="actiproPropertyGrid"
    AreAttachedPropertiesBrowsable="True"
    SelectedObject="{Binding SelectedObject}"
    DataFactory="{Binding DataFactory}">
    
    <propgrid:PropertyGrid.Resources>
      <DataTemplate DataType="{x:Type pe:CustomEditor}" x:Key="CustomEditor">
        <propgrid:PropertyGridPropertyItem
                  DisplayName="My Custom Editor"
                  Value="{x:Static Colors.Red}"
                  DefaultValue="Test"
                  Description="A test editor.">
                  
          <propgrid:PropertyGridPropertyItem.ValueTemplate>
            <DataTemplate>

              ...

            </DataTemplate>
          </propgrid:PropertyGridPropertyItem.ValueTemplate>
        </propgrid:PropertyGridPropertyItem>
      </DataTemplate>
    </propgrid:PropertyGrid.Resources>
  </propgrid:PropertyGrid>

Do I need to define the resource elsewhere?

Thanks

Rick
Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Rick,

In the PropertyEditor you are using a ComponentResourceKey, but in the XAML you are simply using the string "CustomEditor". So you should just return "CustomEditor" from the getter for ValueTemplateKey, and leave the setter empty.


Actipro Software Support

Posted 10 years ago by Rick Edwards - UK
Avatar
???

Sorry not sure I follow this, the overridden ValueTemplateKey returns a ResourceKey not a string, simply returning "CustomEditor" throws a compile error as a string cannot be implicitely cast to a ResourceKey. How do I cast a string to a ResourceKey abstraction or create a ResourceKey of the correct type from the "CustomEditor" string?

Thanks

Rick
Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Rick,

I apologize, I thought the ValueTemplateKey property was an object. I've actually changed it so ValueTemplateKey is an object (i.e. not a ResourceKey) for the next maintenance release. This allows you to use strings or Type objects, in addition to ComponentResourceKey.

In the mean time, you would need to return the ComponentResourceKey like you previously had and use the same ComponentResourceKey as the key for your DataTemplate. So you would need to change your DataTemplate like so:
<DataTemplate x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:CustomEditor}, ResourceId=CustomEditor}">
    ...
</DataTemplate>
Also, based on the last DataTemplate code you posted, you should not include a PropertyGridPropertyItem in your DataTemplate. The PropertyGridPropertyItem should only be used to explicitly define properties to be included in the PropertyGrid (by inserting an instance in PropertyGrid.Properties). The DataTemplate should just include the controls you want to use to modify the associated property. So if you have a string property, it would only include a TextBox control.


Actipro Software Support

Posted 10 years ago by Rick Edwards - UK
Avatar
Hi thanks for that, seems to work fine and provides a really simple way to map a custom editor object to a set of XAML.

One question though, how can I bind to properties exposed through the CustomEditor class in the DataTemplate? For example if I have a string property in the CustomEditor class (defined in the above posts) that I want to use as text for ca. a button in the DataTemplate mapped to the CustomEditor type, how do I find the associated CustomEditor object? I can bind to the ancestor IPropertyDataAccessor but can't find the CustomEditor.

Thanks for your ongoing help, all very much appreciated.

Rick
Posted 10 years ago by Actipro Software Support - Cleveland, OH, USA
Avatar
Hi Rick,

There isn't a straight-forward way to gain access to your CustomEditor. The problem is that it's not accessible anywhere in the Visual or Logical trees. One solution that we implemented with Editors.Interop.PropertyGrid, is to inject a ContentPresenter. The Content is set to the property editor instance, and the ContentTemplate can be set to the actual DataTemplate to be used.

For your code above, you would remove the ValueTemplateKey override and do something like:
private DataTemplate dataTemplate = null;

public override DataTemplate ValueTemplate {
    get {
        if (null == this.dataTemplate) {
            FrameworkElementFactory factory = new FrameworkElementFactory(typeof(ContentPresenter));
            factory.SetValue(ContentPresenter.ContentProperty, this);
            factory.SetResourceReference(ContentPresenter.ContentTemplateProperty, this.customEditorKey);

            this.dataTemplate = new DataTemplate() { VisualTree = factory };
        }
        return this.dataTemplate;
    }
    set { /* No-op */ }
}
Then in your DataTemplate, you can access the CustomEditor using something like "{Binding PropertyOnCustomEditor}".

Hope this helps.


Actipro Software Support

Posted 10 years ago by Rick Edwards - UK
Avatar
Excellent, thanks for that, all seems to be working.

Just in case anyone else is interested I managed to bind commands on the DataTemplate to my custom editor class via a DelegateCommand and pass in the IPropertyDataAccessor as a CommandParameter.

So in my custom editor class I have:


private DelegateCommand<IPropertyDataAccessor> someCommand = null;

public ICommand SomeCommand
{
  get
    {
      if (this.someCommand == null)
      {
        this.someCommand = new DelegateCommand<IPropertyDataAccessor>(DoCommandExecute, DoCommandCanExecute);
      }
      return this.someCommand;
    }
}

public void DoCommandExecute(IPropertyDataAccessor dataAccessor)
{
  ...
}

public bool DoCommandCanExecute(IPropertyDataAccessor dataAccessor)
{
  ...
}

And in the DataTemplate:


<DataTemplate x:Key="{ComponentResourceKey
              TypeInTargetAssembly={x:Type pe:FolderLocationDialogEditor},
              ResourceId=FolderLocationDialogEditor}">
        
  <Grid>
    <shared:PopupButton Content="..."
                        DisplayMode="ButtonOnly"
            IsTransparencyModeEnabled="False"
            Command="{Binding ShowDialogCommand}"
                        CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type propgrid:IPropertyDataAccessor}}}"
    />
  </Grid>
</DataTemplate>

Seems to work well thanks to your help.

Now on to custom type converters......

Regards

Rick
Posted 9 years ago by Eli Obadia
Avatar
Rick, I'm trying to build a small custom editor for the property grid, and I'm a little bit lost, do you have like a code a snipet of what worked for you... the Data template in th xml and the code in the c#?

Thanks,
Eli
Posted 9 years ago by Rick Edwards - UK
Avatar
Hi Eli,

unfortunately I don't have any code to hand that I can send you right now and I won't be available for the following week so let me know if you're still struggling in a weeks time and I'll try to sort out an example app.

Sorry I can't be more help right now, busy busy!

Regards

Rick
Posted 8 years ago by Mick
Avatar
Eli,

Do you still need a sample of this?

I came across this post and realized I have a sample project that lays out a few different ways to use the PropertyGrid (using DataTemplates and such).

Let me know and I'll find a way to get it to you (it's somewhat cumbersome to post it all here).

Mick
Posted 8 years ago by Eli Obadia
Avatar
Thanks Mick!,

I was able to work it out more or less but any help will be appreciated!,

Eli
Posted 8 years ago by Eli Obadia
Avatar
Thanks Mick!,

I was able to work it out more or less but any help will be appreciated!,

Eli
Posted 8 years ago by Mick
Avatar
You can contact me at mickullman at finfolio dot com if you want.

Mick
The latest build of this product (v2018.1 build 0675) was released 10 days ago, which was after the last post in this thread.

Add Comment

Please log in to a validated account to post comments.