2012-11-24 22 views
2

我正在为使用datagrid的学生记录创建一个C#wpf应用程序。 如何在标题中创建复选框以选择/取消选中连续的所有复选框? 如何通过点击选择行中的复选框,以便我们可以编辑/删除记录?以及我们如何选择多个复选框来删除?Datagrid中的复选框wpf c#应用程序

回答

0

使用某事像这样:

DataGridCheckBoxColumn cbc = new DataGridCheckBoxColumn(); 
dataGrid.Columns.Add(cbc); 
CheckBox cb = new CheckBox(); 
cbc.Header = cb; 

和处理CheckedUnChecked事件:

cb.Checked+=new RoutedEventHandler(cb_Checked); 
cb.Unchecked+=new RoutedEventHandler(cb_Unchecked); 
2

我创建了一个行为,允许在控制属性绑定到的属性以下列方式收集项目:

  • 如果更改控件中的属性,则所有项目更新。
  • 如果您更改项目中的属性,如果所有项目具有相同的属性,控件将反映它。如果不是,控件的属性将被赋予一个回退值(如null)。

有了这种行为,您可以将复选框添加到您的DataGrid标题并将其IsChecked属性绑定到DataGrid的ItemSource,以集合类型的属性。

我们可以使用MVVM模式处理选择逻辑。例如,我们有以下视图模型的收藏单位:

public class ItemViewModel : INotifyPropertyChanged 
{ 
    #region INotifyPropertyChanged 

    public event PropertyChangedEventHandler PropertyChanged; 

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

    #endregion 

    private bool isSelected; 
    public bool IsSelected { 
     get { return this.isSelected; } 

     set 
     { 
      if (this.isSelected == value) 
       return; 

      this.isSelected = value; 
      this.OnPropertyChanged("IsSelected"); 
     } 
    } 

    private string myProperty; 
    public string MyProperty 
    { 
     get { return this.myProperty; } 
     set 
     { 
      if (this.myProperty != value) 
      { 
       this.myProperty = value; 
       this.OnPropertyChanged("MyProperty"); 
      } 
     } 
    } 
} 

然后我们有MainViewModel,控制主窗口的逻辑:

public class MainViewModel: INotifyPropertyChanged 
{ 
    #region INotifyPropertyChanged 

    public event PropertyChangedEventHandler PropertyChanged; 

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

    #endregion 

    #region Items (INotifyPropertyChanged Property) 
    private ObservableCollection<ItemViewModel> items; 

    public ObservableCollection<ItemViewModel> Items 
    { 
     get { return this.items; } 
     set 
     { 
      if (this.items != value) 
      { 
       this.items = value; 
       this.OnPropertyChanged("Items"); 
      } 
     } 
    } 
    #endregion 

    public MainViewModel() 
    { 
     this.Items = new ObservableCollection<ItemViewModel>(); 
     for (int i = 0; i < 10; i++) 
      this.Items.Add(new ItemViewModel() { MyProperty = "Item" + i }); 
    } 
} 

在我们的窗口,我们可以宣布DataGrid中。为了达到你想要什么,我们将不得不为以下内容:

  • 分配MainViewModel到Window.DataContext
  • 绑定DataGrid.ItemsSource在MainViewModel
  • 项目属性定义的列DataGrid。在这个例子中,我选择了一个“IsSelected”列,并按照您的指定将“SelectAll”复选框添加到其标题中,但是您可以在任何地方使用CheckBox来控制选择。
  • 我们需要使行的选择更新我们项目的“IsSelected”属性,反之亦然。为此,我们修改RowStyle,以便我们可以将行的“IsSelected”属性绑定到其项目的“IsSelected”。因此,现在选择逻辑可以完全由ViewModel驱动。
  • 最后要做的就是让“SelectAll”复选框执行其工作。我们应用CollectionPropertyBehavior并对其进行配置,使其“SourcePropertyPath”指向我们想要绑定的CheckBox中的属性(“IsChecked”),CollectionPropertyPath指向项中的属性(“IsSelected”)。然后我们只需要将它的ItemsSource绑定到DataGridItemsSource。请注意,默认值为“null”,这意味着当项目的属性中具有不同的值时,CheckBox将收回“null”并处于未定义状态。

