Question

I am using ModernUIIcons that come with MahApps.Metro in a Xaml file as StaticResources. To put one in my UI is very easy, like this:

<Rectangle  Width="19" 
            Height="19">
   <Rectangle.Fill>
      <VisualBrush Visual="{StaticResource appbar_database}" />
   </Rectangle.Fill>
</Rectangle>

I want to encapsulate all the logic of the rectangle in a CustomControl so I can do something like the following:

<cc:MenuItemIcon Source="{StaticResource appbar_page}"/>

This is what I got so far:

In one project as library, Themes/Generic.xaml:

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:AMIGEDM.CustomControls.Menu">

<Style TargetType="{x:Type local:MenuItemIcon}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MenuItemIcon}">
                <Rectangle  Width="19" 
                            Height="19">
                    <Rectangle.Fill>
                        <VisualBrush Visual="{TemplateBinding Source}" />
                    </Rectangle.Fill>
                </Rectangle>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
</ResourceDictionary>

And in the CS file

namespace AMIGEDM.CustomControls.Menu
{
   public class MenuItemIcon : Control
{
   static MenuItemIcon()
   {
      DefaultStyleKeyProperty.OverrideMetadata(typeof(MenuItemIcon), new  FrameworkPropertyMetadata(typeof(MenuItemIcon)));
   }

   public static readonly DependencyProperty SourceProperty =
      DependencyProperty.Register("Source", typeof(Visual), typeof(MenuItemIcon));

   public Visual Source
   {
      get { return (Visual)GetValue(SourceProperty); }
      set { SetValue(SourceProperty, value); }
   }
  }
}

Everything compiles silky smooth, so I go to my TestDummy Project

<Window x:Class="AMIGEDM.TestDummy.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525"
    xmlns:cc="clr-namespace:AMIGEDM.CustomControls.Menu;assembly=AMIGEDM.CustomControls">
<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/AMIGEDM.TestDummy;component/Resources/Icons.xaml"/>
            <ResourceDictionary Source="pack://application:,,,/AMIGEDM.CustomControls;component/Themes/Generic.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>
<Grid>
    <Menu IsMainMenu="True" SnapsToDevicePixels="True">
        <MenuItem Header="_Open">
            <MenuItem Header="_File">
                <MenuItem.Icon>
                    <cc:MenuItemIcon Source="{StaticResource appbar_page}"/>
                </MenuItem.Icon>
            </MenuItem>
            <MenuItem Header="_File">
                <MenuItem.Icon>
                    <Rectangle  Width="19" 
                            Height="19">
                        <Rectangle.Fill>
                            <VisualBrush Visual="{StaticResource appbar_database}" />
                        </Rectangle.Fill>
                    </Rectangle>
                </MenuItem.Icon>
            </MenuItem>
        </MenuItem>
    </Menu>
</Grid>

The Library puts the Icon using the Rectangle, Rectangle Fill and VisualBrush, but when I try to use the CustomControl it shows nothing

As you see, the icon appears using the complete rectangle but not with my custom control

Was it helpful?

Solution

All the code looks normal, except for the style of MenuItemIcon. Quote about TemplateBinding from Adam Nathan book:

TemplateBinding doesn’t work outside a template or outside its VisualTree property, so you can’t even use TemplateBinding inside a template’s trigger. Furthermore, TemplateBinding doesn’t work when applied to a Freezable (for mostly artificial reasons).

And quote from MSDN about VisualBrush:

Freezable Features: Because it inherits from the Freezable class, the VisualBrush class provides several special features: VisualBrush objects can be declared as resources and shared among multiple objects.

Therefore instead of:

<VisualBrush Visual="{TemplateBinding Source}" />

Use the construction {RelativeSource TemplatedParent} and a Path equal to the dependency property whose value you want to retrieve:

<Style TargetType="{x:Type local:MenuItemIcon}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MenuItemIcon}">
                <Rectangle Width="22" Height="22">
                    <Rectangle.Fill>
                        <VisualBrush Visual="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Source}" />                                
                    </Rectangle.Fill>
                </Rectangle>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top