将数据网格的选定项绑定到树视图的选定值时出现问题

发布于 2024-09-17 11:41:39 字数 973 浏览 13 评论 0原文

我在将树视图绑定到数据网格的选定项目时遇到问题。

它们位于不同的视图中,但数据网格的所选项目已传递到树视图的相关视图模型。 TreeView相关的ViewModel中有一个SelectedGroup属性,它是DataGrid的选中项,其类型是Group。我想将组的 ID 字段绑定到树视图,即我希望在树视图中选择所选项目的 ID,并通过树视图的所选值进行更新。 我找不到如何绑定。 这是我的树视图的骨架,它可以按层次结构列出所有组。 有人可以帮我填写必填字段吗? 提前致谢。

    <TreeView Grid.Column="1" Grid.Row="4" Height="251" HorizontalAlignment="Left"
        Margin="4,3,0,0" Name="parentGroupTreeView" VerticalAlignment="Top" 
        Width="246" ItemsSource="{Binding Groups}" ItemContainerStyle="{x:Null}"  
        SelectedValuePath="ID">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding 
                 Converter={x:Static Member=conv:GroupSubGroupsConv.Default}}">
                    <Label Name="groupLabel" Content="{Binding GroupName}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>                
        </TreeView>

I have problems with binding a treeview to a datagrid's selected item.

they are in different views, but datagrid's selected item is already passed to treeview's related viewmodel.
There is a SelectedGroup property in treeview's related viewmodel which is datagrid's selected item and its type is Group. I want to bind the ID field of Group to treeview, i.e. I want the ID of selected item to be selected in treeview and also be updated by selected value of treeview.
I couldn't find out how to bind.
Here's my treeview's skeleton, which can just lists all of the groups hierarchically.
Can anyone help me on filling the required fields please?
Thanks in advance.

    <TreeView Grid.Column="1" Grid.Row="4" Height="251" HorizontalAlignment="Left"
        Margin="4,3,0,0" Name="parentGroupTreeView" VerticalAlignment="Top" 
        Width="246" ItemsSource="{Binding Groups}" ItemContainerStyle="{x:Null}"  
        SelectedValuePath="ID">
            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding 
                 Converter={x:Static Member=conv:GroupSubGroupsConv.Default}}">
                    <Label Name="groupLabel" Content="{Binding GroupName}"/>
                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>                
        </TreeView>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

梦里°也失望 2024-09-24 11:41:39

首先查看 Josh Smith 撰写的以下文章:通过使用视图模型模式

我还使用 WPF 工具包中的 DataGrid。

要了解此代码的工作原理,请查看下面的 IsSelected 属性。

这是包含树和数据网格的 XAML:

<Window x:Class="TreeviewDatagrid.Views.MainView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:WpfToolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
  xmlns:ViewModels="clr-namespace:TreeviewDatagrid.ViewModels" Title="Main Window" Height="400" Width="800">
  <DockPanel>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="7*"/>
        </Grid.ColumnDefinitions>
        <TreeView ItemsSource="{Binding Groups}"
                  Grid.Column="0">
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                    <Setter Property="FontWeight" Value="Normal" />
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="FontWeight" Value="Bold" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </TreeView.ItemContainerStyle>
            <TreeView.Resources>
                <HierarchicalDataTemplate 
                    DataType="{x:Type ViewModels:GroupViewModel}" 
                    ItemsSource="{Binding Children}" >
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding GroupName}" />
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>
        <WpfToolkit:DataGrid  
            Grid.Column="1"
            SelectedItem="{Binding Path=SelectedGroup, Mode=TwoWay}"
            ItemsSource="{Binding Path=Groups, Mode=OneWay}" >
        </WpfToolkit:DataGrid>
    </Grid>
  </DockPanel>
</Window>

这是 TreeView 和 DataGrid 绑定到的主视图模型:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using TreeviewDatagrid.Models;

namespace TreeviewDatagrid.ViewModels
{
 public class MainViewModel : ViewModelBase
 {
  public MainViewModel()
  {
     Group g1 = new Group();
     g1.Id = 1;
     g1.GroupName = "Planners";
     g1.Description = "People who plan";
     GroupViewModel gvm1 = new GroupViewModel(this, g1);

     Group g2 = new Group();
     g2.Id = 2;
     g2.GroupName = "Thinkers";
     g2.Description = "People who think";
     GroupViewModel gvm2 = new GroupViewModel(this, g2);

     Group g3 = new Group();
     g3.Id = 3;
     g3.GroupName = "Doers";
     g3.Description = "People who do";
     GroupViewModel gvm3 = new GroupViewModel(this, g3);

     IList<GroupViewModel> list = new List<GroupViewModel>();
     list.Add(gvm1);
     list.Add(gvm2);
     list.Add(gvm3);

     _selectedGroup = gvm1;

     _groups = new ReadOnlyCollection<GroupViewModel>(list);
  }

