locked
Switching DataTemplate on DataTrigger - How to bind in DataTemplate RRS feed

  • Question

  • Hello,

    I have a ItemsControl sourcing to a list (of persons)
    When a persons' name is 'Nothing', I would like to call a different datatemplate.
    I'm using a datatrigger for the purpose and it works great!

    Now I'm trying to bind to the 'persons' data from within the alternate datatemplate (called "Empty Row") and don't know how to do!!

    Could you please help!?

    <Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
        <Grid HorizontalAlignment="Right" Background="#FF203338">
            <Grid.Resources>
                <DataTemplate x:Key="EmptyRow">
                    <Grid Height="32" Background="#FF3A5A62">
                        <Label Content="{Binding Descript}" Foreground="White" FontSize="8" Padding="0"/>
                    </Grid>
                </DataTemplate>
            </Grid.Resources>
            
            <Grid Width="266" Height="600">
                <ItemsControl x:Name="CommandPanelIControl" HorizontalAlignment="Left" VerticalAlignment="Top" >
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <ContentControl>
                                <ContentControl.Style>
                                    <Style TargetType="{x:Type ContentControl}">
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding Name}" Value="{x:Null}">
                                                <Setter Property="ContentTemplate" Value="{StaticResource EmptyRow}" />
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </ContentControl.Style>
                                <Grid Height="32" Margin="0,0,0,3">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="150"/>
                                        <ColumnDefinition Width="100"/>
                                    </Grid.ColumnDefinitions>
                                    <Label Grid.Column="0" Content="{Binding Descript}" FontSize="10" FontFamily="Segoe UI" Foreground="White"/>
                                    <Label Grid.Column="1" Content="{Binding Name}" FontSize="10" FontFamily="Segoe UI" Foreground="White"/>
                                </Grid>
                            </ContentControl>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </Grid>
        </Grid>
    </Window>
    

    Class MainWindow 
        Private Sub MainWindow_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) Handles Me.Loaded
            populateCmdPanel()
        End Sub
        Private Sub populateCmdPanel()
            Dim items As New List(Of persons)()
    
            items.Add(New persons With {.Name = Nothing, .Descript = "CAT 1"})
            items.Add(New persons With {.Name = "Alan", .Descript = "Description Alan"})
            items.Add(New persons With {.Name = "Marc", .Descript = "Description Marc"})
            items.Add(New persons With {.Name = Nothing, .Descript = "CAT 2"})
            items.Add(New persons With {.Name = "John", .Descript = "Description John"})
            '
            CommandPanelIControl.ItemsSource = items
        End Sub
    End Class
    
    Public Class persons
        Private m_name As String
        Private m_descript As String
        Public Property Name() As String
            Get
                Return m_name
            End Get
            Set(value As String)
                m_name = value
            End Set
        End Property
        Public Property Descript() As String
            Get
                Return m_descript
            End Get
            Set(value As String)
                m_descript = value
            End Set
        End Property
    End Class
    



    Friday, April 8, 2016 11:41 AM

Answers

  • A ContentControl is supposed to have a Content. This Content object will be the DataContext of the ContentTemplate. So you should always bind the Content property to the persons objects and then use the DataTrigger to set the ContentTemplate property to a DataTemplate:

                <ItemsControl x:Name="CommandPanelIControl" HorizontalAlignment="Left" VerticalAlignment="Top" >
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <ContentControl Content="{Binding}">
                                <ContentControl.Style>
                                    <Style TargetType="ContentControl">
                                        <Setter Property="ContentTemplate">
                                            <Setter.Value>
                                                <DataTemplate>
                                                    <Grid Height="32" Margin="0,0,0,3">
                                                        <Grid.ColumnDefinitions>
                                                            <ColumnDefinition Width="150"/>
                                                            <ColumnDefinition Width="100"/>
                                                        </Grid.ColumnDefinitions>
                                                        <Label Grid.Column="0" Content="{Binding Descript}" FontSize="10" FontFamily="Segoe UI" Foreground="White"/>
                                                        <Label Grid.Column="1" Content="{Binding Name}" FontSize="10" FontFamily="Segoe UI" Foreground="White"/>
                                                    </Grid>
                                                </DataTemplate>
                                            </Setter.Value>
                                        </Setter>
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding Name}" Value="{x:Null}">
                                                <Setter Property="ContentTemplate" Value="{StaticResource EmptyRow}" />
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </ContentControl.Style>
                            </ContentControl>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>

    Then there is no data written twice. You could also switch templates programmatically using a custom DataTemplateSelector: https://msdn.microsoft.com/en-us/library/system.windows.controls.datatemplateselector%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    • Marked as answer by jmdeb Monday, April 11, 2016 5:51 PM
    Saturday, April 9, 2016 7:05 PM

