如何使ItemsControl对同一列中的不同行使用不同的编辑器(取决于数据类型)?

时间:2015-04-07 17:37:49

标签: wpf templates binding datatemplate itemscontrol

我需要允许用户指定应用于搜索的过滤器。我希望UI看起来像这样:

different editors in the same column
(在上图中我手动输入“Test”并在ComboBox中选择“Both”,实际绑定不起作用)

因此,用户可以选择要应用的过滤器并使用相应的编辑器指定值(TextBox用于字符串,ComboBox用于枚举等)。

为了创建这个,我使用DataGrid与TemplateColumn,DataTriggers和DataTemplates(它不能完全按照我的需要工作,这就是我写这个问题的原因):

<DataGridTemplateColumn Header="Value">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ContentControl>
                <ContentControl.Style>
                    <Style TargetType="ContentControl">
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding SearchFilter.Type}" Value="string">
                                <Setter Property="ContentTemplate">
                                    <Setter.Value>
                                        <DataTemplate>
                                            <TextBox Text="{Binding SearchFilter.Value}"/>
                                        </DataTemplate>
                                    </Setter.Value>
                                </Setter>
                            </DataTrigger>
                            <DataTrigger Binding="{Binding SearchFilter.Type}" Value="enum">
                                <Setter Property="ContentTemplate">
                                    <Setter.Value>
                                        <DataTemplate>
                                            <ComboBox ItemsSource="{wpf:EnumMembers dataModel:MyEnumType}" SelectedItem="{Binding SearchFilter.Value}" />
                                        </DataTemplate>
                                    </Setter.Value>
                                </Setter>
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </ContentControl.Style>
            </ContentControl>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

DataGrid绑定到包含这些过滤器对象列表的ViewModel:

public class PositionSearchFilter
{
    public string DisplayName { get; set; }
    public object Value { get; set; }
    public string Type { get; set; }
    ...
}

这种方法的问题是数据绑定在DataTemplate中不起作用(至少在我的代码中,也许我做错了),我的意思是这一部分:

<TextBox Text="{Binding SearchFilter.Value}"/>

当然,我可以手动创建一堆控件(即不使用ItemsControl),但我想要一个通用的解决方案,所以我可以简单地获取一个过滤器对象列表并获得一个完全正常工作的UI。

请帮我解决我的任务。

3 个答案:

答案 0 :(得分:1)

您可以创建自己的DataTemplateSelector,以决定哪个DataTemplate用于哪种特定数据类型。

以下是DataTemplateSelector

的示例
class ValueCellTemplateSelector : DataTemplateSelector
{
    public DataTemplate StringTemplate { get; set; }
    public DataTemplate EnumTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item is PositionSearchFilter)
        {
            PositionSearchFilter element = (PositionSearchFilter)item;
            if (element.Value is string)
            {
                return this.StringTemplate;
            }
            else if (element.Value is MyEnumType)
            {
                return this.EnumTemplate;
            }
        }

        return null;
    }
}

您可以在资源中实例化此选择器,同时提供相应的DataTemplate

<DataGrid.Resources>
    <local:ValueCellTemplateSelector x:Key="ValueCellTemplateSelector">
        <local:ValueCellTemplateSelector.StringTemplate>
            <DataTemplate>
                <TextBox Text="{Binding Value}"/>
            </DataTemplate>
        </local:ValueCellTemplateSelector.StringTemplate>
        <local:ValueCellTemplateSelector.EnumTemplate>
            <DataTemplate>
                <ComboBox ItemsSource="{wpf:EnumMembers dataModel:MyEnumType}" SelectedItem="{Binding Value}"/>
            </DataTemplate>
        </local:ValueCellTemplateSelector.EnumTemplate>
    </local:ValueCellTemplateSelector>
 </DataGrid.Resources>

最后,只需在列中设置此选择器:

<DataGridTemplateColumn CellTemplateSelector="{StaticResource ValueCellTemplateSelector}"/>

请注意,在我的示例中,将根据DataTemplate属性中包含的实际对象类型选择Value。因此,我们在过滤器类中不需要额外的Type属性。如果它对您很重要,只需相应地更改选择器。

答案 1 :(得分:0)

如果您在DataTemplate中,您需要稍微挖掘一下。尝试:

<TextBox Text="{Binding SearchFilter.Value, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid}}}" />

这应该强制绑定工作,直到它到达某种类型的DataGrid,然后使用该控件的DataContext作为绑定的上下文。

答案 2 :(得分:0)

DataGridTemplateColumn不是DataGrid的逻辑或可视树的一部分。要解决此问题并在其中设置DataContext以进行绑定,您可以使用绑定代理,如下所述:http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/

public class BindingProxy : Freezable
{
    #region Overrides of Freezable

    protected override Freezable CreateInstanceCore()
    {
        return new BindingProxy();
    }

    #endregion

    public object Data
    {
        get { return (object)GetValue(DataProperty); }
        set { SetValue(DataProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}

在您的控制/窗口资源中,您可以定义代理的实例:

<UserControl.Resources>
    <local:BindingProxy x:Key="proxy" Data="{Binding}" />
</UserControl.Resources>

从DataGridTemplateColumn中,您可以引用绑定的代理:

<TextBox Text="{Binding SearchFilter.Value, Source={StaticResource proxy}}"/>