  readonly ReadOnlyCollection<GroupViewModel> _groups;
  public ReadOnlyCollection<GroupViewModel> Groups
  {
     get { return _groups; }
  }

  private GroupViewModel _selectedGroup;
  public GroupViewModel SelectedGroup
  {
     get
     {
        return _selectedGroup;
     }
     set
     {
        // keep selection in grid in-sync with tree
        _selectedGroup.IsSelected = false;
        _selectedGroup = value;
        _selectedGroup.IsSelected = true;
        OnPropertyChanged("SelectedGroup");
     }
  }

  public void ChangeSelectedGroup(GroupViewModel selectedGroup)
  {
     _selectedGroup = selectedGroup;
     OnPropertyChanged("SelectedGroup");
  }
 }
}

这是我用来绑定到网格和树的视图模型:

using TreeviewDatagrid.Models;

namespace TreeviewDatagrid.ViewModels
{
   public class GroupViewModel : TreeViewItemViewModel
   {
      private readonly MainViewModel _mainViewModel;
      readonly Group _group;
      bool _isSelected;

      public GroupViewModel(MainViewModel mainViewModel, Group group) : base(null, true)
      {
         _mainViewModel = mainViewModel;
         _group = group;
      }

      public string GroupName
      {
         get { return _group.GroupName; }
      }

      public override bool IsSelected
      {
         get { return _isSelected; }
         set
         {
            if (value != _isSelected)
            {
               _isSelected = value;
               if (_isSelected )
               {
                  // keep tree selection in sync with grid
                  _mainViewModel.ChangeSelectedGroup(this);
               }
               this.OnPropertyChanged("IsSelected");
            }
         }
      }

      protected override void LoadChildren()
      {
        // load children in treeview here
      }
   }
}

为了完整起见,这里是 Group 对象:

  namespace TreeviewDatagrid.Models
  {
     public class Group
     {
        public int Id  { get; set; }
        public string GroupName { get; set; }
        public string Description { get; set; }
     }
  }

还有 TreeView 的基类:

using System.Collections.ObjectModel;
using System.ComponentModel;

namespace TreeviewDatagrid.ViewModels
{
   /// <summary>
   /// Base class for all ViewModel classes displayed by TreeViewItems.  
   /// This acts as an adapter between a raw data object and a TreeViewItem.
   /// </summary>
   public class TreeViewItemViewModel : INotifyPropertyChanged
   {
       #region Data

       static readonly TreeViewItemViewModel DummyChild = new TreeViewItemViewModel();

       readonly ObservableCollection<TreeViewItemViewModel> _children;
       readonly TreeViewItemViewModel _parent;

       bool _isExpanded;
       bool _isSelected;

       #endregion // Data

       #region Constructors

       protected TreeViewItemViewModel(TreeViewItemViewModel parent, bool lazyLoadChildren)
       {
           _parent = parent;

           _children = new ObservableCollection<TreeViewItemViewModel>();

           if (lazyLoadChildren)
               _children.Add(DummyChild);
       }

       // This is used to create the DummyChild instance.
       private TreeViewItemViewModel()
       {
       }

       #endregion // Constructors

       #region Presentation Members

       #region Children

       /// <summary>
       /// Returns the logical child items of this object.
       /// </summary>
       public ObservableCollection<TreeViewItemViewModel> Children
       {
           get { return _children; }
       }

       #endregion // Children

       #region HasLoadedChildren

       /// <summary>
       /// Returns true if this object's Children have not yet been populated.
       /// </summary>
       public bool HasDummyChild
       {
           get { return this.Children.Count == 1 && this.Children[0] == DummyChild; }
       }

       #endregion // HasLoadedChildren

       #region IsExpanded

       /// <summary>
       /// Gets/sets whether the TreeViewItem 
       /// associated with this object is expanded.
       /// </summary>
       public bool IsExpanded
       {
           get { return _isExpanded; }
           set
           {
               if (value != _isExpanded)
               {
                   _isExpanded = value;
                   this.OnPropertyChanged("IsExpanded");
               }

               // Expand all the way up to the root.
               if (_isExpanded && _parent != null)
                   _parent.IsExpanded = true;

               // Lazy load the child items, if necessary.
               if (this.HasDummyChild)
               {
                   this.Children.Remove(DummyChild);
                   this.LoadChildren();
               }
           }
       }

       #endregion // IsExpanded

       #region IsSelected

