Cannot add content of type '...' to an object of type 'System.Windows.ResourceDictionary'

In order to customize controls the way you want, you can either use a DataTemplate, different specific ControlTemplate properties, use the properties that the controls publish, but in the end, to get it right, you must actually change the ControlTemplate of the control itself. The best practice is to use an external resource dictionary that contains a style that changes various properties and also, if needed, the ControlTemplate.
The problem there is that there is no way to partially change the control template. I mean, yeah, I could think of a few ways, but in the end is the same thing: you need to replace the control template completely. And now you have two ... err.. actually three problems.
First, there are ways of getting the default control template, with all the functionality (we are talking about one, two or even more pages of XAML) and change only what you want. It's not trivial, but it can be done, either by copying it from the published sources (like the Windows SDK or the Expression Blend SystemThemes folder) or by using a utility like ShowMeTheTemplate.
Second, there is the issue of control templates differing depending on the Windows theme. And therefore one must either accept that the application will only look one way for every theme and Windows operating system, or provide templates for all the themes available (there are 6 defaults and a generic fallback theme).
Now, there are many tutorials about how to do that, basically just create a Themes folder, change AssemblyInfo.cs to
[assembly: ThemeInfo(
ResourceDictionaryLocation.SourceAssembly, //where theme specific resource dictionaries are located
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
)]
, copy the xaml files (with certain names) there and change them as you see fit. There is a catch, though! This will only work on your custom controls. Default controls already have a template in the operating system theme, so you need to do one extra thing in order to overwrite them, and that is to write the following in the a parent control Resources block:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="{ThemeDictionary [LibraryOrApplicationNamespace]}" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
otherwise it will NOT work.
You can get the same result by adding this to the static constructor of the control, provided you have access to the sources:
DefaultStyleKeyProperty.OverrideMetadata(typeof([MyControlClass]), new FrameworkPropertyMetadata(typeof(MyControlClass)));
One thing you also need to be aware of is that Visual Studio caches the themes. If you change them, especially if you do it outside visual Studio, you need to force a Rebuild of the project.
I've decided to write this entry before I could actually master these tools because I don't think I will afterwards (I'd be too old to type). So here it goes:
All of the tools above, except ReSharper and Blend are free to use.
I was trying to bind a property to the value of a property of the parent UserControl in WPF. When you google for it you get something pretty interesting: give a name to the user control right inside the user control XAML, then reference it with ElementName.
What I found after hours of torment was that this doesn't always work. I guess it only works up to the first item container in the logical tree and then stops. So that when I tried to use a property ImageSource of the user control in a Menu.Icon it failed!
My solution looks pretty ugly, but it does work in all cases!
<Image Source="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Controls:MyUserControl}},Path=MainMenuImageSource}"
Width="16" Height="16"></Image>
In other words: find the ancestor of the type of your user control and use it as the RelativeSource of the binding.
public class SectionHeaderConverter:IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
List<ToolBarItem> list = new List<ToolBarItem>();
foreach (IEnumerable<ToolBarItem> collection in values)
{
foreach (ToolBarItem element in collection)
{
list.Add(element);
var toolBarSectionHeader = element as ToolBarSectionHeader;
if (toolBarSectionHeader != null)
{
foreach (var item in toolBarSectionHeader.Items)
{
list.Add(item);
}
}
}
}
return list;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
<MultiBinding Converter="{StaticResource SectionHeaderConverter}">
<Binding Path="Items"/>
</MultiBinding>
<Menu
ItemsSource="{Binding ElementName=ToolBarCurrent,Path=Items}"
ItemContainerStyleSelector="{StaticResource MenuItemContainerStyleSelector}"
ItemTemplateSelector="{StaticResource MenuItemTemplateStyleSelector}"
></Menu>
ItemsControl ic = ItemsControl.ItemsControlFromItemContainer(container);
var style = (Style) ic.FindResource("SectionStyle");
var ic = Helper.FindVisualParent<Grid>(container);The source for it is underneath.
.
public static TParentItem FindVisualParent<TParentItem>(DependencyObject obj)
where TParentItem : DependencyObject
{
DependencyObject current = obj;
while (current != null)
{
var tCurrent = current as TParentItem;
if (tCurrent != null)
return tCurrent;
current = VisualTreeHelper.GetParent(current);
}
return null;
}
<HierarchicalDataTemplate DataType="{x:Type Code:League}" ItemsSource="{Binding Path=Divisions}">Somewhere in the Window declaration there was a
<TextBlock Text="{Binding Path=Name}"/>
</HierarchicalDataTemplate>
xmlns:Code="clr-namespace:MyControls.Desktop.Code"which is the namespace of the test application for the control in the library.