WPF feature request: initializing object instances from style setters

Assume that you have a WPF CustomControl class (inheriting from Control) and you want to initialize its ContextMenu property to include a set of pre-created MenuItem objects that you would like to be available by default in your control instances, for example being bound to some application commands that they can handle. Usually, you would do it from the default control Style, using a Setter in XAML:

<Style TargetType=”{x:Type local:CustomControl1}”>
    <Setter Property=”ContextMenu“>
        <Setter.Value>
            <ContextMenu>
                <MenuItem Header=”Copy” Command=”ApplicationCommands.Copy”/>
                <MenuItem Header=”Paste” Command=”ApplicationCommands.Paste”/>
            </ContextMenu>
        </Setter.Value>
    </Setter>
    […]
</Style>

This works as expected in most cases, but sometimes the developer that uses your library will need multiple instances of your custom control, and sooner or later she would also need to add new, different MenuItem objects to the context menus of these control instances, preserving the default ones that are already available on each of them.

In this case, assuming that she has two CustomControl instances named CC1, and CC2, the developer tries to do this to add different specialized menu items on the two instances:

CC1.ContextMenu.Items.Add(new MenuItem { Header = “My Item 1“, […] });
CC2.ContextMenu.Items.Add(new MenuItem { Header = “My Item 2“, […] });

What do you think it happens? It’s not that obvious, but right clicking any of the two control instances will now show the same context menu, including 4 menu items: Copy, Paste, My Item 1, and My Item 2! The explanation is that the a single ContextMenu object instance was created (from the Setter.Value element in the default Style), and actually CC1.ContextMenu == CC2.ContextMenu returns true. The developer won’t be pleased as she would need to duplicate the menus, setting the ContextMenu property of each control instance to a different ContextMenu object. To preserve your default items, she will also need to duplicate your initialization code to redefine the Copy and Paste items, and she’ll now need to do it twice (one time for each instance).

What can you do to avoid this today? You can remove the style setter and create the ContextMenu from code behind, in your CustomControl constructor. But you’ll lost the XAML declarative features this way (C# code may be ugly if you have bindings and many other settings to apply). You could also try to define the default context menu as a resource, and then clone that when initializing the ContextMenu property of each new instance. But both workarounds are not acceptable from an elegance point of view. (If you know a third, better, way to do it, don’t hesitate to post a comment – I’ll appreciate it).

So, wouldn’t it be a lot better if you could something like this from the Style setter in XAML directly?

<!– This code doesn’t work, it’s just a suggestion! –>
<Setter Property=”ContextMenu” IsInstanceValue=”True”>
    <Setter.Value>…</Setter.Value>
</Setter>

Declaring that the setter value should be regenerated for each styled object instance by setting an IsInstanceValue declaration on the Setter (or maybe a better name could be found instead), the Framework could be told to create the value multiple times, one per each object instance that has the style applied (the created content could still survive theme changing this way). What do you think?

Update: A simple way to resolve this (at least when you don’t have multiple themes to support) is to not rely on the standard theming mechanism for your custom control. Instead, in the constructor of the control, merge the Generic.xaml file containing the control styles like below – this way objects defined as style setter values are recreated for each control instance, exactly as needed:

string assemblyName = GetType().Assembly.FullName;
int index = assemblyName.IndexOf(‘,’);
if (index >= 0)
    assemblyName = assemblyName.Substring(0, index);
Uri sourceUri = new Uri(string.Format(
    “pack://application:,,,/{0};component/Themes/Generic.xaml”, assemblyName));
Resources.MergedDictionaries.Add(
    new ResourceDictionary { Source = sourceUri });

About Sorin Dolha

My passion is software development, but I also like physics.
This entry was posted in Computers and Internet and tagged , , , , , , . Bookmark the permalink.

One Response to WPF feature request: initializing object instances from style setters

  1. Guillaume Pouillet says:

    It looks like you could also use the x:Shared=”false” marker on your style in order to re-instantiate the whole style on each use.
    For more information, see https://msdn.microsoft.com/en-us/library/aa970778(v=vs.110).aspx

Add a reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s