       /// <summary>
       /// Gets/sets whether the TreeViewItem 
       /// associated with this object is selected.
       /// </summary>
       public virtual bool IsSelected
       {
           get { return _isSelected; }
           set
           {
               if (value != _isSelected)
               {
                   _isSelected = value;
                   this.OnPropertyChanged("IsSelected");
               }
           }
       }

       #endregion // IsSelected

       #region LoadChildren

       /// <summary>
       /// Invoked when the child items need to be loaded on demand.
       /// Subclasses can override this to populate the Children collection.
       /// </summary>
       protected virtual void LoadChildren()
       {
       }

       #endregion // LoadChildren

       #region Parent

       public TreeViewItemViewModel Parent
       {
           get { return _parent; }
       }

       #endregion // Parent

       #endregion // Presentation Members

       #region INotifyPropertyChanged Members

       public event PropertyChangedEventHandler PropertyChanged;

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

       #endregion // INotifyPropertyChanged Members
   }
}

Start by taking a look at the following article by Josh Smith on Simplifying the WPF TreeView by Using the ViewModel Pattern.

I am also using the DataGrid from the WPF toolkit.

To get a sense as to how this code works look at the IsSelected property below.

Here is the XAML that contains a tree and a datagrid:

<Window x:Class="TreeviewDatagrid.Views.MainView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:WpfToolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit" 
  xmlns:ViewModels="clr-namespace:TreeviewDatagrid.ViewModels" Title="Main Window" Height="400" Width="800">
  <DockPanel>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="7*"/>
        </Grid.ColumnDefinitions>
        <TreeView ItemsSource="{Binding Groups}"
                  Grid.Column="0">
            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
                    <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
                    <Setter Property="FontWeight" Value="Normal" />
                    <Style.Triggers>
                        <Trigger Property="IsSelected" Value="True">
                            <Setter Property="FontWeight" Value="Bold" />
                        </Trigger>
                    </Style.Triggers>
                </Style>
            </TreeView.ItemContainerStyle>
            <TreeView.Resources>
                <HierarchicalDataTemplate 
                    DataType="{x:Type ViewModels:GroupViewModel}" 
                    ItemsSource="{Binding Children}" >
                    <StackPanel Orientation="Horizontal">
                        <TextBlock Text="{Binding GroupName}" />
                    </StackPanel>
                </HierarchicalDataTemplate>
            </TreeView.Resources>
        </TreeView>
        <WpfToolkit:DataGrid  
            Grid.Column="1"
            SelectedItem="{Binding Path=SelectedGroup, Mode=TwoWay}"
            ItemsSource="{Binding Path=Groups, Mode=OneWay}" >
        </WpfToolkit:DataGrid>
    </Grid>
  </DockPanel>
</Window>

Here is the main view model that the TreeView and DataGrid bind to:

using System.Collections.Generic;
using System.Collections.ObjectModel;
using TreeviewDatagrid.Models;

namespace TreeviewDatagrid.ViewModels
{
 public class MainViewModel : ViewModelBase
 {
  public MainViewModel()
  {
     Group g1 = new Group();
     g1.Id = 1;
     g1.GroupName = "Planners";
     g1.Description = "People who plan";
     GroupViewModel gvm1 = new GroupViewModel(this, g1);

     Group g2 = new Group();
     g2.Id = 2;
     g2.GroupName = "Thinkers";
     g2.Description = "People who think";
     GroupViewModel gvm2 = new GroupViewModel(this, g2);

     Group g3 = new Group();
     g3.Id = 3;
     g3.GroupName = "Doers";
     g3.Description = "People who do";
     GroupViewModel gvm3 = new GroupViewModel(this, g3);

     IList<GroupViewModel> list = new List<GroupViewModel>();
     list.Add(gvm1);
     list.Add(gvm2);
     list.Add(gvm3);

     _selectedGroup = gvm1;

     _groups = new ReadOnlyCollection<GroupViewModel>(list);
  }

  readonly ReadOnlyCollection<GroupViewModel> _groups;
  public ReadOnlyCollection<GroupViewModel> Groups
  {
     get { return _groups; }
  }

  private GroupViewModel _selectedGroup;
  public GroupViewModel SelectedGroup
  {
     get
     {
        return _selectedGroup;
     }
     set
     {
        // keep selection in grid in-sync with tree
        _selectedGroup.IsSelected = false;
        _selectedGroup = value;
        _selectedGroup.IsSelected = true;
        OnPropertyChanged("SelectedGroup");
     }
  }

  public void ChangeSelectedGroup(GroupViewModel selectedGroup)
  {
     _selectedGroup = selectedGroup;
     OnPropertyChanged("SelectedGroup");
  }
 }
}

