DataTemplate 绑定取决于 属性 类型和有效的 属性 绑定
DataTemplate Binding depending on property type and with working property Binding
我查看了那些关于做 DataTemplate 的文章:
- WPF DataTemplate Binding
- WPF DataTemplate and Binding
- WPF DataTemplate Textblock binding
以及那些关于 DataTemplate 的内容取决于 属性 类型:
- WPF DataTemplate Binding depending on the type of a property
- Dynamically display a control depending on bound property using WPF
我正在尝试根据 属性 值显示具有不同控件的 属性。我有这个 Xaml 可以部分工作。我有 2 个问题:
属性 使用正确的控件显示,但是当我设置值时它不会返回到 属性。意味着我的 属性 的 "set" 没有被调用(但是在我创建 DataTemplate 之前)。我检测到关于设置 属性 的问题是关于 ="{Binding Path=.}" 但我找不到设置它的解决方案。
此外,为了能够使其工作,我必须 "isolate" 将值放入单个 ViewModel 中,以便 DataTemplate 不会影响所有其他控件。
你能帮我找到更好的解决方案来解决这两个问题吗?
这是与 MyContainerViewModel 链接的视图的 xaml 代码,它具有 "ChangingDataType" :
<UserControl >
<UserControl.Resources>
<!-- DataTemplate for strings -->
<DataTemplate DataType="{x:Type sys:String}">
<TextBox Text="{Binding Path=.}" HorizontalAlignment="Stretch"/>
</DataTemplate>
<!-- DataTemplate for bool -->
<DataTemplate DataType="{x:Type sys:Boolean}">
<CheckBox IsChecked="{Binding Path=.}" />
</DataTemplate>
<!-- DataTemplate for Int32 -->
<DataTemplate DataType="{x:Type sys:Int32}">
<dxe:TextEdit Text="{Binding Path=.}" MinWidth="50" Mask="d" MaskType="Numeric" HorizontalAlignment="Stretch"/>
<!--<Slider Maximum="100" Minimum="0" Value="{Binding Path=.}" Width="100" />-->
</DataTemplate>
<!-- DataTemplate for decimals -->
<DataTemplate DataType="{x:Type sys:Decimal}">
<!-- <TextBox Text="{Binding Path=.}" MinWidth="50" HorizontalAlignment="Stretch" />-->
<dxe:TextEdit Text="{Binding Path=.}" MinWidth="50" Mask="f" MaskType="Numeric" HorizontalAlignment="Stretch" />
</DataTemplate>
<!-- DataTemplate for DateTimes -->
<DataTemplate DataType="{x:Type sys:DateTime}">
<DataTemplate.Resources>
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding Path=.}"/>
</DataTemplate>
</DataTemplate.Resources>
<DatePicker SelectedDate="{Binding Path=.}" HorizontalAlignment="Stretch"/>
</DataTemplate>
</UserControl.Resources>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</UserControl>
关于 2 的更多信息:
我想在视图中有一个标签和一个根据对象而变化的 属性。像这样:
<UserControl>
<UserControl.Resources>
<!-- ...DataTemplate here... -->
</UserControl.Resources>
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</StackPanel>
</UserControl>
但是如果我将DataTemplate 放在这个UserControl 资源上,它也会影响Label "allo"。所以我不得不创建另一个包含 DataTemplate 和 MyChangingProperty 的视图,这样标签 Allo 就不会受到影响。但是为一个 属性 创建的额外视图对我来说有点难看,我相信有更好的方法来隔离 DataTemplate,因此它只能应用于一个 UIControl。
<UserControl >
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyContainerViewModel}"/>
</StackPanel>
</UserControl>
注意:此处的 MyContainerViewModel 与描述的第一个视图相关联。
提前致谢!
一种可能的解决方案是使用 DataTemplateSelector
。您不能使用两种方式绑定来绑定原始类型,因为这必须通过 DataTemplate
以某种方式引用,我认为 WPF 不支持它。
DataTemplateSelector
现在根据 属性 类型选择权限 DataTemplate
,并按名称在资源中搜索权限 DataTemplate
。这也解决了您的 DataTemplates
与 Label
.
交互的问题
所以首先你需要定义一个 DataTemplateSelector
来根据 属性 的类型改变 DataTemplate
:
public class MyDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var fe = (FrameworkElement)container;
var prop = (item as MyViewModelType)?.MyChangingProperty;
if (prop is string)
return fe.FindResource("MyStringDT") as DataTemplate;
else if (prop is bool)
return fe.FindResource("MyBoolDT") as DataTemplate;
// More types...
return base.SelectTemplate(item, container);
}
}
然后你需要像这样改变UserControl
:
<UserControl>
<UserControl.Resources>
<local:MyDataTemplateSelector x:Key="MyDTSelector" />
<!-- DataTemplate for strings -->
<DataTemplate x:Key="MyStringDT">
<TextBox Text="{Binding MyChangingProperty, Mode=TwoWay}"
HorizontalAlignment="Stretch"/>
</DataTemplate>
<!-- DataTemplate for bool -->
<DataTemplate x:Key="MyBoolDT">
<CheckBox IsChecked="{Binding MyChangingProperty, Mode=TwoWay}" />
<!-- More DataTemplates... -->
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyContainerViewModel}"
ContentTemplateSelector="{StaticResource MyDTSelector}" />
</StackPanel>
</UserControl>
您可以找到更多关于 DataTemplateSelector
here 的信息。
您当然也可以在这个新的 DataTemplates
上设置 DataType
,但这不是必需的,因为 x:Key
无论如何都会使它们独一无二。但是如果你想要那么它必须看起来像这样:
<DataTemplate x:Key="MyStringDT" DataType="{x:Type local:MyViewModelType}">
在我看来,之前发布的答案有些矫枉过正。虽然 DateTemplateSelector
是一个有用的知识,但在这种情况下对我来说似乎没有必要。
But if I put the DataTemplate on this UserControl resources, it will also affect the Label "allo".
它影响 Label
对象的原因是 Label
对象是一个 ContentControl
,因此内容类型的模板匹配行为与您自己的 ContentPresenter
元素。并且您已将 Label
对象的内容设置为 string
值。但是你可以把任何东西作为它的内容。
修复不良效果的方法是通过将内容从 string
对象更改为显式 TextBlock
(模板中的控件 string
对象通常被分配)。例如:
<UserControl>
<UserControl.Resources>
<!-- ...DataTemplate here... -->
</UserControl.Resources>
<StackPanel>
<Label>
<TextBlock Text="Allo"/>
</Label>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</StackPanel>
</UserControl>
这样,您就绕过了模板查找行为(因为 TextBlock
不映射到任何模板,可以直接使用),Label
的内容将只是TextBlock
加上你想要的文字。
与创建全新视图或添加 DataTemplateSelector
.
相比,这似乎是解决问题的更简单方法
我查看了那些关于做 DataTemplate 的文章:
- WPF DataTemplate Binding
- WPF DataTemplate and Binding
- WPF DataTemplate Textblock binding
以及那些关于 DataTemplate 的内容取决于 属性 类型:
- WPF DataTemplate Binding depending on the type of a property
- Dynamically display a control depending on bound property using WPF
我正在尝试根据 属性 值显示具有不同控件的 属性。我有这个 Xaml 可以部分工作。我有 2 个问题:
属性 使用正确的控件显示,但是当我设置值时它不会返回到 属性。意味着我的 属性 的 "set" 没有被调用(但是在我创建 DataTemplate 之前)。我检测到关于设置 属性 的问题是关于 ="{Binding Path=.}" 但我找不到设置它的解决方案。
此外,为了能够使其工作,我必须 "isolate" 将值放入单个 ViewModel 中,以便 DataTemplate 不会影响所有其他控件。
你能帮我找到更好的解决方案来解决这两个问题吗?
这是与 MyContainerViewModel 链接的视图的 xaml 代码,它具有 "ChangingDataType" :
<UserControl >
<UserControl.Resources>
<!-- DataTemplate for strings -->
<DataTemplate DataType="{x:Type sys:String}">
<TextBox Text="{Binding Path=.}" HorizontalAlignment="Stretch"/>
</DataTemplate>
<!-- DataTemplate for bool -->
<DataTemplate DataType="{x:Type sys:Boolean}">
<CheckBox IsChecked="{Binding Path=.}" />
</DataTemplate>
<!-- DataTemplate for Int32 -->
<DataTemplate DataType="{x:Type sys:Int32}">
<dxe:TextEdit Text="{Binding Path=.}" MinWidth="50" Mask="d" MaskType="Numeric" HorizontalAlignment="Stretch"/>
<!--<Slider Maximum="100" Minimum="0" Value="{Binding Path=.}" Width="100" />-->
</DataTemplate>
<!-- DataTemplate for decimals -->
<DataTemplate DataType="{x:Type sys:Decimal}">
<!-- <TextBox Text="{Binding Path=.}" MinWidth="50" HorizontalAlignment="Stretch" />-->
<dxe:TextEdit Text="{Binding Path=.}" MinWidth="50" Mask="f" MaskType="Numeric" HorizontalAlignment="Stretch" />
</DataTemplate>
<!-- DataTemplate for DateTimes -->
<DataTemplate DataType="{x:Type sys:DateTime}">
<DataTemplate.Resources>
<DataTemplate DataType="{x:Type sys:String}">
<TextBlock Text="{Binding Path=.}"/>
</DataTemplate>
</DataTemplate.Resources>
<DatePicker SelectedDate="{Binding Path=.}" HorizontalAlignment="Stretch"/>
</DataTemplate>
</UserControl.Resources>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</UserControl>
关于 2 的更多信息:
我想在视图中有一个标签和一个根据对象而变化的 属性。像这样:
<UserControl>
<UserControl.Resources>
<!-- ...DataTemplate here... -->
</UserControl.Resources>
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</StackPanel>
</UserControl>
但是如果我将DataTemplate 放在这个UserControl 资源上,它也会影响Label "allo"。所以我不得不创建另一个包含 DataTemplate 和 MyChangingProperty 的视图,这样标签 Allo 就不会受到影响。但是为一个 属性 创建的额外视图对我来说有点难看,我相信有更好的方法来隔离 DataTemplate,因此它只能应用于一个 UIControl。
<UserControl >
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyContainerViewModel}"/>
</StackPanel>
</UserControl>
注意:此处的 MyContainerViewModel 与描述的第一个视图相关联。
提前致谢!
一种可能的解决方案是使用 DataTemplateSelector
。您不能使用两种方式绑定来绑定原始类型,因为这必须通过 DataTemplate
以某种方式引用,我认为 WPF 不支持它。
DataTemplateSelector
现在根据 属性 类型选择权限 DataTemplate
,并按名称在资源中搜索权限 DataTemplate
。这也解决了您的 DataTemplates
与 Label
.
所以首先你需要定义一个 DataTemplateSelector
来根据 属性 的类型改变 DataTemplate
:
public class MyDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var fe = (FrameworkElement)container;
var prop = (item as MyViewModelType)?.MyChangingProperty;
if (prop is string)
return fe.FindResource("MyStringDT") as DataTemplate;
else if (prop is bool)
return fe.FindResource("MyBoolDT") as DataTemplate;
// More types...
return base.SelectTemplate(item, container);
}
}
然后你需要像这样改变UserControl
:
<UserControl>
<UserControl.Resources>
<local:MyDataTemplateSelector x:Key="MyDTSelector" />
<!-- DataTemplate for strings -->
<DataTemplate x:Key="MyStringDT">
<TextBox Text="{Binding MyChangingProperty, Mode=TwoWay}"
HorizontalAlignment="Stretch"/>
</DataTemplate>
<!-- DataTemplate for bool -->
<DataTemplate x:Key="MyBoolDT">
<CheckBox IsChecked="{Binding MyChangingProperty, Mode=TwoWay}" />
<!-- More DataTemplates... -->
</DataTemplate>
</UserControl.Resources>
<StackPanel>
<Label Content="Allo"/>
<ContentPresenter Content="{Binding MyContainerViewModel}"
ContentTemplateSelector="{StaticResource MyDTSelector}" />
</StackPanel>
</UserControl>
您可以找到更多关于 DataTemplateSelector
here 的信息。
您当然也可以在这个新的 DataTemplates
上设置 DataType
,但这不是必需的,因为 x:Key
无论如何都会使它们独一无二。但是如果你想要那么它必须看起来像这样:
<DataTemplate x:Key="MyStringDT" DataType="{x:Type local:MyViewModelType}">
在我看来,之前发布的答案有些矫枉过正。虽然 DateTemplateSelector
是一个有用的知识,但在这种情况下对我来说似乎没有必要。
But if I put the DataTemplate on this UserControl resources, it will also affect the Label "allo".
它影响 Label
对象的原因是 Label
对象是一个 ContentControl
,因此内容类型的模板匹配行为与您自己的 ContentPresenter
元素。并且您已将 Label
对象的内容设置为 string
值。但是你可以把任何东西作为它的内容。
修复不良效果的方法是通过将内容从 string
对象更改为显式 TextBlock
(模板中的控件 string
对象通常被分配)。例如:
<UserControl>
<UserControl.Resources>
<!-- ...DataTemplate here... -->
</UserControl.Resources>
<StackPanel>
<Label>
<TextBlock Text="Allo"/>
</Label>
<ContentPresenter Content="{Binding MyChangingPropery}"/>
</StackPanel>
</UserControl>
这样,您就绕过了模板查找行为(因为 TextBlock
不映射到任何模板,可以直接使用),Label
的内容将只是TextBlock
加上你想要的文字。
与创建全新视图或添加 DataTemplateSelector
.