Данные в TreeView.ItemTemplate HierarchicalDataTemplate стираются при переключении выбранного TreeItem - PullRequest
1 голос
/ 06 января 2020

У меня есть следующий код:

Template.XAML

<Style TargetType="{x:Type HeaderedContentControl}">
    <Setter Property="Header">
        <Setter.Value>
            <ContentControl  Foreground="Red" 
                       FontFamily="Segoe UI" 
                       Margin="0,0,0,20" 
                        Content="{Binding Tag, RelativeSource={RelativeSource AncestorType=HeaderedContentControl}}" 
                       HorizontalAlignment="Center" 
                       VerticalAlignment="Center" />
        </Setter.Value>
    </Setter>
</Style>

<DataTemplate DataType="{x:Type local:ViewModel}">
    <HeaderedContentControl  xmlns:sys="clr-namespace:System;assembly=mscorlib"
                   Tag="{Binding Header}"
                   Background="SteelBlue"
                   BorderBrush="DarkSlateBlue">
    </HeaderedContentControl>
</DataTemplate>

<DataTemplate DataType="{x:Type local:ViewModel2}">
    <HeaderedContentControl  xmlns:sys="clr-namespace:System;assembly=mscorlib"
                   Tag="{Binding Header}"
                   Background="SteelBlue"
                   BorderBrush="DarkSlateBlue">
    </HeaderedContentControl>
</DataTemplate>

Windows.XAML:

<Window.DataContext>
    <local:WindowsVM x:Name="viewModel"/>
</Window.DataContext>

<Window.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="Templates.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Window.Resources>

<StackPanel Orientation="Horizontal">
    <TreeView SelectedItemChanged="TreeView_SelectedItemChanged" ItemsSource="{Binding AllContents}">
        <TreeView.ItemTemplate>
            <HierarchicalDataTemplate>
                <Label Content="{Binding Header}" />
            </HierarchicalDataTemplate>
        </TreeView.ItemTemplate>
    </TreeView>
    <StackPanel Orientation="Vertical">
        <ContentControl Content="{Binding SelectedItem}" />
    </StackPanel>
</StackPanel>

Windows.XAML.cs

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        viewModel.SelectedItem = (ViewModel)e.NewValue;
    }
}

ViewModel.cs

public class WindowsVM : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public WindowsVM()
    {
        AllContents = new ObservableCollection<ViewModel>();
        AllContents.Add(new ViewModel("Item 1"));
        AllContents.Add(new ViewModel2("Item 2")); //using ViewModel("Item 2") will show the Header as it should
        SelectedItem = AllContents.First();
    }

    private ViewModel _selectedItem;
    public ViewModel SelectedItem
    {
        get { return _selectedItem; }
        set
        {
            _selectedItem = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(SelectedItem)));
        }
    }

    private ObservableCollection<ViewModel> _allContents;
    public ObservableCollection<ViewModel> AllContents
    {
        get { return _allContents; }
        set
        {
            _allContents = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(AllContents)));
        }
    }
}

public  class ViewModel:INotifyPropertyChanged
{
    private string _header;

    public event PropertyChangedEventHandler PropertyChanged;

    public string Header
    {
        get { return _header; }
        set
        {
            _header = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Header)));
        }
    }

    public ViewModel(string header)
    {
        Header = header;
    }
}


public class ViewModel2 : ViewModel
{
    public ViewModel2(string header) : base(header)
    {
    }
}

Если вы запустите приложение, «Элемент 1» будет отображаться как заголовок выбранного элемента, как и должно быть.

Если вы нажмете на Второй узел дерева, я бы ожидал, что «Элемент 2» отображается в качестве заголовка выбранного элемента, но это не так.

Здесь следует интересная часть, если для «Элемента 2» он имеет тип ViewModel вместо ViewModel2, тогда будет показан заголовок «Элемент 2». Как это происходит? Это ошибка WPF treeview?

Я также приветствовал бы обходной путь, в духе модели MVVM.

1 Ответ

1 голос
/ 07 января 2020

Вы должны привязать свойство Header HeaderedContentControl к свойству Header source, а затем определить HeaderTemplate в стиле HeaderedContentControl.

Если вы реализуете Templates.xaml таким образом, ваш пример работает как положено:

<Style TargetType="{x:Type HeaderedContentControl}">
    <Setter Property="HeaderTemplate">
        <Setter.Value>
            <DataTemplate>
                <Label Foreground="Red" 
                       FontFamily="Segoe UI" 
                       Margin="0,0,0,20" 
                       Content="{Binding}" 
                       HorizontalAlignment="Center" 
                       VerticalAlignment="Center" />
            </DataTemplate>
        </Setter.Value>
    </Setter>
</Style>

<DataTemplate DataType="{x:Type local:ViewModel}">
    <HeaderedContentControl Header="{Binding Header}"
                   Background="SteelBlue"
                   BorderBrush="DarkSlateBlue">
    </HeaderedContentControl>
</DataTemplate>

<DataTemplate DataType="{x:Type local:ViewModel2}">
    <HeaderedContentControl Header="{Binding Header}"
                   Background="SteelBlue"
                   BorderBrush="DarkSlateBlue">
    </HeaderedContentControl>
</DataTemplate>
...