在 DataGridTemplateColumn 中使用 TemplateSelector 时绑定不再工作

Binding not working anymore when using TemplateSelector in DataGridTemplateColumn

我有以下(有效)XAML定义:

<DataGridTemplateColumn Header="Station &amp; Programm" Width="*">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>                                                
            <ListBox 
                ItemsSource="{Binding Targets}" 
                Style="{StaticResource ListBoxTransparentStyle}"    
                VerticalAlignment="Center"
                IsHitTestVisible="False"> <!--Disable Selection-->
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition SharedSizeGroup="A"/>
                                <ColumnDefinition Width="5"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <TextBlock
                                Grid.Column="0"
                                Text="{Binding Station}"
                                Height="Auto"/>
                            <TextBlock
                                Grid.Column="2"
                                Text="{Binding Program}"
                                Height="Auto"/>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

我想将此 DataTemplate 外包并使用以下 DataTemplateSelector:

public class ZlsRouteEditorDataTemplateSelector : DataTemplateSelector
{
    public DataTemplate ViewDataTemplate { get;set; }
    public DataTemplate EditDataTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item is ERouteEditor e)
        {
            switch (e)
            {
                case ERouteEditor.View:
                    return ViewDataTemplate;
                case ERouteEditor.Edit:
                    return EditDataTemplate;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
        return base.SelectTemplate(item, container);
    }
}

之后我更改了我的代码如下:

<ControlTemplate>
    <ControlTemplate.Resources>
        <DataTemplate x:Key="StationProgramView" x:Shared="True">                                                
            <ListBox 
                ItemsSource="{Binding Targets}" 
                Style="{StaticResource ListBoxTransparentStyle}"    
                VerticalAlignment="Center"
                IsHitTestVisible="False"> <!--Disable Selection-->
                <ListBox.ItemTemplate>
                    <DataTemplate>
                        <Grid >
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition SharedSizeGroup="A"/>
                                <ColumnDefinition Width="5"/>
                                <ColumnDefinition Width="*"/>
                            </Grid.ColumnDefinitions>
                            <controls:HtTextfeld
                                Grid.Column="0"
                                Text="{Binding Station}"
                                DisableTranslation="True"
                                Height="Auto"/>
                            <controls:HtTextfeld
                                Grid.Column="2"
                                Text="{Binding Program}"
                                DisableTranslation="True"
                                Height="Auto"/>
                        </Grid>
                    </DataTemplate>
                </ListBox.ItemTemplate>
            </ListBox>
        </DataTemplate>
        <DataTemplate x:Key="StationProgramEdit" x:Shared="True">                                                
            <TextBlock Text="Hello World"></TextBlock>
        </DataTemplate>
        <recipeControls:ZlsRouteEditorDataTemplateSelector x:Key="StationProgramTemplateSelector" ViewDataTemplate="{StaticResource StationProgramView}" EditDataTemplate="{StaticResource StationProgramEdit}"/>

<DataGridTemplateColumn Header="Station &amp; Programm" Width="*">
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <ContentControl
                ContentTemplateSelector="{StaticResource StationProgramTemplateSelector}"
                Content="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=recipeControls:ZlsRouteEditor}, Path=Mode}"/>
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

DataTemplate选择正确,但数据绑定不再起作用。

尝试设置列的 CellTemplateSelector 属性 而不是将 CellTemplate 设置为 ContentControl:

<DataGridTemplateColumn Header="Station &amp; Programm" Width="*" CellTemplateSelector="{StaticResource StationProgramTemplateSelector}" />

一个ContentControlContentTemplate中根元素的DataContext是同一个ContentControlContent

这是我的个人解决方案,如何让它发挥作用。

我已经更改了 ZlsRouteEditorDataTemplateSelector class,可以查找我的特定控件 (ZlsRouteEditor) 并获得 Mode 值。

ZlsRouteEditorDataTemplateSelector

public class ZlsRouteEditorDataTemplateSelector : DataTemplateSelector
{
    public DataTemplate ViewDataTemplate { get;set; }
    public DataTemplate EditDataTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, DependencyObject container)
    {
        if (item is ERouteEditor e)
            return _GetTemplate(e);

        ZlsRouteEditor parent = container.TryFindParent<ZlsRouteEditor>();
        if (parent != null)
            return _GetTemplate(parent.Mode);

        return base.SelectTemplate(item, container);
    }

    private DataTemplate _GetTemplate(ERouteEditor e)
    {
        switch (e)
        {
            case ERouteEditor.View:
                return ViewDataTemplate;
            case ERouteEditor.Edit:
                return EditDataTemplate;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }
}

尝试查找父母

/// <summary>
/// Finds a parent of a given item on the visual tree.
/// </summary>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="child">A direct or indirect child of the
/// queried item.</param>
/// <returns>The first parent item that matches the submitted
/// type parameter. If not matching item can be found, a null
/// reference is being returned.</returns>
public static T TryFindParent<T>(this DependencyObject child) where T : DependencyObject
{
    //get parent item
    DependencyObject parentObject = GetParentObject(child);

    //we've reached the end of the tree
    if (parentObject == null) return null;

    //check if the parent matches the type we're looking for
    T parent = parentObject as T;
    if (parent != null)
    {
        return parent;
    }
    else
    {
        //use recursion to proceed with next level
        return TryFindParent<T>(parentObject);
    }
}

然后直接调用(如mm8所说)

<DataGridTemplateColumn Header="Station &amp; Programm" Width="*" CellTemplateSelector="{StaticResource StationProgramTemplateSelector}"/>

Where, why and when do you need to Mode property?

我已经将 recipe object 绑定到 ZlsRouteEditor 控件到 viewedit recipe

为了避免多重控制或StylesZlsRouteEditorViewZlsRouteEditorEdit ...),我做了一个enum ERouteEditor作为DependencyPropertyZlsRouteEditor 中更改我的 Template.

中特定区域的 DataTemplates

<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
    <recipeControls:ZlsRouteEditor Height="570" Margin="5" Mode="View"/>
    <recipeControls:ZlsRouteEditor Height="570" Margin="5" Mode="Edit"/>
</StackPanel>