Mar*_*eIV 7 c# wpf datatemplate hierarchicaldatatemplate datatemplateselector
我已经创建了一个DataTemplateSelector,它使用已知接口的集合进行初始化.如果传入选择器的项目实现其中一个接口,则返回关联的数据模板.
首先,这是有问题的ICategory接口......
public interface ICategory
{
ICategory ParentCategory { get; set; }
string Name { get; set; }
ICategoryCollection Subcategories { get; }
}
Run Code Online (Sandbox Code Playgroud)
这是基于基类或接口而不仅仅是特定具体类匹配的DataTemplateSelector ...
[ContentProperty("BaseTypeMappings")]
public class SubclassedTypeTemplateSelector : DataTemplateSelector
{
private delegate object TryFindResourceDelegate(object key);
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var frameworkElement = container as FrameworkElement;
foreach(var baseTypeMapping in BaseTypeMappings)
{
// Check if the item is an instance of, a subclass of,
// or implements the interface specified in BaseType
if(baseTypeMapping.BaseType.IsInstanceOfType(item))
{
// Create a key based on the BaseType, (not item.DataType as usual)
var resourceKey = new DataTemplateKey(baseTypeMapping.BaseType);
// Get TryFindResource method from either the FrameworkElement,
// or from the application
var tryFindResource = (frameworkElement != null)
? (TryFindResourceDelegate)frameworkElement.TryFindResource
: Application.Current.TryFindResource;
// Use the TryFindResource delegate from above to try finding
// the resource based on the resource key
var dataTemplate = (DataTemplate)tryFindResource(resourceKey);
dataTemplate.DataType = item.GetType();
if(dataTemplate != null)
return dataTemplate;
}
}
var defaultTemplate = DefaultDataTemplate ?? base.SelectTemplate(item, container);
return defaultTemplate;
}
public DataTemplate DefaultDataTemplate { get; set; }
public Collection<BaseTypeMapping> BaseTypeMappings { get; } = new Collection<BaseTypeMapping>();
}
public class BaseTypeMapping
{
public Type BaseType { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
以下是它在资源中的设置方式以及各自的HierarchicalDataTemplate和DataType = ICategory ......
<HierarchicalDataTemplate DataType="{x:Type model:ICategory}"
ItemsSource="{Binding Subcategories}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
<is:SubclassedTypeTemplateSelector x:Key="SubclassedTypeTemplateSelector">
<!--<is:BaseTypeMapping BaseType="{x:Type model:ICategory}" />-->
</is:SubclassedTypeTemplateSelector>
Run Code Online (Sandbox Code Playgroud)
最后,这是一个使用它的TreeView ......
<TreeView x:Name="MainTreeView"
ItemsSource="{Binding Categories}"
ItemTemplateSelector="{StaticResource SubclassedTypeTemplateSelector}" />
Run Code Online (Sandbox Code Playgroud)
我已经调试了它,并且可以确认正确的数据模板是否按预期返回到TreeView,并且因为TreeView正在按照HierarchicalDataTemplate上的ItemSource绑定正确加载子类别.所有这些都按预期工作.
什么不起作用的是模板本身的内容.正如您所看到的,模板只是显示类别的名称,但它只是将对象原始呈现,就好像它直接放在没有任何模板的ContentPresenter中一样.你在UI中看到的只是ToString的结果.模板的内容完全被忽略.
我唯一能想到的是它没有工作,因为我正在使用DataType的接口,但同样,子项的ItemsSource的绑定确实有效,所以我有点难过.
值得注意的是:作为测试,我根据具体类型(即Category而不仅仅是ICategory)创建了第二个DataTemplate,当我这样做时,它按预期工作.问题是具体类型是在一个不应该由UI引用的程序集中.这就是我们首先使用接口的全部原因.
*注意:我还尝试使用Key而不是设置DataType属性来更改我查找模板的方式.在这种情况下,就像以前一样,选择器仍然找到相同的资源,但它仍然不起作用!
然而讽刺的是,如果我使用的是相同的密钥来直接通过StaticResource的绑定设置TreeView控件的ItemTemplate中,那么它做的工作,这意味着它只有当我从选择返回模板不工作,也没有出现相关的数据类型是否设定与否.*
不起作用的是模板本身的内容
这是因为,由于 DataType 属性设置为接口类型,因此未应用您在 XAML 标记中定义的模板。正如 @Manfred Radlwimmer 所说,这是设计使然:https://social.msdn.microsoft.com/Forums/vstudio/en-US/1e774a24-0deb-4acd-a719-32abd847041d/data-templates-and-interfaces ?forum= .wpf . 从 DataTemplateSelector 返回这样的模板并不会让它像您已经发现的那样工作。
但是,如果您使用 DataTemplateSelector 选择适当的数据模板,您可以从数据模板中删除 DataType 属性,并为每个模板指定一个唯一的 x:Key:
<HierarchicalDataTemplate x:Key="ICategory" ItemsSource="{Binding Subcategories}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
Run Code Online (Sandbox Code Playgroud)
然后您应该能够使用此密钥解析资源,例如:
var baseTypeName = "ICategory";
var dataTemplate = (DataTemplate)tryFindResource("baseTypeName");
Run Code Online (Sandbox Code Playgroud)