2017-03-08 65 views
1

我对如何使用绑定设置CheckBox有点困惑,该绑定确保我的ViewModel中填充了所有选中的字段。我在底部提供了一些代码和说明。WPF TreeView复选框绑定 - 如何使用复选框填充ViewModel

我的XAML文件让我们把它TreeView.xaml

<TreeView x:Name="availableColumnsTreeView"   
      ItemsSource="{Binding Path=TreeFieldData, Mode=OneWay, Converter={StaticResource SortingConverter}, ConverterParameter='DisplayName.Text'}"> 

    <TreeView.ItemTemplate> 
     <HierarchicalDataTemplate x:Uid="HierarchicalDataTemplate_1" ItemsSource="{Binding Path=Children, Mode=OneWay, Converter={StaticResource SortingConverter}, ConverterParameter='DisplayName.Text'}"> 
      <CheckBox VerticalAlignment="Center" IsChecked="{Binding IsSelected, Mode=TwoWay}"> 
       <TextBlock x:Uid="TextBlock_1" Text="{Binding DisplayName.Text, Mode=OneWay}" /> 
      </CheckBox>    
     </HierarchicalDataTemplate> 
    </TreeView.ItemTemplate> 
</TreeView> 

的 “代码隐藏” TreeView.xaml.cs

public partial class MultipleColumnsSelectorView : UserControl 
{ 
    public MultipleColumnsSelectorView() 
    { 
     InitializeComponent(); 
    } 

    private MultipleColumnsSelectorVM Model 
    { 
     get { return DataContext as MultipleColumnsSelectorVM; } 
    } 
} 

视图模型(试图只包括相关的东西)MultipleColumnsSelectorVM

public partial class MultipleColumnsSelectorVM : ViewModel, IMultipleColumnsSelectorVM 
{ 
    public ReadOnlyCollection<TreeFieldData> TreeFieldData 
    { 
     get { return GetValue(Properties.TreeFieldData); } 
     set { SetValue(Properties.TreeFieldData, value); } 
    } 

    public List<TreeFieldData> SelectedFields 
    { 
     get { return GetValue(Properties.SelectedFields); } 
     set { SetValue(Properties.SelectedFields, value); } 
    } 

    private void AddFields() 
    { 
     //Logic which loops over SelectedFields and when done calls a delegate which passes 
     //the result to another class. This works, implementation hidden 
    } 

该模型TreeFieldData

public class TreeFieldData : INotifyPropertyChanged 
{  
    public event PropertyChangedEventHandler PropertyChanged; 
    public IEnumerable<TreeFieldData> Children { get; private set; } 
    private bool _isSelected; 
    public bool IsSelected 
    { 
     get { return _isSelected; } 
     set 
     { 
      _isSelected = value; 
      if (PropertyChanged != null) 
       PropertyChanged.Invoke(this, new PropertyChangedEventArgs("IsSelected")); 
     } 
    } 
} 

问题:

,我想是当用户检查一个复选框,它应该设置的TreeFieldIsSelected财产行为(它是现在),但后来我想返回ViewModel并确保将此特定TreeField添加到SelectedFields。我真的不明白PropertyChangedEvent.Invoke会做什么以及谁会收到该事件?我怎样才能确保SelectedFields得到填充,所以当AddFields()被调用时,它有所有TreeField数据实例被检查?

+0

我猜MultipleColumnsSelectorVM类有一个“TreeFieldData”属性,它返回TreeView中看到的TreeFieldData对象的列表? – mm8

+0

@ mm8对不起,现在我已经包括了。 – DSF

+0

@ mm8 FYI:TreeFieldData属性由我没有包含的Initialize方法填充。 – DSF

回答

2

你可以通过TreeFieldData对象TreeFieldData集合中迭代,挂钩事件处理程序,以他们的PropertyChanged事件,然后添加/从SelectedFields集合中删除选定/未选定项目,如:

public MultipleColumnsSelectorVM() 
{ 
    Initialize(); 

    //do this after you have populated the TreeFieldData collection 
    foreach (TreeFieldData data in TreeFieldData) 
    { 
     data.PropertyChanged += OnPropertyChanged; 
    } 
} 

private void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) 
{ 
    if (e.PropertyName == "IsSelected") 
    { 
     TreeFieldData data = sender as TreeFieldData; 
     if (data.IsSelected && !SelectedFields.Contains(data)) 
      SelectedFields.Add(data); 
     else if (!data.IsSelected && SelectedFields.Contains(data)) 
      SelectedFields.Remove(data); 
    } 
} 
+0

它的工作表示感谢,但仅限于“最外层”节点。每个TreeFieldData节点都有一个Children列表,所以我必须递归循环以确保所有字段都附有PropertyChangedEvent。如果这有道理?我现在正在处理这个问题,在Initialize方法中进行递归。 – DSF

+0

是的,您需要遍历并连接所有您想要在SelectedFields集合中跟踪的项目的事件处理程序。 – mm8

+0

是的。这样做对性能不利?假设我们有几百个领域?这只是真的附加处理程序,但我不知道这是多么昂贵 – DSF

0

的PropertyChanged事件的订阅者是视图,因此如果以编程方式更改IsSelected,则视图知道它需要更新。

要将选定的TreeField插入到列表中,您需要将此代码添加到您的setter中。

此外,你可以定义下面的函数,这使得该通知,如果你有很多属性更容易:

private void NotifyPropertyChange([CallerMemberName] string propertyName = null) 
{ 
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
} 

的CallerMemberName属性指示编译器自动插入属性调用该方法的名称。这个?在PropertyChanged之后,你的比较是非空的。

IsSelected的制定者然后可以改为

set 
{ 
    _isSelected = value; 
    if (value) { viewModel.SelectedFields.Add(this); } 
    else { viewModel.SelectedFields.Remove(this); } 
    NotifyPropertyChange(); 
} 

当然,你将需要提供TreeFieldData与视图模型的实例,例如在构造函数中。

我不知道在您的视图中SelectedFields是否有界/显示。如果是,并且希望显示对列表所做的更改,则应将List更改为ObservableCollection。