Home > Back-end >  How to have dynamically loaded instances of unbound named XAML elements behave independently from ea
How to have dynamically loaded instances of unbound named XAML elements behave independently from ea

Time:03-28

I have a WPF application where I dynamically load document view instances into a TabControl. The view has a ToolBar with some ToggleButtons which I use to control the visibility of certain elements in that view like so (only relevant elements shown):

<UserControl x:Class="MyProject.View.Views.DocumentView" ...>
    ...
    <ToolBar>
        <ToggleButton x:Name="togglePropInspector" ... />
        ...
    </ToolBar>
    ...
    <Border Visibility={Binding ElementName=togglePropInspector, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}">
        ...
    </Border>
</UserControl>

I found this kinda neat as everything is handled inside the view and didn't require adding code to the view model (or code behind). However, the problem is that checking the toggle button on one tab now checks it in all instances of the view, not just the current tab. This basically applies to all elements whose state is not bound to the view model in any way. Is there a way around this without having to add code to the view model?

For completeness' sake here's the relevant part of how I'm loading the views:

<TabControl ItemsSource="{Binding Documents}">
    <TabControl.Resources>
        <DataTemplate DataType="{x:Type viewModels:DocumentViewModel}">
            <local:DocumentView />
        </DataTemplate>
    </TabControl.Resources>
</TabControl>

CodePudding user response:

TabControl has a single content host that is used for all TabItem instances. When the data models assigned to the TabItem.Content property are of the same data type, then the TabControl will reuse the same DataTemplate, which means same element instances, only updated with the changed data from data bindings.

To change the state of the reused controls, you must either access the control explicitly, or force the TabControl to reapply the ContentTemplate by temporarily changing the data type of the Content:

<TabControl SelectionChanged="TabControl_SelectionChanged" />
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
  var tabControl = sender as TabControl;
  var tabItemContainer = tabControl.ItemContainerGenerator.ContainerFromItem(tabControl.SelectedItem) as TabItem;
  object currentContent = tabItemContainer.Content;
  tabItemContainer.Content = null;

  // Wait to leave the context to allow the TabControl to handle the new data type (null).
  // The content switch shouldn't be noticable in the GUI.
  Dispatcher.InvokeAsync(() => tabItemContainer.Content = currentContent);
}

You can also use a dedicated data type for each tab. This way the TabControl is automatically forced to switch the DataTemplate.

The cleanest solution would be to bind the ToggleButton to the data model.

  • Related