Home > OS >  Reading the "IsExpanded" property from the ItemSource of a TreeViewItem
Reading the "IsExpanded" property from the ItemSource of a TreeViewItem

Time:04-07

So I want the IsExpanded Property of the TreeView Item so be reflected in the datacontext.

        <TreeView x:Name="TreeViewMonitorEvents" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Grid.Column="0" Grid.Row="1" Grid.RowSpan="5"
              ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Page}, Path=MonitorEventCatagories}">
        <TreeView.Resources>
            <HierarchicalDataTemplate DataType="{x:Type tree:TreeGroup}" ItemsSource="{Binding Members}" >
                <StackPanel Orientation="Horizontal" VerticalAlignment="Center" MouseMove="DragDrop_MouseMove_TreeGroup">
                    <CheckBox Name="CheckboxTreeGroup" IsChecked="{Binding Path=(tree:TreeItemHelper.IsChecked), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
                          Template="{DynamicResource MonitoringUICheckBox}" Style="{StaticResource MonitoringUICheckBoxStyling}"
                              MouseMove="DragDrop_MouseMove_TreeGroup" Checked="CheckboxTreeGroup_Checked" Unchecked="CheckboxTreeGroup_Unchecked">
                    </CheckBox>
                    <TextBlock Text="{Binding Name}" Style="{StaticResource MonitorUIText}" MouseMove="DragDrop_MouseMove_TreeGroup"/>
                </StackPanel>
                <HierarchicalDataTemplate.ItemContainerStyle>
                    <Style TargetType="{x:Type TreeViewItem}"  >
                        <Setter Property="IsExpanded" Value="{Binding IsExpanded,Mode=TwoWay}" />
                    </Style>
                </HierarchicalDataTemplate.ItemContainerStyle>
            </HierarchicalDataTemplate>

I.e the TreeGroup Class. See Treegroup here:

namespace RTX64MonitorUtility.TreeView
{
    //Data class.  Holds information in the Datacontext section of the UI Elements it is attached to.
    public class TreeGroup : DependencyObject, IParent<object>
    {


        public string Name { get; set; }
        public List<TreeMonitoringEvent> Members { get; set; }
        public IEnumerable<object> GetChildren()
        {
            return Members;
        }

        public bool IsExpanded { get; set; } = false;
    }
}

Here is the List it's drawing from:

namespace RTX64MonitorUtility.Pages
{
    /// <summary>
    /// Interaction logic for EventsAndTriggers.xaml
    /// </summary>
    public partial class EventsAndTriggers : Page
    {
         public ObservableCollection<TreeGroup> MonitorEventCatagories { get; set; }
         ...
    }
}

My primary goal here is that if the TreeGroup Item is not expanded then the chidren's Checked and Unchecked events do not get triggered so I need to do the required actions for them. If I can find out a way of reading the IsExpanded value from those events that would also be a solution.

CodePudding user response:

You usually bind the item container's properties like TreeViewItem.IsExpanded or ListBoxItem.IsSelected etc. to your data model by using a Style that targets the item container. The DataContext of the item container is the data model:

<TreeView>
  <TreeView.ItemContainerStyle>
    <Style TargetType="TreeViewItem">
      <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
    </Style>
  </TreeView.ItemContainerStyle>
</TreeView>

Next, implement the data model properly. The properties that are the source of the data binding must be implemented as dependency properties (for dependency objects) or raise INotifyPropertyChanged.PropertyChanged event.

Since DependencyObject extends DispatcherObject, any class that extends DependencyObject is thread affine: in WPF, a DispatcherObject can only be accessed by the Dispatcher it is associated with.
For this reason you usually prefer INotifyPropertyChanged on data models.
DependencyObject is for UI objects that are bound to the Dispatcher thread by definition.

// Let the model implement the INotifyPropertyChanged interface
public class TreeGroup : INotifyPropertyChanged, IParent<object>
{
  // Follow this pattern for all properties that will change 
  // and are source of a data binding.
  private bool isExpanded;
  public IsExpanded 
  { 
    get => this.isExpanded; 
    set; 
    {
      this.isExpanded = value;
      OnPropertyChanged();
    }
  }

  public event PropertyChangedEventHandler PropertyChanged;
  private void OnPropertyChanged([CallerMemberName] string propertyName = "")
    => this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}

See Microsoft Docs: Data binding overview (WPF .NET)

  • Related