All replies

  • You should look into mvvm and binding.

    Plus, every time you ask a question people are very likely to post replies with c#. Very few devs use VB nowadays.

    Another thing you need to watch out for is devs who don't work in vb trying to write VB. ( It's not always very good vb. )

    Anyhow, I think you're looking at this the wrong way.  Putting a contentcontrol in your datatemplate is a complication.

    Assuming you really don't want to use a datatemplate selector I would go with something like:

            Title="MainWindow" Height="350" Width="525">
        <Window.DataContext>
            <local:MainWindowViewModel/>
        </Window.DataContext>
        <Grid Width="266" Height="600"
               Background="#FF203338">
    
            <Grid.Resources>
                <Style TargetType="Label">
                    <Setter Property="Foreground" Value="White"/>
                </Style>
            </Grid.Resources>
            <ItemsControl 
                    ItemsSource="{Binding People}" >
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <Grid Height="32">
                            <Grid  Background="#FF3A5A62">
                                 <Label Content="{Binding Descript}" Foreground="White" FontSize="8" />
                             </Grid>
                            <Grid  Background="#FF203338">
                                 <Grid.Style>
                                     <Style TargetType="Grid">
                                         <Style.Triggers>
                                             <DataTrigger Binding="{Binding Name}" Value="{x:Null}">
                                                 <Setter Property="Visibility" Value="Collapsed" />
                                             </DataTrigger>
                                         </Style.Triggers>
                                     </Style>
                                  </Grid.Style>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="150"/>
                                    <ColumnDefinition Width="100"/>
                                </Grid.ColumnDefinitions>
                                <Label Grid.Column="0" Content="{Binding Descript}" FontSize="10" FontFamily="Segoe UI" Foreground="White" />
                                <Label Grid.Column="1" Content="{Binding Name}" FontSize="10" FontFamily="Segoe UI" Foreground="White"  />
                                </Grid>
                            </Grid>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
        </Grid>
    </Window>

    I'm binding there and I've called my list<Person> People.

    My viewmodel:

        public class MainWindowViewModel
        {
            public List<Person> People { get; set; } = new List<Person>
            {
                new Person {Descript ="This is null Name" },
                new Person {Name="Alan", Descript ="des 1" },
                new Person {Name="John", Descript="des 2" },
                new Person {  Descript="Another null Name" }
            };
        }

    Which looks like:


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Friday, April 8, 2016 2:55 PM
  • Thank you very much for this clear and complete answer!!

    Isn't it cumbersome to have the data written twice and superimposed?

    Adding the datacontext to the label will also work (keeping the original approach)...what do you think of it?

    <Grid.Resources>
        <DataTemplate x:Key="EmptyRow">
            <Grid Height="32" Background="#FF3A5A62">
                <Label DataContext="{Binding DataContext,RelativeSource={RelativeSource 
    AncestorType={x:Type ContentControl}}}" Content="{Binding Descript}" Foreground="White" FontSize="8" Padding="0"/>
            </Grid>
        </DataTemplate>
    </Grid.Resources>
    Friday, April 8, 2016 6:14 PM
  • Isn't it cumbersome to have the data written twice and superimposed?

    Not particularly.

    It's easier to understand what's going on imo.

    .

    I don't really like switching the template out as a mechanism to just highlight a row.

    Making the Person with no name a different type would be preferable if you want drastic changes which meant you absolutely needed to switch templates.

    If the idea is just to highlight someone with a null name then my preference would be just to change the background colour.

    If you did like:

        <Grid Width="266" Height="600"
               Background="#FF203338">
            <Grid.Resources>
                <Style TargetType="Label">
                    <Setter Property="Foreground" Value="White"/>
                </Style>
            </Grid.Resources>
            <ItemsControl 
                     ItemsSource="{Binding People}" >
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <ContentControl>
                            <Grid Height="32" Margin="0,0,0,3">
                                <Grid.Style>
                                    <Style TargetType="{x:Type Grid}">
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding Name}" Value="{x:Null}">
                                                <Setter Property="Background" Value="#FF3A5A62"/>
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </Grid.Style>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="150"/>
                                    <ColumnDefinition Width="100"/>
                                </Grid.ColumnDefinitions>
                                <Label Grid.Column="0" Content="{Binding Descript}" />
                                <Label Grid.Column="1" Content="{Binding Name}"  />
                            </Grid>
                        </ContentControl>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
    Then the odd null name row is going to stand out.


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    Friday, April 8, 2016 7:21 PM
  • A ContentControl is supposed to have a Content. This Content object will be the DataContext of the ContentTemplate. So you should always bind the Content property to the persons objects and then use the DataTrigger to set the ContentTemplate property to a DataTemplate:

                <ItemsControl x:Name="CommandPanelIControl" HorizontalAlignment="Left" VerticalAlignment="Top" >
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <ContentControl Content="{Binding}">
                                <ContentControl.Style>
                                    <Style TargetType="ContentControl">
                                        <Setter Property="ContentTemplate">
                                            <Setter.Value>
                                                <DataTemplate>
                                                    <Grid Height="32" Margin="0,0,0,3">
                                                        <Grid.ColumnDefinitions>
                                                            <ColumnDefinition Width="150"/>
                                                            <ColumnDefinition Width="100"/>
                                                        </Grid.ColumnDefinitions>
                                                        <Label Grid.Column="0" Content="{Binding Descript}" FontSize="10" FontFamily="Segoe UI" Foreground="White"/>
                                                        <Label Grid.Column="1" Content="{Binding Name}" FontSize="10" FontFamily="Segoe UI" Foreground="White"/>
                                                    </Grid>
                                                </DataTemplate>
                                            </Setter.Value>
                                        </Setter>
                                        <Style.Triggers>
                                            <DataTrigger Binding="{Binding Name}" Value="{x:Null}">
                                                <Setter Property="ContentTemplate" Value="{StaticResource EmptyRow}" />
                                            </DataTrigger>
                                        </Style.Triggers>
                                    </Style>
                                </ContentControl.Style>
                            </ContentControl>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>

    Then there is no data written twice. You could also switch templates programmatically using a custom DataTemplateSelector: https://msdn.microsoft.com/en-us/library/system.windows.controls.datatemplateselector%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    • Marked as answer by jmdeb Monday, April 11, 2016 5:51 PM
    Saturday, April 9, 2016 7:05 PM