DataGrid和CheckBox - 当IsChecked(MVVM)发生更改时更改外部属性的值

时间:2018-01-09 13:24:06

标签: c# wpf mvvm

我遇到问题,当CheckBox中的选项发生变化时,我需要在Amount属性中添加或减去Quantity属性值,这在今天更改所选行时完成

我做错了什么?

MainWindow.xaml

<Window.DataContext>
    <local:MainViewModel />
</Window.DataContext>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <StackPanel Orientation="Horizontal" Margin="5">
        <Label Content="Sum of selected items: " />
        <TextBlock Text="{Binding Amount}"  Grid.Row="0" VerticalAlignment="Center" />
    </StackPanel>


    <DataGrid Grid.Row="1" Margin="5"
              ColumnWidth="*" 
              AutoGenerateColumns="False"
              SelectionMode="Single" 
              HorizontalContentAlignment="Center" 
              ItemsSource="{Binding MyDataList}"
              SelectedItem="{Binding MyData, UpdateSourceTrigger=PropertyChanged}"
              ScrollViewer.CanContentScroll="True"
              ScrollViewer.VerticalScrollBarVisibility="Auto"
              ScrollViewer.HorizontalScrollBarVisibility="Auto" >
        <DataGrid.Columns>
            <DataGridTemplateColumn Width="30">
                <DataGridTemplateColumn.Header>
                    <Grid>
                        <CheckBox IsChecked="{Binding DataContext.AllSelected, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>
                    </Grid>
                </DataGridTemplateColumn.Header>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <!--<Viewbox Height="25">-->
                        <CheckBox IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True, Mode=TwoWay}"/>
                        <!--</Viewbox>-->
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTextColumn Header="Description" Binding="{Binding Description}"  Width="*" IsReadOnly="True"/>
            <DataGridTextColumn Header="Quantity" Binding="{Binding Quantity, StringFormat=N2}" Width="160" IsReadOnly="True"/>

        </DataGrid.Columns>
    </DataGrid>
</Grid>

(ViewModelBase实现INotifyPropertyChanged)

public class MyDataClass: ViewModelBase
{
    private bool _isSelected;

    public bool IsSelected
    {
        get => _isSelected;
        set
        {
            _isSelected = value;
            OnPropertyChanged();
        }
    }
    public string Description { get; set; }
    public double Quantity { get; set; }
}

MainViewModel

public class MainViewModel : ViewModelBase
{
    private ObservableCollection<MyDataClass> _myDataList;
    private MyDataClass _myData;

    public MainViewModel()
    {
        var list = new List<MyDataClass>
        {
            new MyDataClass { Description = "Item 01", Quantity = 20, IsSelected = false },
            new MyDataClass { Description = "Item 02", Quantity = 50, IsSelected = false },
            new MyDataClass { Description = "Item 03", Quantity = 60, IsSelected = false }
        };
        MyDataList = new ObservableCollection<MyDataClass>(list);
    }

    public ObservableCollection<MyDataClass> MyDataList
    {
        get => _myDataList;
        set
        {
            _myDataList = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(Amount));
        }
    }

    public MyDataClass MyData
    {
        get => _myData;
        set
        {
            _myData = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(Amount));
        }
    }

    public double Amount => MyDataList.Where(x=>x.IsSelected).Sum(x => x.Quantity);

}

3 个答案:

答案 0 :(得分:2)

另一种方法是向所有复选框添加命令,因此每当您单击复选框时,它将触发命令

<CheckBox Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
                                                     AncestorType={x:Type DataGrid}},
                                                     Path=DataContext.CheckCommand}" IsChecked="{Binding DataContext.AllSelected, RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, UpdateSourceTrigger=PropertyChanged, NotifyOnSourceUpdated=True}"/>

