英文:
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`
<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)
{
}
}
If you run the application, the "Item 1" will be shown as the header of the selected item, as it should.
If you click on the second tree node, I would expect "Item 2" shown as the header of the selected item, but it is not.
Here comes the interesting part, if for "Item 2", it is of the type `ViewModel` instead of `ViewModel2`, then the "Item 2" 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:
<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>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论