I have the following (working) XAML
definition:
<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>
I want to outsource this DataTemplate
and use the following 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);
}
}
Afterwards I have changed my code as follow:
<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>
The DataTemplate
s are selected correctly, but the data binding does not work anymore.
Here is my personal solution how to get it working.
I have changed the ZlsRouteEditorDataTemplateSelector
class with the ability to look for my specific control (ZlsRouteEditor
) and get the Mode
value.
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);
}
}
Then call it directly (like mm8 said)
<DataGridTemplateColumn Header="Station & Programm" Width="*" CellTemplateSelector="{StaticResource StationProgramTemplateSelector}"/>
Where, why and when do you need to Mode property?
I have bound a recipe
object to the ZlsRouteEditor
control to view or edit the recipe
.
To avoid multiple controls or Styles
(ZlsRouteEditorView
, ZlsRouteEditorEdit
... ), I made an enum
ERouteEditor
as DependencyProperty
in the ZlsRouteEditor
to change the DataTemplates
of specific areas inside of my Template
.
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<recipeControls:ZlsRouteEditor Height="570" Margin="5" Mode="View"/>
<recipeControls:ZlsRouteEditor Height="570" Margin="5" Mode="Edit"/>
</StackPanel>