MainViewModel

    private ObservableCollection<MyDataClass> _myDataList;
    private MyDataClass _myData;

    public ICommand CheckCommand { get; set; }

    public MainViewModel()
    {
        var list = new List<MyDataClass>
            {
                new MyDataClass { Description = "Item 01", Quantity = 20, IsSelected = false },
                new MyDataClass { Description = "Item 02", Quantity = 50, IsSelected = false },
                new MyDataClass { Description = "Item 03", Quantity = 60, IsSelected = false }
            };
        MyDataList = new ObservableCollection<MyDataClass>(list);
        CheckCommand = new RelayCommand(()=> { Amount = MyDataList.Where(x => x.IsSelected).Sum(x => x.Quantity); });
    }

    public ObservableCollection<MyDataClass> MyDataList
    {
        get => _myDataList;
        set
        {
            _myDataList = value;
            OnPropertyChanged();
        }
    }

我的RelayCommand类

 public class RelayCommand : ICommand
{

    private Action mAction;
    public event EventHandler CanExecuteChanged = (sender, e) => { };
    public RelayCommand(Action action)
    {
        mAction = action;
    }
    public bool CanExecute(object parameter)
    {
        return true;
    }
    public void Execute(object parameter)
    {
        mAction();
    }
}

答案 1 :(得分:1)

您需要为每个项目收听MyDataClass.PropertyChanged,如果IsSelected属性发生变化,请致电OnPropertyChanged(nameof(Amount))

public MainViewModel()
{
    var list = new List<MyDataClass>
    {
        new MyDataClass { Description = "Item 01", Quantity = 20, IsSelected = false },
        new MyDataClass { Description = "Item 02", Quantity = 50, IsSelected = false },
        new MyDataClass { Description = "Item 03", Quantity = 60, IsSelected = false }
    };

    foreach (var item in list)
    {
        item.PropertyChanged += OnItemPropertyChanged;
    }

    MyDataList = new ObservableCollection<MyDataClass>(list);
}

void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "IsSelected") OnPropertyChanged(nameof(Amount));
}

请注意,代码可能包含小错字,我只是在纯文本编辑器中编写。

如果您动态添加/删除项目,则需要处理这些项目的更改通知订阅(例如@Marisa已回答)。

答案 2 :(得分:1)

您需要挂钩CollectionChanged事件处理程序以获取数据集合,然后挂钩到每个项目的PropertyChanged。

请注意,您还需要更改初始化observable集合的方式;像在原始样本中那样初始化它意味着该集合不会获得PropertyChanged事件处理程序,因此不会通知集合成员已对其属性应用了更改。

public class MainViewModel : ViewModelBase
{
   private ObservableCollection<MyDataClass> _myDataList;

   public MainViewModel()
   {
      var list = new List<MyDataClass>
      {
         new MyDataClass {Description = "Item 01", Quantity = 20, IsSelected = false},
         new MyDataClass {Description = "Item 02", Quantity = 50, IsSelected = false},
         new MyDataClass {Description = "Item 03", Quantity = 60, IsSelected = false}
      };

      MyDataList = new ObservableCollection<MyDataClass>();

      foreach (var myDataClass in list)
      {
         MyDataList.Add(myDataClass);
      }
   }

   public ObservableCollection<MyDataClass> MyDataList
   {
      get => _myDataList;
      set
      {
         _myDataList = value;
         MyDataList.CollectionChanged += MyDataList_CollectionChanged; // sets up the collection so it will auto-hookup each element
         OnPropertyChanged();
      }
   }

   private void MyDataList_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
   {
      if (e.NewItems != null)
         foreach (MyDataClass item in e.NewItems)
            item.PropertyChanged += MyData_PropertyChanged;

      if (e.OldItems != null)
         foreach (MyDataClass item in e.OldItems)
            item.PropertyChanged -= MyData_PropertyChanged;

      OnPropertyChanged(nameof(MyDataList));
   }

   private void MyData_PropertyChanged(object sender, PropertyChangedEventArgs e)
   {
      switch (e.PropertyName)
      {
         case nameof(MyDataClass.IsSelected):
            OnPropertyChanged(nameof(Amount));
            break;
      }
   }

   public double Amount => MyDataList.Where(x => x.IsSelected).Sum(x => x.Quantity);
}