最终的XAML将是这样的:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication2" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" x:Class="WpfApplication2.MainWindow" 
    Title="MainWindow" mc:Ignorable="d" Height="350" Width="525"> 
<Window.Resources> 
    <Style x:Key="DataGridRowStyle" TargetType="{x:Type DataGridRow}"> 
     <Setter Property="IsSelected" Value="{Binding IsSelected}"/> 
    </Style> 
</Window.Resources> 
<Window.DataContext> 
    <local:MainViewModel/> 
</Window.DataContext> 
<Grid> 
    <DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Items}" CanUserAddRows="False" RowStyle="{DynamicResource DataGridRowStyle}"> 
     <DataGrid.Columns> 
      <DataGridCheckBoxColumn Binding="{Binding IsSelected}"> 
       <DataGridCheckBoxColumn.Header> 
        <CheckBox> 
         <i:Interaction.Behaviors> 
          <local:CollectionPropertyBehavior CollectionPropertyPath="IsSelected" SourcePropertyPath="IsChecked" ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}"/> 
         </i:Interaction.Behaviors> 
        </CheckBox>         
       </DataGridCheckBoxColumn.Header>    
      </DataGridCheckBoxColumn> 
      <DataGridTextColumn Width="*" Binding="{Binding MyProperty}"/> 
     </DataGrid.Columns> 
    </DataGrid> 
</Grid> 

最后,行为:

public class CollectionPropertyBehavior : Behavior<DependencyObject> 
{ 
    private IEnumerable<ValueProxy> proxies; 
    private bool syncking; 

    public string SourcePropertyPath 
    { 
     get { return (string)GetValue(SourcePropertyPathProperty); } 
     set { SetValue(SourcePropertyPathProperty, value); } 
    } 
    public static readonly DependencyProperty SourcePropertyPathProperty = 
     DependencyProperty.Register("SourcePropertyPath", typeof(string), typeof(CollectionPropertyBehavior), new PropertyMetadata(null)); 

    public string CollectionPropertyPath 
    { 
     get { return (string)GetValue(CollectionPropertyPathProperty); } 
     set { SetValue(CollectionPropertyPathProperty, value); } 
    } 
    public static readonly DependencyProperty CollectionPropertyPathProperty = 
     DependencyProperty.Register("CollectionPropertyPath", typeof(string), typeof(CollectionPropertyBehavior), new PropertyMetadata(null)); 


    private IEnumerable<object> Items { get { return this.ItemsSource == null ? null : this.ItemsSource.OfType<object>(); } } 
    public IEnumerable ItemsSource 
    { 
     get { return (IEnumerable)GetValue(ItemsSourceProperty); } 
     set { SetValue(ItemsSourceProperty, value); } 
    } 
    public static readonly DependencyProperty ItemsSourceProperty = 
     DependencyProperty.Register("ItemsSource", typeof(IEnumerable), typeof(CollectionPropertyBehavior), new PropertyMetadata(null, ItemsSourceChanged)); 


    private object Value 
    { 
     get { return (object)GetValue(ValueProperty); } 
     set { SetValue(ValueProperty, value); } 
    } 
    private static readonly DependencyProperty ValueProperty = 
     DependencyProperty.Register("Value", typeof(object), typeof(CollectionPropertyBehavior), new PropertyMetadata(null, ValueChanged)); 


    public object DefaultValue 
    { 
     get { return (object)GetValue(DefaultValueProperty); } 
     set { SetValue(DefaultValueProperty, value); } 
    } 
    public static readonly DependencyProperty DefaultValueProperty = 
     DependencyProperty.Register("DefaultValue", typeof(object), typeof(CollectionPropertyBehavior), new PropertyMetadata(null)); 



    private static void ValueChanged(object sender, DependencyPropertyChangedEventArgs args) 
    { 
     var element = sender as CollectionPropertyBehavior; 
     if (element == null || element.ItemsSource == null) return; 

     element.UpdateCollection(); 
    } 

