【问题标题】:DataTemplate Binding depending on property type and with working property BindingDataTemplate 绑定取决于属性类型和工作属性绑定
【发布时间】:2017-02-21 12:34:02
【问题描述】:

我查看了那些关于做 DataTemplate 的文章:

以及关于 DataTemplate 的内容取决于属性类型:

我正在尝试根据属性值显示具有不同控件的属性。我有这个部分工作的 Xaml。我有两个问题:

  1. 该属性正在使用正确的控件显示,但是当我设置该值时,它不会返回到该属性。表示未调用 My 属性的“集合”(但在创建 DataTemplate 之前)。我检测到设置属性的问题是关于 ="{Binding Path=.}" 但我找不到其他设置的解决方案。

  2. 此外,为了使其工作,我必须将值“隔离”到单个 ViewModel 中,以便 DataTemplate 不会影响所有其他控件。

你能帮我找到更好的解决方案来解决这两个问题吗?

这是我的视图的 xaml 代码,它与具有“ChangingDataType”的 MyContainerViewModel 链接:

<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 与描述的第一个视图相关联。

提前致谢!

【问题讨论】:

  • 你能解释一下第2个问题是什么吗? WPF 中资源的范围是设计使然。
  • 当然,我会为数字 2 添加更多信息。

标签: wpf xaml mvvm binding datatemplate


【解决方案1】:

在我看来,之前发布的答案是矫枉过正。虽然DateTemplateSelector 是一个有用的信息,但在这种情况下对我来说似乎没有必要。

但是如果我把DataTemplate放在这个UserControl资源上,也会影响到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 要简单得多。

【讨论】:

    【解决方案2】:

    一种可能的解决方案是使用DataTemplateSelector。您不能使用两种方式绑定原始类型,因为这必须以某种方式通过 DataTemplate 进行引用,我认为 WPF 不支持。

    DataTemplateSelector 现在会根据属性类型选择正确的 DataTemplate,并按名称在资源中搜索正确的 DataTemplate。这也解决了您的DataTemplatesLabel 交互的问题。

    所以首先你需要定义一个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}">
    

    【讨论】:

    • DataTemplateSelector 下的xaml DataTemplate 不起作用,出现错误“MyDataTemplateSelector 类型不支持直接内容”
    • 哎呀这是我的错误,工作正常。它回答了我的第 2 个问题,但是当我在控件上设置值时仍未设置 MyChangingProperty。我在内容演示者中添加了“, Mode=TwoWay}”,以防万一,但仍然不起作用。
    • @Rachel Mode=TwoWay ContentPresenter 不会改变任何东西。您尝试了哪个DataTemplateString 的那个只有在TextBox 失去焦点时才有效。 (提示:如果你想在按键上应用反向绑定,你必须在绑定中设置另一个UpdateSourceTrigger&lt;TextBox Text="{Binding MyChangingProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"。)请也尝试使用CheckBox 的布尔属性。这应该立即将绑定调用回源。如果没有,请验证您的 ViewModel 属性是否具有公共设置器。
    • 感谢您的快速响应。我尝试了字符串和复选框的丢失焦点,但它不起作用。我
    • 修改了行 '' 为 '
    猜你喜欢
    • 2011-06-27
    • 2013-01-01
    • 2018-09-30
    • 2015-09-09
    • 2013-03-23
    • 2012-04-11
    • 1970-01-01
    • 1970-01-01
    • 2013-03-22
    相关资源
    最近更新 更多