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

时间:2010-09-07 07:56:13

标签: wpf mvvm treeview selectedvalue

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

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

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

1 个答案:

答案 0 :(得分:1)

首先看看Josh Smith在Simplifying the WPF TreeView by Using the ViewModel Pattern上的以下文章。

我也在使用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
   }
}