在WPF表单中使用MVVM返回绑定的复选框值



我有一个由字符串和数组组成的对象。根据所选字符串值,字符串填充ComboBox,数组填充ListView。CCD_ 3的每一行由CCD_ 4和CCD_。

在提交时,我希望能够验证哪些项目已被选择进行进一步处理,但在使用MVVM方法时会出现断开连接的情况。我目前有提交ButtonDataContext绑定到ListView,但在提交时只返回第一个值(我需要将所选值保存到我假设的列表中,但我不确定在哪里(。我在模型中添加了IsSelected属性,我认为这是第一步,但在那之后,我一直在抓救命稻草。

型号

namespace DataBinding_WPF.Model
{
public class ExampleModel { }
public class Example : INotifyPropertyChanged
{
private string _name;
private string[] _ids;
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set
{
if (_isSelected != value)
{
_isSelected = value;
RaisePropertyChanged("IsSelected");
}
}
}
public string Name
{
get => _name;
set
{
if (_name != value)
{
_name = value;
RaisePropertyChanged("Name");
}
}
}
public string[] IDs
{
get => _ids;
set
{
if (_ids != value)
{
_ids = value;
RaisePropertyChanged("IDs");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new 
PropertyChangedEventArgs(property));
}
}
}
}

ViewModel

namespace DataBinding_WPF.ViewModel
{
public class ExampleViewModel : INotifyPropertyChanged
{
public ObservableCollection<Example> Examples
{
get;
set;
}
// SelectedItem in the ComboBox
// SelectedItem.Ids will be ItemsSource for the ListBox
private Example _selectedItem;
public Example SelectedItem
{
get => _selectedItem;
set
{
_selectedItem = value;
RaisePropertyChanged(nameof(SelectedItem));
}
}
// SelectedId in ListView
private string _selectedId;
public string SelectedId
{
get => _selectedId;
set
{
_selectedId = value;
RaisePropertyChanged(nameof(SelectedId));
}
}
private string _selectedCheckBox;
public string IsSelected
{
get => _selectedCheckBox;
set
{
_selectedCheckBox = value;
RaisePropertyChanged(nameof(IsSelected));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs(property));
}
}
public void LoadExample()  
{
ObservableCollection<Example> examples = new ObservableCollection<Example>();
examples.Add(new Example { Name = "Mark", IDs = new string[] { "123", "456" }, IsSelected = false });
examples.Add(new Example { Name = "Sally", IDs = new string[] { "789", "101112" }, IsSelected = false });
Examples = examples;
}
/* BELOW IS A SNIPPET I ADDED FROM AN EXAMPLE I FOUND ONLINE BUT NOT SURE IF IT'S NEEDED */
private ObservableCollection<Example> _bindCheckBox;
public ObservableCollection<Example> BindingCheckBox
{
get => _bindCheckBox;
set
{
_bindCheckBox = value;
RaisePropertyChanged("BindingCheckBox");
}
}
}
}

查看

<UserControl x:Class = "DataBinding_WPF.Views.StudentView" 
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" 
xmlns:mc = "http://schemas.openxmlformats.org/markup-compatibility/2006" 
xmlns:d = "http://schemas.microsoft.com/expression/blend/2008" 
xmlns:local = "clr-namespace:DataBinding_WPF" 
mc:Ignorable = "d" 
d:DesignHeight = "300" d:DesignWidth = "300">
<Grid>
<StackPanel HorizontalAlignment = "Left" >
<ComboBox HorizontalAlignment="Left"   
VerticalAlignment="Top"   
Width="120"   
ItemsSource="{Binding Path=Examples}"    
SelectedItem="{Binding SelectedItem}"  
DisplayMemberPath="Name"/>
<ListView x:Name="myListView" 
ItemsSource="{Binding SelectedItem.IDs}"
DataContext="{Binding DataContext, ElementName=submit_btn}"
SelectedItem="{Binding SelectedId}" 
Height="200" Margin="10,50,0,0" 
Width="Auto"
VerticalAlignment="Top"
Background="AliceBlue">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<CheckBox 
Name="myCheckBox"                            
IsChecked="{Binding IsSelected,
RelativeSource={RelativeSource AncestorType=ListViewItem}}"
Margin="5, 0"/>
<TextBlock Text="{Binding}" FontWeight="Bold" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button HorizontalAlignment="Left" Height="20" Width="100"
Click="Submit" x:Name="submit_btn">Submit</Button>
</StackPanel>
</Grid>
</UserControl>

查看.cs

