How to set the visiblility of a grid when the grid is inside a DataTemplate?

70 views Asked by At

In our UWP app the DataTemplate for MyListView is set in the code behind to either DataTemplateA or DataTemplateB in Page.Resources. Each data template contains a grid (TopGrid) which contains a DisplayGridButton and another grid (DisplayGrid).

DisplayGrid contains SecondListView and a HideGridButton

DisplayGridButton should show DisplayGrid. HideGridButton should collapse DisplayGrid.

The XAML is

<Page.Resources>
    <DataTemplate x:Key="DataTemplateA">
        <Grid Name="TopGrid">
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <StackPanel Orientation="Horizontal">
                <TextBox/>
                <Button Name="DisplayGridButton" Content="Show" Margin="10,0" Click="DisplayGridButton_Click"/>
            </StackPanel>
            <Grid Name="DisplayGrid" Grid.Row="1" Visibility="Collapsed">
                <StackPanel>
                    <Button Name="HideGridButton" Content="Hide" Click="HideGridButton_Click"/>
                    <ListView Name="SecondListView">
                        <ListView.ItemTemplate>
                            <DataTemplate >

                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </StackPanel>
            </Grid>
        </Grid>
    </DataTemplate>

    <DataTemplate x:Key="DataTemplateB">
        <Grid Name="TopGrid">
            <Grid.RowDefinitions>
                <RowDefinition Height="auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <StackPanel Orientation="Horizontal">
                <TextBox/>
                <Button Name="DisplayGridButton" Content="Show" Margin="10,0" Click="DisplayGridButton_Click"/>
            </StackPanel>
            <Grid Name="DisplayGrid" Grid.Row="1" Visibility="Collapsed">
                <StackPanel>
                    <Button Name="HideGridButton" Content="Hide" Click="HideGridButton_Click"/>
                    <ListView Name="SecondListView">
                        <ListView.ItemTemplate>
                            <DataTemplate >

                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </StackPanel>
            </Grid>
        </Grid>
    </DataTemplate>
</Page.Resources>

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <ListView Name="MyListView">

    </ListView>
</Grid>

DataTemplateA or DataTemplateB is set in the code behind.

if (condition)
{
    MyListView.ItemTemplate = (DataTemplate)Resources["DataTemplateA"];
}
    else
{
    MyListView.ItemTemplate = (DataTemplate)Resources["DataTemplateB"];
}

In the Code behind I can create the event handler but I cannot access the DisplayGrid to make it visible or to collapse it.

I would normally set visibility like this.

 private void DisplayGridButton_Click(object sender, RoutedEventArgs e)
    {
        DisplayGrid.Visibility = Visibility.Visible;
    }

 private void HideGridButton_Click(object sender, RoutedEventArgs e)
    {
        DisplayGrid.Visibility = Visibility.Collapsed;
    }

How do I access the DisplayGrid in the DataTemplate from the button click events?

1

There are 1 answers

0
Petter Hesselberg On BEST ANSWER

Since the grid is defined in a template, you'll have to dig it out at runtime. (If you could reference it as "DisplayGrid" from code-behind, you wouldn't know which listview item it belonged to anyway.)

Implement click handlers something like this:

private void DisplayGridButton_Click(object sender, RoutedEventArgs e)
{
    Button button = sender as Button;
    StackPanel stackPanel = button?.Parent as StackPanel;
    Grid grid = stackPanel?.Parent as Grid;
    if (grid != null)
    {
        Grid displayGrid = FindVisualChild<Grid>(grid, "DisplayGrid");
        if (displayGrid != null)
        {
            displayGrid.Visibility = Visibility.Visible;
        }
    }
}

private void HideGridButton_Click(object sender, RoutedEventArgs e)
{
    Button button = sender as Button;
    StackPanel stackPanel = button?.Parent as StackPanel;
    Grid grid = stackPanel?.Parent as Grid;
    if (grid != null)
    {
        grid.Visibility = Visibility.Collapsed;
    }
}

(Fair warning: The way this code finds the appropriate parent to start from is a bit brittle; it might break if the template changes. Searching by name would be better, but I don't have anything handy right now.)

Here is the helper method that finds a named child in the visual tree:

public static T FindVisualChild<T>(
    DependencyObject parent,
    string name = null)
    where T : DependencyObject
{
    if (parent != null)
    {
        int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
        for (int i = 0; i < childrenCount; i++)
        {
            DependencyObject child = VisualTreeHelper.GetChild(parent, i);
            T candidate = child as T;
            if (candidate != null)
            {
                if (name == null)
                {
                    return candidate;
                }

                FrameworkElement element = candidate as FrameworkElement;
                if (name == element?.Name)
                {
                    return candidate;
                }
            }

            T childOfChild = FindVisualChild<T>(child, name);
            if (childOfChild != null)
            {
                return childOfChild;
            }
        }
    }

    return default(T);
}

(This method can also search by type only; just pass null as the name.)