在 DataGridTemplateColumn 中使用 TemplateSelector 时绑定不再工作
Binding not working anymore when using TemplateSelector in DataGridTemplateColumn
我有以下(有效)XAML
定义:
<DataGridTemplateColumn Header="Station & 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 & 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 & Programm" Width="*" CellTemplateSelector="{StaticResource StationProgramTemplateSelector}" />
一个ContentControl
的ContentTemplate
中根元素的DataContext
是同一个ContentControl
的Content
。
这是我的个人解决方案,如何让它发挥作用。
我已经更改了 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 & Programm" Width="*" CellTemplateSelector="{StaticResource StationProgramTemplateSelector}"/>
Where, why and when do you need to Mode property?
我已经将 recipe
object 绑定到 ZlsRouteEditor
控件到 view 或 edit recipe
。
为了避免多重控制或Styles
(ZlsRouteEditorView
、ZlsRouteEditorEdit
...),我做了一个enum
ERouteEditor
作为DependencyProperty
在 ZlsRouteEditor
中更改我的 Template
.
中特定区域的 DataTemplates
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<recipeControls:ZlsRouteEditor Height="570" Margin="5" Mode="View"/>
<recipeControls:ZlsRouteEditor Height="570" Margin="5" Mode="Edit"/>
</StackPanel>
我有以下(有效)XAML
定义:
<DataGridTemplateColumn Header="Station & 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 & 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 & Programm" Width="*" CellTemplateSelector="{StaticResource StationProgramTemplateSelector}" />
一个ContentControl
的ContentTemplate
中根元素的DataContext
是同一个ContentControl
的Content
。
这是我的个人解决方案,如何让它发挥作用。
我已经更改了 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 & Programm" Width="*" CellTemplateSelector="{StaticResource StationProgramTemplateSelector}"/>
Where, why and when do you need to Mode property?
我已经将 recipe
object 绑定到 ZlsRouteEditor
控件到 view 或 edit recipe
。
为了避免多重控制或Styles
(ZlsRouteEditorView
、ZlsRouteEditorEdit
...),我做了一个enum
ERouteEditor
作为DependencyProperty
在 ZlsRouteEditor
中更改我的 Template
.
DataTemplates
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<recipeControls:ZlsRouteEditor Height="570" Margin="5" Mode="View"/>
<recipeControls:ZlsRouteEditor Height="570" Margin="5" Mode="Edit"/>
</StackPanel>