Here is the viewmodel that I use to bind to the grid and the tree:

using TreeviewDatagrid.Models;

namespace TreeviewDatagrid.ViewModels
{
   public class GroupViewModel : TreeViewItemViewModel
   {
      private readonly MainViewModel _mainViewModel;
      readonly Group _group;
      bool _isSelected;

      public GroupViewModel(MainViewModel mainViewModel, Group group) : base(null, true)
      {
         _mainViewModel = mainViewModel;
         _group = group;
      }

      public string GroupName
      {
         get { return _group.GroupName; }
      }

      public override bool IsSelected
      {
         get { return _isSelected; }
         set
         {
            if (value != _isSelected)
            {
               _isSelected = value;
               if (_isSelected )
               {
                  // keep tree selection in sync with grid
                  _mainViewModel.ChangeSelectedGroup(this);
               }
               this.OnPropertyChanged("IsSelected");
            }
         }
      }

      protected override void LoadChildren()
      {
        // load children in treeview here
      }
   }
}

For completeness here is the Group object:

  namespace TreeviewDatagrid.Models
  {
     public class Group
     {
        public int Id  { get; set; }
        public string GroupName { get; set; }
        public string Description { get; set; }
     }
  }

And also the base class for the TreeView:

using System.Collections.ObjectModel;
using System.ComponentModel;

namespace TreeviewDatagrid.ViewModels
{
   /// <summary>
   /// Base class for all ViewModel classes displayed by TreeViewItems.  
   /// This acts as an adapter between a raw data object and a TreeViewItem.
   /// </summary>
   public class TreeViewItemViewModel : INotifyPropertyChanged
   {
       #region Data

       static readonly TreeViewItemViewModel DummyChild = new TreeViewItemViewModel();

       readonly ObservableCollection<TreeViewItemViewModel> _children;
       readonly TreeViewItemViewModel _parent;

       bool _isExpanded;
       bool _isSelected;

       #endregion // Data

       #region Constructors

       protected TreeViewItemViewModel(TreeViewItemViewModel parent, bool lazyLoadChildren)
       {
           _parent = parent;

           _children = new ObservableCollection<TreeViewItemViewModel>();

           if (lazyLoadChildren)
               _children.Add(DummyChild);
       }

       // This is used to create the DummyChild instance.
       private TreeViewItemViewModel()
       {
       }

       #endregion // Constructors

       #region Presentation Members

       #region Children

       /// <summary>
       /// Returns the logical child items of this object.
       /// </summary>
       public ObservableCollection<TreeViewItemViewModel> Children
       {
           get { return _children; }
       }

       #endregion // Children

       #region HasLoadedChildren

       /// <summary>
       /// Returns true if this object's Children have not yet been populated.
       /// </summary>
       public bool HasDummyChild
       {
           get { return this.Children.Count == 1 && this.Children[0] == DummyChild; }
       }

       #endregion // HasLoadedChildren

       #region IsExpanded

       /// <summary>
       /// Gets/sets whether the TreeViewItem 
       /// associated with this object is expanded.
       /// </summary>
       public bool IsExpanded
       {
           get { return _isExpanded; }
           set
           {
               if (value != _isExpanded)
               {
                   _isExpanded = value;
                   this.OnPropertyChanged("IsExpanded");
               }

               // Expand all the way up to the root.
               if (_isExpanded && _parent != null)
                   _parent.IsExpanded = true;

               // Lazy load the child items, if necessary.
               if (this.HasDummyChild)
               {
                   this.Children.Remove(DummyChild);
                   this.LoadChildren();
               }
           }
       }

       #endregion // IsExpanded

       #region IsSelected

       /// <summary>
       /// Gets/sets whether the TreeViewItem 
       /// associated with this object is selected.
       /// </summary>
       public virtual bool IsSelected
       {
           get { return _isSelected; }
           set
           {
               if (value != _isSelected)
               {
                   _isSelected = value;
                   this.OnPropertyChanged("IsSelected");
               }
           }
       }

       #endregion // IsSelected

       #region LoadChildren

       /// <summary>
       /// Invoked when the child items need to be loaded on demand.
       /// Subclasses can override this to populate the Children collection.
       /// </summary>
       protected virtual void LoadChildren()
       {
       }

       #endregion // LoadChildren

       #region Parent

       public TreeViewItemViewModel Parent
       {
           get { return _parent; }
       }

       #endregion // Parent

       #endregion // Presentation Members

       #region INotifyPropertyChanged Members

       public event PropertyChangedEventHandler PropertyChanged;

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

       #endregion // INotifyPropertyChanged Members
   }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文