我已经阅读了将近一千篇文章,它们解释说在DataType
上将封闭的通用类型设置为DataTemplate
是行不通的,因为WPF不支持。但是事实上,这是错误的。
我可以在DataTemplate
中定义以下Window.Resources
,当我将字符串列表分配给内容控件时将使用它。例如:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Generic="clr-namespace:System.Collections.Generic;assembly=mscorlib"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type TypeName=Generic:List`1[System.String]}">
<TextBlock Text="Hi List of Strings"
FontSize="40"
Foreground="Cyan"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl x:Name="_contentControl">
</ContentControl>
</Grid>
</Window>
和隐藏在代码中:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
_contentControl.Content = new List<string> { "Huhu" };
}
}
使用此设置,您将看到“高字符串列表”。对我来说,这就是我可以将泛型定义为DataType
的证明。但我想更进一步:我想将Dictionary<string, string>
定义为DataType
。但不幸的是,我无法使它正常工作。
问题是:如何将Dictionary<string, string>
定义为DataType
的{{1}}?
如果您知道答案,则可以停止阅读。但是由于这是展示我已经做过的好习惯,所以我继续写作。 我已经做了什么? 刚开始我就蛮力尝试了几种类似的组合:
DataTemplate
但是,由于它们都不起作用,因此我深入研究- DataType="{x:Type TypeName=Generic:Dictionary`2[System.String];[System.String]}"
- DataType="{x:Type TypeName=Generic:Dictionary`2[System.String],[System.String]}"
- DataType="{x:Type TypeName=Generic:Dictionary`2[System.String,System.String]}"
,并查看了System.Xaml
,TypeExtension
和GenericTypeNameParser
,因为我认为这些是可以解决类型。但是看着代码,我意识到`是一个无效字符。
为了证明这一点,我写了自己的GenericTypeNameScanner
MarkupExtension
并按如下方式使用它:
public class UseTheTypeExtensionsParser : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
var a = new TypeExtension("Generic:List`1[[System.String]]");
var type = a.ProvideValue(serviceProvider);
return type.ToString();
}
}
这引发了一个例外,即字符'不是预期的,并且xaml类型无效。
这让我想知道为什么我的第一个示例有效。我认为,在为wpf标记xaml进行编译时,不是<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Generic="clr-namespace:System.Collections.Generic;assembly=mscorlib"
xmlns:WpfApp1="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ContentControl Content="{WpfApp1:UseTheTypeExtensionsParser}"/>
</Grid>
</Window>
用于解析XamlType,但是我认为使用了TypeExtension
。因为此类具有使用`-字符的XamlNamespace
方法。
但是我仍然看不到提取类型参数的代码,因此我看不到为Dictionary指定类型参数的正确语法。这就是我被困住的地方。
(不必说Microsoft文档在这个主题上毫无价值。)
编辑:由于不清楚为什么要这样做,我将对其进行解释:我希望自动选择ContentControl的ContentTemplate。当然,我在示例中构造的MangleGenericTypeName
非常简单。但是每个人都应该能够想象,我想要列表,字典或简单字符串使用不同的DataTemplates。
我有一个具有DataTemplate
属性的ViewModel。有时,结果是一个int,有时是一个字符串,有时是一个List等,依此类推。我将此public object Result { get; }
属性绑定到Result
的{{1}}属性。对于提到的所有类型,我编写了不同的DataTemplates,这些模板由WPF自动选择。因此Content
显示在ContentControl
中,而int
显示在Rectangle
中。
在所有这些工作之后,我想要另一个DataTemplate,但这一次是Dictionary。
答案 0 :(得分:0)
我可以使用以下代码:
编写一个MarkupExtension,将您想要的闭合通用类型作为DataTemplate的DataType返回(这不是我自己的。它位于SO的某处,但我没有保留链接)。
public class GenericType : MarkupExtension
{
public GenericType() { }
public GenericType(Type baseType, params Type[] innerTypes)
{
BaseType = baseType;
InnerTypes = innerTypes;
}
public Type BaseType { get; set; }
public Type[] InnerTypes { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
Type result = BaseType.MakeGenericType(InnerTypes);
return result;
}
}
按以下方式使用它:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:Generic="clr-namespace:System.Collections.Generic;assembly=mscorlib"
xmlns:WpfApp1="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<x:Array Type="{x:Type System:Type}"
x:Key="ListWithTwoStringTypes">
<x:Type TypeName="System:String" />
<x:Type TypeName="System:String" />
</x:Array>
<WpfApp1:GenericType BaseType="{x:Type TypeName=Generic:Dictionary`2}"
InnerTypes="{StaticResource ListWithTwoStringTypes}"
x:Key="DictionaryStringString" />
<DataTemplate DataType="{StaticResource DictionaryStringString}">
<TextBlock Text="Hi Dictionary"
FontSize="40"
Foreground="Cyan"/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl x:Name="_contentControl"/>
</Grid>
</Window>
要查看是否自动应用了DataTemplate,请使用可以在代码后边写:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
_contentControl.Content = new Dictionary<string, string>();
}
}
您将看到您的DataTemplate。
但是在我的项目中,我有一个专用的程序集,用于编写所有DataTemplates和ControlTemplates的样式。通常我有一个ResourceDictionary来保存它们。但是,当我要将DataTemplate放在ResourceDictionary中时,编译器告诉我它没有Key。
这不起作用:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:DataTemplates="clr-namespace:Dana.Styles.Flat.DataTemplates"
xmlns:Generic="clr-namespace:System.Collections.Generic;assembly=mscorlib">
<x:Array Type="{x:Type System:Type}"
x:Key="ListWithTwoStringTypes">
<x:Type TypeName="System:String" />
<x:Type TypeName="System:String" />
</x:Array>
<DataTemplates:GenericType BaseType="{x:Type TypeName=Generic:Dictionary`2}"
InnerTypes="{StaticResource ListWithTwoStringTypes}"
x:Key="DictionaryStringString" />
<DataTemplate DataType="{StaticResource DictionaryStringString}">
<TextBlock Text="Hi Dictionary"
FontSize="40"
Foreground="Cyan"/>
</DataTemplate>
</ResourceDictionary>
作为一种解决方法,我现在在FrameworkElement的资源中定义我的DataTemplates,并将它们以代码隐藏的方式添加到Application.Resources。
这是DictionaryStringString.xaml
<FrameworkElement x:Class="Dana.Styles.Flat.DataTemplates.DictionaryStringString"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:Generic="clr-namespace:System.Collections.Generic;assembly=mscorlib"
xmlns:DataTemplates="clr-namespace:Dana.Styles.Flat.DataTemplates"
xmlns:System="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<FrameworkElement.Resources>
<x:Array Type="{x:Type System:Type}"
x:Key="ListWithTwoStringTypes">
<x:Type TypeName="System:String" />
<x:Type TypeName="System:String" />
</x:Array>
<DataTemplates:GenericType BaseType="{x:Type TypeName=Generic:Dictionary`2}"
InnerTypes="{StaticResource ListWithTwoStringTypes}"
x:Key="DictionaryStringString" />
<DataTemplate DataType="{StaticResource DictionaryStringString}">
<TextBlock Text="Hallo Wörterbuch"
FontSize="40"
Foreground="Cyan"/>Template>
</ItemsControl>-->
</DataTemplate>
</FrameworkElement.Resources>
</FrameworkElement>
这是DictionaryStringString.xaml.cs:
public partial class DictionaryStringString
{
/// <summary>
/// Konstruktor
/// </summary>
public DictionaryStringString()
{
InitializeComponent();
}
}
然后,在我初始化样式的地方添加
:var _dictionaryStringString = new DictionaryStringString();
Application.Current.Resources.MergedDictionaries.Add(_dictionaryStringString.Resources);
现在我可以为所有封闭的泛型类型定义DataTemplates,并通过WPF =)自动应用它们。