User-Provided DataTemplate

33 views Asked by At

I'm trying to figure out the best way to allow users to provide their own DataTemplates.

My app creates global hotkeys that users can write their own actions for in C#. These "actions" are just methods that are marked with specific attributes that mostly just provide metadata.

One of the attributes lets them add "settings" for an action which can be edited by the end-user. Settings can have values of any type, which is where my problem arises. I provided a few DataTemplates for common types, but for custom types I want users to be able to write their own DataTemplates and specify them directly through the attribute.

Since attributes don't allow for properties of type DataTemplate, I made an abstract DataTemplateProvider class that looks like this:

public abstract class DataTemplateProvider
{
    public abstract DataTemplate ProvideDataTemplate();
}

Users then override ProvideDataTemplate() in a derived class, then pass the Type of that class to the attribute.

Here's an example of a user-created action that does this:

[HotkeyActionGroup("Name")]
public class ExampleGroup
{
    [HotkeyAction]
    [HotkeyActionSetting("MySetting", valueType: typeof(bool), dataTemplateProviderType: typeof(MyDataTemplateProvider))]
    public void ExampleMethod(object sender, HotkeyActionPressedEventArgs e)
    {
        // do something with MySetting
    }
}
public class MyDataTemplateProvider : DataTemplateProvider
{
    public override DataTemplate ProvideDataTemplate()
    {
        var checkboxFactory = new FrameworkElementFactory(typeof(CheckBox));

        // bind the IsChecked property to the action setting instance's Value property (which is a bool)
        checkboxFactory.SetBinding(CheckBox.IsCheckedProperty, new Binding("Value"));

        return new DataTemplate(typeof(bool)) { VisualTree = checkboxFactory };
    }
}

While this does work, FrameworkElementFactory is deprecated and creating larger and more complicated templates is cumbersome to say the least.
Microsoft's recommended replacement for FrameworkElementFactory is to use XamlReader.Load() along with a XAML file (or string... somehow).
While this is more straightforward to use, it presents its own problems. When I define a DataTemplate in an embedded XAML resource file, I have nowhere to define event handlers for RoutedEvents.

I tried loading the data template from XAML in ProvideDataTemplate(), then modifying it before returning the modified template, but that doesn't seem to actually be possible.
Accessing the visual tree of the FrameworkElementFactory returned from XamlReader doesn't work since loading that is deferred until after calling FrameworkElementFactory.LoadContent(), but calling LoadContent() seems to load & instantiate the template, which means I can't modify it.

The ideal answer would include some way to define event handlers in a XAML file with a DataTemplate as its root object, or to modify it programmatically in order to add event handlers, but I'm also open to suggestions of other ways to accomplish this.

0

There are 0 answers