    private static void ItemsSourceChanged(object sender, DependencyPropertyChangedEventArgs args) 
    { 
     var element = sender as CollectionPropertyBehavior; 
     if (element == null || element.ItemsSource == null) return; 
     element.ItemsSourceChanged(); 
    } 

    private void ItemsSourceChanged() 
    { 
     this.proxies = null; 

     if (this.Items == null || !this.Items.Any() || this.CollectionPropertyPath == null) return; 

     // Cria os proxies 
     this.proxies = this.Items.Select(o => 
     { 
      var proxy = new ValueProxy(); 
      proxy.Bind(o, this.CollectionPropertyPath); 
      proxy.ValueChanged += (s, e) => this.UpdateSource(); 
      return proxy; 
     }).ToArray(); 

     this.UpdateSource(); 
    } 

    private void UpdateSource() 
    { 
     if (this.syncking) return; 

     // Atualiza o valor 
     using (new SynckingScope(this)) 
     { 
      object value = this.proxies.First().Value; 
      foreach (var proxy in this.proxies.Skip(1)) 
      { 
       value = object.Equals(proxy.Value, value) ? value : this.DefaultValue; 
      } 

      this.Value = value; 
     } 
    } 

    private void UpdateCollection() 
    { 
     // Se o valor estiver mudando em função da atualização de algum 
     // elemento da coleção, não faz nada 
     if (this.syncking) return; 

     using (new SynckingScope(this)) 
     { 
      // Atualiza todos os elementos da coleção, 
      // atrávés dos proxies 
      if (this.proxies != null) 
       foreach (var proxy in this.proxies) 
        proxy.Value = this.Value; 
     } 
    } 

    protected override void OnAttached() 
    { 
     base.OnAttached(); 


     // Bind da propriedade do objeto fonte para o behavior 
     var binding = new Binding(this.SourcePropertyPath); 
     binding.Source = this.AssociatedObject; 
     binding.Mode = BindingMode.TwoWay; 
     BindingOperations.SetBinding(this, ValueProperty, binding); 
    } 

    protected override void OnDetaching() 
    { 
     base.OnDetaching(); 

     // Limpa o binding de value para a propriedade do objeto associado 
     this.ClearValue(ValueProperty); 
    } 

    internal class SynckingScope : IDisposable 
    { 
     private readonly CollectionPropertyBehavior parent; 

     public SynckingScope(CollectionPropertyBehavior parent) 
     { 
      this.parent = parent; 
      this.parent.syncking = true; 
     } 

     public void Dispose() 
     { 
      this.parent.syncking = false; 
     } 
    } 

    internal class ValueProxy : DependencyObject 
    { 
     public event EventHandler ValueChanged; 

     public object Value 
     { 
      get { return (object)GetValue(ValueProperty); } 
      set { SetValue(ValueProperty, value); } 
     } 
     public static readonly DependencyProperty ValueProperty = 
      DependencyProperty.Register("Value", typeof(object), typeof(ValueProxy), new PropertyMetadata(null, OnValueChanged)); 


     private static void OnValueChanged(object sender, DependencyPropertyChangedEventArgs args) 
     { 
      var element = sender as ValueProxy; 
      if (element == null || element.ValueChanged == null) return; 

      element.ValueChanged(element, EventArgs.Empty); 
     } 

     public void Bind(object source, string path) 
     { 
      // Realiza o binding de value com o objeto desejado 
      var binding = new Binding(path); 
      binding.Source = source; 
      binding.Mode = BindingMode.TwoWay; 

      BindingOperations.SetBinding(this, ValueProperty, binding); 
     } 
    } 
} 

这种方法的优点是,它是完全可重用。在这个例子中,我们用它来处理选择,但是你可以在你的ItemViewModel中绑定到一个ComboBox的头部等等。

行为是从我的Silverlight 4项目移植过来的,但我测试过它在WPF应用程序中正常工作。不过,我认为在WPF中,我们可能会找到一个更好的方法来将行为调整为MarkupExtension。如果我有时间的话,我可以看看。此外,还可以对其进行调整,以便可以将选定项绑定到SelectedItems,当有选定项目时,会更新它们,如果没有,则会更新全部项目。