DataTemplate绑定取决于属性类型和工作属性绑定

时间:2016-10-12 15:59:35

标签: wpf xaml mvvm binding datatemplate

我查看有关执行DataTemplate的文章:

并根据属性类型关于DataTemplate:

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

  1. 该属性显示正确的控件,但是当我设置该值时,它不会返回该属性。意味着"设置"我的属性不是调用(但在我创建DataTemplate之前)。我发现设置属性的问题是关于=" {Binding Path =。}"但我无法找到解决方案来设置它。

  2. 此外,为了能够使其发挥作用,我必须"隔离"将Value转换为单个ViewModel,以便DataTemplate不会影响所有其他控件。

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

    以下是与MyContainerViewModel链接的我的View的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&#34; allo&#34;。所以我不得不创建另一个包含DataTemplate和MyChangingProperty的视图,这样标签Allo就不会受到影响。但是为一个属性创建的额外View对我来说有点难看,我确信有更好的方法可以隔离DataTemplate,因此它只能应用于一个UIControl。

    <UserControl >
        <StackPanel>
            <Label Content="Allo"/>
            <ContentPresenter Content="{Binding MyContainerViewModel}"/>
        </StackPanel>
    </UserControl>
    

    注意:此处的MyContainerViewModel与描述的第一个视图相关联。

    提前致谢!

2 个答案:

答案 0 :(得分:1)

一种可能的解决方案是使用DataTemplateSelector。你不能使用双向绑定来绑定基本类型,因为这必须通过我认为WPF不支持的DataTemplate以某种方式引用。

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的更多信息。

您当然也可以在这个新DataType上设置DataTemplates,但这不是必需的,因为x:Key无论如何都会使它们成为唯一。但如果你想,那么它必须是这样的:

<DataTemplate x:Key="MyStringDT" DataType="{x:Type local:MyViewModelType}">

答案 1 :(得分:0)

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

  

但是如果我将DataTemplate放在这个UserControl资源上,它也会影响Label&#34; allo&#34;。

它影响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