c# - Xaml 中的 TreeViewItem MVVM IsSelected 不会将值发送到 View 模型

标签 c# wpf mvvm treeviewitem

我对 IsSelected 属性有疑问。它不会将值从 View 发送到 View 模型。我在下面发布了我的代码 View 模型:

public class Viewmodel : INotifyPropertyChanged
{
    private ObservableCollection<int> seznam;
    public ObservableCollection<int> Seznam
    {
        get { return seznam; }
        set
        {
            seznam = value;
        }
    }

    public Viewmodel()
    {
        Seznam = new ObservableCollection<int>();
        for (int i = 0; i < 3; i++)
        {
            Seznam.Add(i);
        }
    }

    bool isSelected;
    public bool IsSelected
    {
        get { return isSelected; }
        set
        {
            isSelected = value;
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

查看:

        <TreeView ItemsSource="{Binding Seznam}">
        <TreeView.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
            </Style>
        </TreeView.ItemContainerStyle>
    </TreeView>

它仍然没有在我设置的断点处停止 get { return isSelected;

最佳答案

根据您更新的帖子,很明显您没有正确实现 View 模型。特别是,您的 TreeView.ItemsSource 绑定(bind)到您唯一的 View 模型的 Seznam 属性。这是 int 值的集合。

这意味着 TreeView 中您尝试绑定(bind)到 IsSelected 属性的每个项目容器的数据上下文是一个 int 值(value)。当然,int 值甚至没有 IsSelected 属性。

(顺便说一句,我对 your claim that "There are no binding errors" 持怀疑态度。如果您查看调试输出,您当然应该看到绑定(bind)错误,其中试图绑定(bind)到不存在的 IsSelected属性(property)。)

考虑一下:假设项目容器确实设法绑定(bind)到 Viewmodel.IsSelected 属性。你认为有多少个元素容器?您认为有多少 Viewmodel 实例?您应该相信有很多元素容器,即您收藏中的每件元素一个。而且只有一个 Viewmodel 实例。那么,所有这些项目的选择状态如何映射到单个 Viewmodel.IsSelected 属性?

正确的方法是为集合创建一个单独的 View 模型对象,为您的 int 值创建一个属性,以及为 IsSelectedIsExpanded 状态(因为您最初提到想要两者)。

这是我之前写的例子,只是为了向自己证明通常的方法会按预期工作。调整它以满足您的需要应该没有任何问题......

每项 View 模型:

class TreeItemViewModel : NotifyPropertyChangedBase
{
    public ObservableCollection<TreeItemViewModel> Items { get; }
        = new ObservableCollection<TreeItemViewModel>();

    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set { _UpdateField(ref _isSelected, value, _OnBoolPropertyChanged); }
    }

    private bool _isExpanded;
    public bool IsExpanded
    {
        get { return _isExpanded; }
        set { _UpdateField(ref _isExpanded, value, _OnBoolPropertyChanged); }
    }

    private void _OnBoolPropertyChanged(bool obj)
    {
        _RaisePropertyChanged(nameof(FullText));
    }

    private string _text;
    public string Text
    {
        get { return _text; }
        set { _UpdateField(ref _text, value, _OnTextChanged); }
    }

    private void _OnTextChanged(string obj)
    {
        _RaisePropertyChanged(nameof(FullText));
    }

    public string FullText
    {
        get { return $"{Text} (IsSelected: {IsSelected}, IsExpanded: {IsExpanded})"; }
    }
}

窗口的主视图模型:

class MainViewModel : NotifyPropertyChangedBase
{
    public ObservableCollection<TreeItemViewModel> Items { get; }
        = new ObservableCollection<TreeItemViewModel>();

    public ICommand ClearSelection { get; }

    public MainViewModel()
    {
        ClearSelection = new ClearSelectionCommand(this);
    }

    class ClearSelectionCommand : ICommand
    {
        private readonly MainViewModel _parent;

        public ClearSelectionCommand(MainViewModel parent)
        {
            _parent = parent;
        }

#pragma warning disable 67
        public event EventHandler CanExecuteChanged;
#pragma warning restore 67

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            _parent._ClearSelection();
        }
    }

    private void _ClearSelection()
    {
        _ClearSelection(Items);
    }

    private static void _ClearSelection(IEnumerable<TreeItemViewModel> collection)
    {
        foreach (TreeItemViewModel item in collection)
        {
            _ClearSelection(item.Items);
            item.IsSelected = false;
            item.IsExpanded = false;
        }
    }
}

窗口的 XAML:

<Window x:Class="TestSO44513864TreeViewIsSelected.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:p="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:l="clr-namespace:TestSO44513864TreeViewIsSelected"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
  <Window.DataContext>
    <l:MainViewModel>
      <l:MainViewModel.Items>
        <l:TreeItemViewModel Text="One">
          <l:TreeItemViewModel.Items>
            <l:TreeItemViewModel Text="One A"/>
            <l:TreeItemViewModel Text="One B"/>
          </l:TreeItemViewModel.Items>
        </l:TreeItemViewModel>
        <l:TreeItemViewModel Text="Two"/>
        <l:TreeItemViewModel Text="Three"/>
      </l:MainViewModel.Items>
    </l:MainViewModel>
  </Window.DataContext>

  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition/>
    </Grid.RowDefinitions>
    <Button Content="Clear Selection" Command="{Binding ClearSelection}"
            HorizontalAlignment="Left"/>
    <TreeView ItemsSource="{Binding Items}" Grid.Row="1">
      <TreeView.ItemContainerStyle>
        <p:Style TargetType="TreeViewItem">
          <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
          <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
        </p:Style>
      </TreeView.ItemContainerStyle>
      <TreeView.ItemTemplate>
        <HierarchicalDataTemplate DataType="l:TreeItemViewModel"
                                  ItemsSource="{Binding Items}">
          <TextBlock Text="{Binding FullText}"/>
        </HierarchicalDataTemplate>
      </TreeView.ItemTemplate>
    </TreeView>
  </Grid>
</Window>

为了完整性......

INotifyPropertyChanged 实现的样板基类:

class NotifyPropertyChangedBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void _UpdateField<T>(ref T field, T newValue,
        Action<T> onChangedCallback = null,
        [CallerMemberName] string propertyName = null)
    {
        if (EqualityComparer<T>.Default.Equals(field, newValue))
        {
            return;
        }

        T oldValue = field;

        field = newValue;
        onChangedCallback?.Invoke(oldValue);
        _RaisePropertyChanged(propertyName);
    }

    protected void _RaisePropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

关于c# - Xaml 中的 TreeViewItem MVVM IsSelected 不会将值发送到 View 模型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44513864/

相关文章:

wpf - RelayCommand 内存泄漏

c# - Microsoft XNA 可以在 Windows 10 上运行吗?

c# - Autofac 的混合生活方式

c# - 什么时候应该在 WPF 中使用依赖属性?

c# - CollectionView.Refresh不过滤

c# - WPF/XAML : How to reference class that is not defined within any namespace

c# - 在显示两列的 WPF 组合框中搜索

c# - 在C#中拦截WebBrowser发出的请求

c# - 可以同时阅读字典吗?

mvvm - 为什么 Durandal 应用程序中没有建议的 "model"文件夹?