使用WPF根据绑定属性动态显示控件

时间:2010-05-26 09:27:07

标签: .net wpf

我有一个属性是数据库数据类型(char,datetime,int,float等...),我想更改用于输入所选类型值的控件。因此,对于文本值,我想要TextBox,对于日期值,我想要DatePicker

我想到的一种方法是在表单上使用每个控件之一,并使用适当的Visibility实现设置IValueConverter。我知道这会起作用,但它会产生很多代码并且感觉不太好。

我想的另一种方式是使用ContentPresenter并使用StyleDataTriggers设置其内容,但我无法让它工作。

    <Style x:Key="TypedValueHelper" TargetType="{x:Type ContentPresenter}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=DataType}" Value="Char">
                <Setter Property="Content" Value="???"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=DataType}" Value="Date">
                <Setter Property="Content" Value="???"/>
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=DataType}" Value="Integer">
                <Setter Property="Content" Value="???"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>

如果有人可以填写我的“???”或者提供更好的解决方案。

3 个答案:

答案 0 :(得分:11)

您可以将样式与setter和datatemplates结合使用。你基本上在代码中有了它的开头,虽然我不认为ContentPresenter是正确的样式控件,因为它没有模板。

可能是这样的:

<Style x:Key="TypedValueHelper" TargetType="{x:Type ContentControl}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding Path=DataType}" Value="Char">
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <TextBox Text="{Binding Path=.}" />
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
            <DataTrigger Binding="{Binding Path=DataType}" Value="Integer">
                <Setter Property="ContentTemplate">
                    <Setter.Value>
                        <DataTemplate>
                            <Slider Maximum="100" Minimum="0" Value="{Binding Path=.}"
                                             Orientation="Horizontal" />
                        </DataTemplate>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </Style.Triggers>
    </Style>

...

<ContentControl Content="{Binding MyValue}"
                        Style="{StaticResource TypedValueHelper}">

答案 1 :(得分:7)

虽然Style解决方案可能有效,但实现动态内容行为的正确方法是使用DataTemplates,如Sdry建议的那样。但是,您将使用枚举来确定要使用的DataTemplate;这实际上意味着您希望将单个类型映射到多个DataTemplates。 DataTemplateSelector类解决了这个问题,以下描述直接来自MSDN:


“通常,如果对同一类型的对象有多个DataTemplate,并且想要提供自己的逻辑以根据每个数据对象的属性选择要应用的DataTemplate,则可以创建DataTemplateSelector。”


您的动态内容应由ContentControl托管,如下所示:

   <ContentControl Content="{Binding Path=ReferenceToYourViewModel}" ContentTemplateSelector="{DynamicResource MyTemplateSelector}"/>

MyTemplateSelector的实现:

    public class MyTemplateSelector: DataTemplateSelector
{
    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        FrameworkElement elem = container as FrameworkElement;
        if(elem == null)
        {
            return null;
        }
        if (item == null || !(item is YourViewModel))
        {
            throw new ApplicationException();
        }
        if ((item as YourViewModel).DataType == DataType.Char)
        {
            return elem.FindResource("CharDataTemplate") as DataTemplate;
        }
        if ((item as YourViewModel).DataType == DataType.Date)
        {
            return elem.FindResource("DateDataTemplate") as DataTemplate;
        }
        if ((item as YourViewModel).DataType == DataType.Integer)
        {
            return elem.FindResource("IntegerDataTemplate") as DataTemplate;
        }
        throw new ApplicationException();
    }
}

然后如您所料,这里是可供选择的DataTemplates:

   <DataTemplate x:Key="CharDataTemplate" DataType="{x:Type YourViewModel}">Put Your Xaml Here</DataTemplate>
   <DataTemplate x:Key="DateDataTemplate" DataType="{x:Type YourViewModel}">Put Your Xaml Here</DataTemplate>
   <DataTemplate x:Key="IntegerDataTemplate" DataType="{x:Type YourViewModel}">Put Your Xaml Here</DataTemplate>

这样,将根据View Model的DataType属性选择适当的DataTemplate。在我看来,这比使用Visibility或Styles要清晰得多。

答案 2 :(得分:0)

我会研究DataTemplates。例如:

<DataTemplate DataType="{x:Type local:Input}">
            <local:InputControl DataContext="{Binding}" />
 </DataTemplate>

 <DataTemplate DataType="{x:Type data:VideoData}">
                <local:VideoControl DataContext="{Binding}"></local:VideoControl>
 </DataTemplate>