在切换所选的树形项时,TreeView.ItemTemplate 中的数据被擦除。

huangapple go评论72阅读模式
英文:

Data in TreeView.ItemTemplate HierarchicalDataTemplate gets erased when switching the Selected TreeItem

问题

以下是要翻译的代码部分:

我有以下代码:

`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")); // 使用 ViewModel("Item 2") 将会显示应该显示的标题
            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)
        {
        }
    }

如果运行该应用程序,"Item 1" 将显示为所选项的标题,正如预期的那样。

如果单击第二个树节点,我希望 "Item 2" 显示为所选项的标题,但实际上没有显示。

这里有一个有趣的部分,如果 "Item 2"  `ViewModel` 类型而不是 `ViewModel2`,那么 "Item 2" 的标题将会显示。为什么会发生这种情况?这是否是 WPF `TreeView`  bug

我也欢迎任何 MVVM 模式下的解决方法。

<details>
<summary>英文:</summary>

I have the following code:

`Template.XAML`

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

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

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

`Windows.XAML:`

    &lt;Window.DataContext&gt;
        &lt;local:WindowsVM x:Name=&quot;viewModel&quot;/&gt;
    &lt;/Window.DataContext&gt;

    &lt;Window.Resources&gt;
        &lt;ResourceDictionary&gt;
            &lt;ResourceDictionary.MergedDictionaries&gt;
                &lt;ResourceDictionary Source=&quot;Templates.xaml&quot; /&gt;
            &lt;/ResourceDictionary.MergedDictionaries&gt;
        &lt;/ResourceDictionary&gt;
    &lt;/Window.Resources&gt;

    &lt;StackPanel Orientation=&quot;Horizontal&quot;&gt;
        &lt;TreeView SelectedItemChanged=&quot;TreeView_SelectedItemChanged&quot; ItemsSource=&quot;{Binding AllContents}&quot;&gt;
            &lt;TreeView.ItemTemplate&gt;
                &lt;HierarchicalDataTemplate&gt;
                    &lt;Label Content=&quot;{Binding Header}&quot; /&gt;
                &lt;/HierarchicalDataTemplate&gt;
            &lt;/TreeView.ItemTemplate&gt;
        &lt;/TreeView&gt;
        &lt;StackPanel Orientation=&quot;Vertical&quot;&gt;
            &lt;ContentControl Content=&quot;{Binding SelectedItem}&quot; /&gt;
        &lt;/StackPanel&gt;
    &lt;/StackPanel&gt;

`Windows.XAML.cs`

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

        private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs&lt;object&gt; e)
        {
            viewModel.SelectedItem = (ViewModel)e.NewValue;
        }
    }

`ViewModel.cs`

    public class WindowsVM : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public WindowsVM()
        {
            AllContents = new ObservableCollection&lt;ViewModel&gt;();
            AllContents.Add(new ViewModel(&quot;Item 1&quot;));
            AllContents.Add(new ViewModel2(&quot;Item 2&quot;)); //using ViewModel(&quot;Item 2&quot;) 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&lt;ViewModel&gt; _allContents;
        public ObservableCollection&lt;ViewModel&gt; 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)
        {
        }
    }


If you run the application, the &quot;Item 1&quot; will be shown as the header of the selected item, as it should.

If you click on the second tree node, I would expect &quot;Item 2&quot; shown as the header of the selected item, but it is not.

Here comes the interesting part, if for &quot;Item 2&quot;, it is of the type `ViewModel` instead of `ViewModel2`, then the &quot;Item 2&quot; header will be shown. How come this is happening? Is this a WPF `treeview` bug?

I would also welcome workaround, in the spirit of MVVM model.



</details>


# 答案1
**得分**: 1

你应该将 `HeaderedContentControl`  `Header` 属性绑定到你的 `Header` 源属性,然后在 `HeaderedContentControl` 样式中定义一个 `HeaderTemplate`。 

如果你像这样实现 `Templates.xaml`,你的示例将按预期工作:

```xml
<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>

这是你要求的翻译,没有额外的内容。

英文:

You should bind the Header property of the HeaderedContentControl to your Header source property and then define a HeaderTemplate in the HeaderedContentControl style.

If you implement Templates.xaml like this, your example works as expected:

&lt;Style TargetType=&quot;{x:Type HeaderedContentControl}&quot;&gt;
&lt;Setter Property=&quot;HeaderTemplate&quot;&gt;
&lt;Setter.Value&gt;
&lt;DataTemplate&gt;
&lt;Label Foreground=&quot;Red&quot; 
FontFamily=&quot;Segoe UI&quot; 
Margin=&quot;0,0,0,20&quot; 
Content=&quot;{Binding}&quot; 
HorizontalAlignment=&quot;Center&quot; 
VerticalAlignment=&quot;Center&quot; /&gt;
&lt;/DataTemplate&gt;
&lt;/Setter.Value&gt;
&lt;/Setter&gt;
&lt;/Style&gt;
&lt;DataTemplate DataType=&quot;{x:Type local:ViewModel}&quot;&gt;
&lt;HeaderedContentControl Header=&quot;{Binding Header}&quot;
Background=&quot;SteelBlue&quot;
BorderBrush=&quot;DarkSlateBlue&quot;&gt;
&lt;/HeaderedContentControl&gt;
&lt;/DataTemplate&gt;
&lt;DataTemplate DataType=&quot;{x:Type local:ViewModel2}&quot;&gt;
&lt;HeaderedContentControl Header=&quot;{Binding Header}&quot;
Background=&quot;SteelBlue&quot;
BorderBrush=&quot;DarkSlateBlue&quot;&gt;
&lt;/HeaderedContentControl&gt;
&lt;/DataTemplate&gt;

huangapple
  • 本文由 发表于 2020年1月6日 15:13:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/59608038.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定