I was working on something recently that required more than one default template. In other words the default template to be used by a control was determined by another property.
For example, suppose you have a Control called Shape
public enum ShapeType {
Red,
Black,
}
public class Shape : Control {
public Shape() {
this.DefaultStyleKey = typeof(Shape);
}
public ShapeType Type {
get { return (ShapeType)GetValue(TypeProperty); }
set { SetValue(TypeProperty, value); }
}
}
with a default style:
<Style TargetType="local:Shape">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:Shape">
<Rectangle Fill="Red"></Rectangle>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and you want the template to change based on the ShapeType. This is simple to do just by looking up the template from the resource dictionary.
First thing to do is to refactor the generic.xaml a little bit so that different control templates are available as resource items.
<ControlTemplate TargetType="local:Shape" x:Key="Shape:Black">
<Rectangle Fill="Black"></Rectangle>
</ControlTemplate>
<ControlTemplate TargetType="local:Shape" x:Key="Shape:Red">
<Rectangle Fill="Red"></Rectangle>
</ControlTemplate>
<Style TargetType="local:Shape">
<Setter Property="Template" Value="{StaticResource Shape:Red}"></Setter>
</Style>
The initial value for the Template is the same as the default value for the ShapeType enum.
Then, when the value of the Type changes, we will look up the control template:
public static readonly DependencyProperty TypeProperty =
DependencyProperty.Register("Type", typeof(ShapeType), typeof(Shape), new PropertyMetadata(new PropertyChangedCallback(delegate(DependencyObject d, DependencyPropertyChangedEventArgs e) {
Shape s = d as Shape;
if (s != null) {
s.OnShapeTypeChanged(true);
}
})));
private string GetTemplateName(ShapeType type) {
switch (type) {
case ShapeType.Red:
return "Shape:Red";
case ShapeType.Black:
return "Shape:Black";
default:
return null;
}
}
protected void OnShapeTypeChanged(bool applyTemplate) {
ResourceDictionary genericXaml = ResourceManager.GetResourceDictionary(null);
if (genericXaml == null) {
this.Template = null;
return;
}
string templateName = GetTemplateName(this.Type);
if (string.IsNullOrEmpty(templateName)) {
this.Template = null;
return;
}
this.Template = genericXaml[templateName] as ControlTemplate;
if (applyTemplate) {
this.ApplyTemplate();
}
}
An additional helper class ResourceManager:
internal static class ResourceManager {
private static Dictionary<string, ResourceDictionary> _resourceDictionaryCache =
new Dictionary<string, ResourceDictionary>();
/// <summary>
/// GetResourceDictionary
/// </summary>
public static ResourceDictionary GetResourceDictionary(string name) {
var resourceName = name;
if (string.IsNullOrEmpty(resourceName)) {
resourceName = "generic.xaml";
}
ResourceDictionary retVal = null;
if (_resourceDictionaryCache.TryGetValue(resourceName, out retVal)) {
return retVal;
}
System.Reflection.Assembly assembly = typeof(ResourceManager).Assembly;
string baseName = string.Format("{0}.g",
assembly.FullName.Split(new char[] { ',' })[0]);
string fullName = string.Format("{0}.resources", baseName);
foreach (string s in assembly.GetManifestResourceNames()) {
if (s == fullName) {
System.Resources.ResourceManager manager =
new System.Resources.ResourceManager(baseName, assembly);
using (System.IO.Stream stream = manager.GetStream(resourceName)) {
using (System.IO.StreamReader reader = new System.IO.StreamReader(stream)) {
retVal = System.Windows.Markup.XamlReader.Load(reader.ReadToEnd()) as ResourceDictionary;
if (retVal != null) {
_resourceDictionaryCache.Add(resourceName, retVal);
return retVal;
}
}
}
break;
}
}
return null;
}
}
And that's it.
Cheers,
Azret