namespace DataBinding_WPF.Views
{
/// <summary>
/// Interaction logic for StudentView.xaml
/// </summary>
public partial class StudentView : UserControl
{
public StudentView()
{
InitializeComponent();
}
private void Submit(object sender, EventArgs e)
{
var selectedItems = ((Button)sender).DataContext;
// process each selected item
// foreach (var selected in ....) { }
}
}
}

ListView控件已将选定项集合公开为属性SelectedItems

private void Submit(object sender, RoutedEventArgs e)
{
var selectedIds = myListView.SelectedItems.Cast<string>().ToList();
// ...do something with the items.
}

但是,我怀疑您是否希望在代码中这样做,而是在视图模型中这样做。为此,WPF提供了命令的概念。

  • MVVM-命令、RelayCommands和EventToCommand

您需要的是一个中继命令或委托命令(不同框架的名称不同(。它封装了一个应该执行的方法,例如单击按钮,以及一个确定命令是否可以作为可以绑定在视图中的对象执行的方法。不幸的是,WPF并没有提供开箱即用的实现,因此您要么像这里一样复制一个实现,要么使用已经提供了实现的MVVM框架,例如Microsoft MVVM Tookit。

您将在ExampleViewModel中公开类型为ICommand的属性Submit,并在构造函数中使用委托给要执行的方法的RelayCommand<T>实例对其进行初始化。

public class ExampleViewModel : INotifyPropertyChanged
{
public ExampleViewModel()
{
Submit = new RelayCommand<IList>(ExecuteSubmit);
}
public RelayCommand<IList> Submit { get; }
// ...other code.
private void ExecuteSubmit(IList selectedItems)
{
// ...do something with the items.
var selectedIds = selectedItems.Cast<string>().ToList();
return;
}
}

在您的视图中,您将删除Click事件处理程序,并将Submit属性绑定到ButtonCommand属性。您还可以将ListViewSelectedItems属性绑定到CommandParameter属性,以便在执行时将所选项目传递给命令。

<Button HorizontalAlignment="Left"
Height="20"
Width="100"
x:Name="submit_btn"
Command="{Binding Submit}"
CommandParameter="{Binding SelectedItems, ElementName=myListView}">Submit</Button>

此外,还有一些关于XAML的备注。

  • XAML中控件的名称应为Pascal大小写,以大写字母开头。

  • 您应该从ListView中完全删除DataContext绑定,因为它无论如何都会自动接收与Button相同的数据上下文。

    DataContext="{Binding DataContext, ElementName=submit_btn}"
    
  • 通过对层次数据使用Master/Detail模式,您可以避免在ExampleViewModel中公开和绑定SelectedItem属性。

    <Grid>
    <StackPanel HorizontalAlignment = "Left" >
    <ComboBox HorizontalAlignment="Left"   
    VerticalAlignment="Top"   
    Width="120"   
    ItemsSource="{Binding Path=Examples}"   
    IsSynchronizedWithCurrentItem="True"
    DisplayMemberPath="Name"/>
    <ListView ItemsSource="{Binding Examples/IDs}"
    SelectedItem="{Binding SelectedId}" 
    Height="200" Margin="10,50,0,0" 
    Width="Auto"
    VerticalAlignment="Top"
    Background="AliceBlue">
    <ListView.ItemTemplate>
    <DataTemplate>
    <StackPanel Orientation="Horizontal" >
    <CheckBox Name="myCheckBox"                            
    IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListViewItem}}"
    Margin="5, 0"/>
    <TextBlock Text="{Binding}"
    FontWeight="Bold" />
    </StackPanel>
    </DataTemplate>
    </ListView.ItemTemplate>
    </ListView>
    <Button HorizontalAlignment="Left"
    Height="20"
    Width="100"
    Command="{Binding Submit}"
    CommandParameter="{Binding SelectedItems, ElementName=myListView}">Submit</Button>
    </StackPanel>
    </Grid>
    

如果视图的数据上下文绑定到视图,则从ListView中删除DataContext。

您可以删除项目模板,转而使用GridView,如:

<ListView.View>
<GridView >
<GridViewColumn Header="Selected" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected}" Content="{Binding Name}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>

由于ItemSource是一个Observable集合,因此有几个选项可以监视复选框中的更改:

  1. 将事件处理程序添加到集合的项更改事件,然后可以将Name或集合索引添加到本地集合。例如示例[e.CollectionIndex].Name
  2. 可替换地在可观察的集合上迭代并选择那些Selected="的实例;真">

最新更新