如何处理 MVVM 模式中的多个复选框?

发布于 2024-12-06 13:36:15 字数 2542 浏览 0 评论 0 原文

WPF 中的绑定复选框是常见问题,但我仍然没有找到适合初学者的示例代码。我在 WPF 中有复选框列表来选择最喜欢的运动名称。在我的例子中,复选框的数量是静态的。谁能告诉我如何实现 ViewModel 来解决这个问题?

FavoriteSportsView.xaml:

  <StackPanel Height="50" HorizontalAlignment="Left" VerticalAlignment="Top" 
  Width="150">

  <CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}"  
  Command="{Binding Path=SportsResponseCommand}" 
  CommandParameter="Football" 
  Content="Football" 
  Margin="5" />

  <CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" 
  Command="{Binding Path=SportsResponseCommand}" 
  CommandParameter="Hockey" 
  Content="Hockey" 
  Margin="5" />

  <CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" 
  Command="{Binding Path=SportsResponseCommand}" 
  CommandParameter="Golf" 
  Content="Golf" 
  Margin="5" />
  </StackPanel>

FavoriteSportsViewModel.cs

  public class FavoriteSportsViewModel.cs {

    //Since I am using the same IsChecked in all check box options, I found all check 
    //boxes gets either checked or unchecked when I just check or uncheck one option.
    //How do i resolve this issue? I don't think i need seprate IsChecked for each 
    //check box option.

    private bool _isChecked;
    public bool IsChecked{
      get {
           return _isChecked;
       }

      set { if (value != _isChecked) 
             _isChecked = value;
            this.OnPropertyChanged("IsChecked");
       }
    }


    //How do i detect parameter in this method?
    private ICommand _sportsResponseCommand;
    public ICommand SportsResponseCommand
    {
        get
        {
            if (_sportsResponseCommand== null)
                _sportsResponseCommand= new
                    RelayCommand(a => DoCollectSelectedGames(), p => true);
            return _sportsResponseCommand;
        }
        set
        {
            _sportsResponseCommand= value;

        }

    }

    private void DoCollectSelectedGames(){ 
      //Here i push all selected games in an array
    }


    public abstract class ViewModelBase : INotifyPropertyChanged
    {
       public event PropertyChangedEventHandler PropertyChanged;
       public void OnPropertyChanged(string propertyName)
       {
         if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
       }
    }

  }

我不知道如何在上面的 ViewModel 中执行以下操作: 1. 如何实现单一方法来处理我的所有选项? 2. 如何检测每个复选框以查看是否已选中 3. 如何使用CommandParameter? 4.如何正确实现SportsResponseCommand

Binding checkbox in WPF is common issue, but I am still not finding example code which is easy to follow for beginners. I have check box list in WPF to select favorite sports’ name. The number of checkboxes is static in my case. Can anyone show me how to implement ViewModel for this issue?

FavoriteSportsView.xaml:

  <StackPanel Height="50" HorizontalAlignment="Left" VerticalAlignment="Top" 
  Width="150">

  <CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}"  
  Command="{Binding Path=SportsResponseCommand}" 
  CommandParameter="Football" 
  Content="Football" 
  Margin="5" />

  <CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" 
  Command="{Binding Path=SportsResponseCommand}" 
  CommandParameter="Hockey" 
  Content="Hockey" 
  Margin="5" />

  <CheckBox IsChecked="{Binding IsChecked, Mode=TwoWay}" 
  Command="{Binding Path=SportsResponseCommand}" 
  CommandParameter="Golf" 
  Content="Golf" 
  Margin="5" />
  </StackPanel>

FavoriteSportsViewModel.cs

  public class FavoriteSportsViewModel.cs {

    //Since I am using the same IsChecked in all check box options, I found all check 
    //boxes gets either checked or unchecked when I just check or uncheck one option.
    //How do i resolve this issue? I don't think i need seprate IsChecked for each 
    //check box option.

    private bool _isChecked;
    public bool IsChecked{
      get {
           return _isChecked;
       }

      set { if (value != _isChecked) 
             _isChecked = value;
            this.OnPropertyChanged("IsChecked");
       }
    }


    //How do i detect parameter in this method?
    private ICommand _sportsResponseCommand;
    public ICommand SportsResponseCommand
    {
        get
        {
            if (_sportsResponseCommand== null)
                _sportsResponseCommand= new
                    RelayCommand(a => DoCollectSelectedGames(), p => true);
            return _sportsResponseCommand;
        }
        set
        {
            _sportsResponseCommand= value;

        }

    }

    private void DoCollectSelectedGames(){ 
      //Here i push all selected games in an array
    }


    public abstract class ViewModelBase : INotifyPropertyChanged
    {
       public event PropertyChangedEventHandler PropertyChanged;
       public void OnPropertyChanged(string propertyName)
       {
         if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
       }
    }

  }

I'm not sure how to do the following in above ViewModel:
1. How do I implement single method to handle all my options?
2. how do I detect each one of the checkboxes to see whether checked or not
3. How do i utlize CommandParameter?
4. How do i implement SportsResponseCommand correctly

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

呢古 2024-12-13 13:36:15

您的视图模型应如下所示:

public class MyViewModel : INotifyPropertyChanged
{
    //INotifyPropertyChanged implementation
    public event PropertyChangedEventHandler PropertyChanged;

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

    //bindable property
    private bool _football;
    public bool Football
    {
        get { return _football; }
        set
        {
            if (value != _football)
            {
                _football = value;
                this.OnPropertyChanged("Football");
            }
        }
    }

    //... and the same for Golf and Hockey
}

然后,通过设置 DataContext 属性(这很可能位于 Window 中)将视图模型与视图关联起来。 UserControl 代码隐藏,尽管有很多方法可以实现这一点)。

最后,更新您的绑定,使它们看起来像:

<CheckBox IsChecked="{Binding Football, Mode=TwoWay}"  
 Content="Football" 
 Margin="5" />

<CheckBox IsChecked="{Binding Golf, Mode=TwoWay}"   
 Content="Football" 
 Margin="5" />

作为最后的评论,您实际上不需要绑定 Command 属性 - 您只需编写需要在属性设置器中运行的任何代码即可视图模型。

Your view model should look something like this:

public class MyViewModel : INotifyPropertyChanged
{
    //INotifyPropertyChanged implementation
    public event PropertyChangedEventHandler PropertyChanged;

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

    //bindable property
    private bool _football;
    public bool Football
    {
        get { return _football; }
        set
        {
            if (value != _football)
            {
                _football = value;
                this.OnPropertyChanged("Football");
            }
        }
    }

    //... and the same for Golf and Hockey
}

Then you associate your view model with the view by setting the DataContext property (this will most likely be in the Window or UserControl code behind, though there are a lot of ways to achieve this).

Finally, update your bindings so that they look like:

<CheckBox IsChecked="{Binding Football, Mode=TwoWay}"  
 Content="Football" 
 Margin="5" />

<CheckBox IsChecked="{Binding Golf, Mode=TwoWay}"   
 Content="Football" 
 Margin="5" />

As a final comment, you shouldn't really need to bind the Command property - you can just write whatever code you need to run in the property setter on the view model.

悲喜皆因你 2024-12-13 13:36:15

我强烈建议您阅读此http://msdn.microsoft.com/en-我们/杂志/dd419663.aspx
我在下面描述了一个解决方案,我尝试不修改您的 XAML 代码,但这不是唯一的方法(或最佳方法),但包含所有必要的元素!

第一步,你需要你的模型,我称之为 Model_Sport

    public class Model_Sport : INotifyPropertyChanged
    {
        #region  Constructor

        public Model_Sport(string name, ICommand command)
        {
            Name = name;
            SportsResponseCommand = command;
        }

        #endregion

        static readonly PropertyChangedEventArgs _NameEventArgs = new PropertyChangedEventArgs("Name");
        private string _Name = null;
        public string Name
        {
            get { return _Name; }
            set
            {
                _Name = value;
                OnPropertyChanged(_NameEventArgs);
            }
        }

        static readonly PropertyChangedEventArgs _SportsResponseCommandEventArgs = new PropertyChangedEventArgs("SportsResponseCommand");
        private ICommand _SportsResponseCommand = null;
        public ICommand SportsResponseCommand
        {
            get { return _SportsResponseCommand; }
            set
            {
                _SportsResponseCommand = value;
                OnPropertyChanged(_SportsResponseCommandEventArgs);
            }
        }

        static readonly PropertyChangedEventArgs _IsCheckedEventArgs = new PropertyChangedEventArgs("IsChecked");
        private bool _IsChecked = false;
        public bool IsChecked
        {
            get { return _IsChecked; }
            set
            {
                _IsChecked = value;
                OnPropertyChanged(_IsCheckedEventArgs);
            }
        }


        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, eventArgs);
            }
        }

        #endregion
    }

现在你需要一种方法来委托你的命令“SportsResponseCommand”,DelegateCommand 对象将帮助你做到这一点

    public class DelegateCommand : ICommand
    {
        private readonly Action<object> _ExecuteMethod;
        private readonly Func< object, bool> _CanExecuteMethod;

        #region Constructors

        public DelegateCommand(Action<object>executeMethod, Func<object, bool> canExecuteMethod)
        {
            if (null == executeMethod)
            {
                throw new ArgumentNullException("executeMethod", "Delegate Command Delegates Cannot Be Null");
            }

            _ExecuteMethod = executeMethod;
            _CanExecuteMethod = canExecuteMethod;

        }

        public DelegateCommand(Action<object>executeMethod) : this(executeMethod, null) { }

        #endregion


        #region Methods

        public bool CanExecute(object parameter)
        {
            if (_CanExecuteMethod == null) return true;
            return _CanExecuteMethod(parameter);
        }

        public void Execute(object parameter)
        {
            if (_ExecuteMethod == null) return;
            _ExecuteMethod(parameter);
        }

        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        void ICommand.Execute(object parameter)
        {
            Execute(parameter);
        }

        #endregion

    }

现在“ViewModel”

    public class ViewModel
    {
        #region property

        public Dictionary<string, Model_Sport> Sports { get; set; }
        public DelegateCommand SportsResponseCommand { get; set; }

        #endregion

        public ViewModel()
        {
            Sports = new Dictionary<string, Model_Sport>();
            SportsResponseCommand = new DelegateCommand(p => execute_SportsResponseCommand(p));

            buildSports();
        }

        private void buildSports()
        {
            Model_Sport football = new Model_Sport("Football", SportsResponseCommand);
            Model_Sport golf = new Model_Sport("Golf", SportsResponseCommand);
            Model_Sport hockey = new Model_Sport("Hockey", SportsResponseCommand);

            football.IsChecked = true; // just for test

            Sports.Add(football.Name, football);
            Sports.Add(golf.Name, golf);
            Sports.Add(hockey.Name, hockey);
        }

        private void execute_SportsResponseCommand(object p)
        {
            // TODO :what ever you want
            MessageBox.Show(p.ToString());
        }

    }

现在查看
请记住为您的窗口设置数据上下文
公共主窗口()
{

        InitializeComponent();
        this.DataContext = new ViewModel();


    }

然后在 XAML 中

    <StackPanel  HorizontalAlignment="Left" VerticalAlignment="Top" >

        <CheckBox DataContext="{Binding Path=Sports[Football]}"
            IsChecked="{Binding IsChecked, Mode=TwoWay}"   
                    Command="{Binding Path=SportsResponseCommand}"  
                    CommandParameter="Football"    
                    Content="Football"   
                    Margin="5" />

        <CheckBox DataContext="{Binding Path=Sports[Hockey]}"
            IsChecked="{Binding IsChecked, Mode=TwoWay}"  
            Command="{Binding Path=SportsResponseCommand}"    
            CommandParameter="Hockey"    
            Content="Hockey"   
            Margin="5" />

        <CheckBox DataContext="{Binding Path=Sports[Golf]}" IsChecked="{Binding IsChecked, Mode=TwoWay}" 
                    Command="{Binding Path=SportsResponseCommand}"
                    CommandParameter="Golf"   
                    Content="Golf" 
                    Margin="5" />
    </StackPanel>

I highly recommend you to read this http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
I describe a solution below I tried to not modify your XAML code but it is not the only way (or the best approach) but contains all necessary elements!

At first step you need your model I call it Model_Sport

    public class Model_Sport : INotifyPropertyChanged
    {
        #region  Constructor

        public Model_Sport(string name, ICommand command)
        {
            Name = name;
            SportsResponseCommand = command;
        }

        #endregion

        static readonly PropertyChangedEventArgs _NameEventArgs = new PropertyChangedEventArgs("Name");
        private string _Name = null;
        public string Name
        {
            get { return _Name; }
            set
            {
                _Name = value;
                OnPropertyChanged(_NameEventArgs);
            }
        }

        static readonly PropertyChangedEventArgs _SportsResponseCommandEventArgs = new PropertyChangedEventArgs("SportsResponseCommand");
        private ICommand _SportsResponseCommand = null;
        public ICommand SportsResponseCommand
        {
            get { return _SportsResponseCommand; }
            set
            {
                _SportsResponseCommand = value;
                OnPropertyChanged(_SportsResponseCommandEventArgs);
            }
        }

        static readonly PropertyChangedEventArgs _IsCheckedEventArgs = new PropertyChangedEventArgs("IsChecked");
        private bool _IsChecked = false;
        public bool IsChecked
        {
            get { return _IsChecked; }
            set
            {
                _IsChecked = value;
                OnPropertyChanged(_IsCheckedEventArgs);
            }
        }


        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(PropertyChangedEventArgs eventArgs)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, eventArgs);
            }
        }

        #endregion
    }

Now you need a way to delegate your command “SportsResponseCommand”, DelegateCommand object will help you to do that

    public class DelegateCommand : ICommand
    {
        private readonly Action<object> _ExecuteMethod;
        private readonly Func< object, bool> _CanExecuteMethod;

        #region Constructors

        public DelegateCommand(Action<object>executeMethod, Func<object, bool> canExecuteMethod)
        {
            if (null == executeMethod)
            {
                throw new ArgumentNullException("executeMethod", "Delegate Command Delegates Cannot Be Null");
            }

            _ExecuteMethod = executeMethod;
            _CanExecuteMethod = canExecuteMethod;

        }

        public DelegateCommand(Action<object>executeMethod) : this(executeMethod, null) { }

        #endregion


        #region Methods

        public bool CanExecute(object parameter)
        {
            if (_CanExecuteMethod == null) return true;
            return _CanExecuteMethod(parameter);
        }

        public void Execute(object parameter)
        {
            if (_ExecuteMethod == null) return;
            _ExecuteMethod(parameter);
        }

        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute(parameter);
        }

        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        void ICommand.Execute(object parameter)
        {
            Execute(parameter);
        }

        #endregion

    }

Now “ViewModel”

    public class ViewModel
    {
        #region property

        public Dictionary<string, Model_Sport> Sports { get; set; }
        public DelegateCommand SportsResponseCommand { get; set; }

        #endregion

        public ViewModel()
        {
            Sports = new Dictionary<string, Model_Sport>();
            SportsResponseCommand = new DelegateCommand(p => execute_SportsResponseCommand(p));

            buildSports();
        }

        private void buildSports()
        {
            Model_Sport football = new Model_Sport("Football", SportsResponseCommand);
            Model_Sport golf = new Model_Sport("Golf", SportsResponseCommand);
            Model_Sport hockey = new Model_Sport("Hockey", SportsResponseCommand);

            football.IsChecked = true; // just for test

            Sports.Add(football.Name, football);
            Sports.Add(golf.Name, golf);
            Sports.Add(hockey.Name, hockey);
        }

        private void execute_SportsResponseCommand(object p)
        {
            // TODO :what ever you want
            MessageBox.Show(p.ToString());
        }

    }

Now View
Remember to set datacontext for your Window
public MainWindow()
{

        InitializeComponent();
        this.DataContext = new ViewModel();


    }

Then in XAML

    <StackPanel  HorizontalAlignment="Left" VerticalAlignment="Top" >

        <CheckBox DataContext="{Binding Path=Sports[Football]}"
            IsChecked="{Binding IsChecked, Mode=TwoWay}"   
                    Command="{Binding Path=SportsResponseCommand}"  
                    CommandParameter="Football"    
                    Content="Football"   
                    Margin="5" />

        <CheckBox DataContext="{Binding Path=Sports[Hockey]}"
            IsChecked="{Binding IsChecked, Mode=TwoWay}"  
            Command="{Binding Path=SportsResponseCommand}"    
            CommandParameter="Hockey"    
            Content="Hockey"   
            Margin="5" />

        <CheckBox DataContext="{Binding Path=Sports[Golf]}" IsChecked="{Binding IsChecked, Mode=TwoWay}" 
                    Command="{Binding Path=SportsResponseCommand}"
                    CommandParameter="Golf"   
                    Content="Golf" 
                    Margin="5" />
    </StackPanel>
浅蓝的眸勾画不出的柔情 2024-12-13 13:36:15

如果您只想在 IsChecked 更改时更新 ViewModel 中的属性,请将 IsChecked 的 Binding 替换为 ViewModel 中的布尔属性,该属性会在其“set”上引发 NotifyPropertyChanged ”。

现在,如果您想在每次 IsChecked 更改 3 个复选框之一时执行操作:

首先,将您的 CommandParameter 替换为

ViewModel 中的 "{BindingrelativeSource={RelativeSource Mode=Self}}" (应该实现 INotifyPropertyChanged),创建一个在参数中采用 CheckBox 的 ICommand (SportsResponseCommand)。

在命令的方法中,检查 CheckBox 的内容和“IsChecked”属性,然后使用它们执行操作。

如果您还有其他问题,请告诉我。

If you just want a property in your ViewModel to get updated when the IsChecked changes, replace the Binding for IsChecked to a boolean property in your ViewModel that raises NotifyPropertyChanged on its "set".

Now if you want to perform an action everytime IsChecked changes for one of the 3 CheckBoxes:

First of all, replace your CommandParameter with "{Binding RelativeSource={RelativeSource Mode=Self}}"

In your ViewModel (that should implement INotifyPropertyChanged), create an ICommand (SportsResponseCommand) that takes a CheckBox in parameter.

In the command's method, check for the Content of your CheckBox, and for the "IsChecked" property then do your stuff with them.

If you have further questions let me know.

你是我的挚爱i 2024-12-13 13:36:15

您可以使用此方法分配视图模型

 //for the view

partial class MainView:Window
 {
         InitializeComponent();
         this.DataContext=new MainViewModel();

 }

//ViewModel Code

public class MainViewModel: INotifyPropertyChanged
{
  //INotifyPropertyChanged implementation
  public event PropertyChangedEventHandler PropertyChanged;

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

//bindable property
private bool _football;
public bool Football
{
    get { return _football; }
    set
    {
        if (value != _football)
        {
            _football = value;
            this.OnPropertyChanged("Football");
        }
    }
}

//... and the same for Golf and Hockey
}`

,然后可以在 XAML 中将 Binding 实现为


命令=“{绑定路径=SportsResponseCommand}”
命令参数=“足球”
内容=“足球”
Margin="5" />


命令=“{绑定路径=SportsResponseCommand}”
命令参数=“足球”
内容=“足球”
边距=“5”/>

You can assign a view model by using this

 //for the view

partial class MainView:Window
 {
         InitializeComponent();
         this.DataContext=new MainViewModel();

 }

//ViewModel Code

public class MainViewModel: INotifyPropertyChanged
{
  //INotifyPropertyChanged implementation
  public event PropertyChangedEventHandler PropertyChanged;

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

//bindable property
private bool _football;
public bool Football
{
    get { return _football; }
    set
    {
        if (value != _football)
        {
            _football = value;
            this.OnPropertyChanged("Football");
        }
    }
}

//... and the same for Golf and Hockey
}`

and then you can implement Binding in XAML as

<CheckBox IsChecked="{Binding Football, Mode=TwoWay}"
Command="{Binding Path=SportsResponseCommand}"
CommandParameter="Football"
Content="Football"
Margin="5" />

<CheckBox IsChecked="{Binding Golf, Mode=TwoWay}"
Command="{Binding Path=SportsResponseCommand}"
CommandParameter="Football"
Content="Football"
Margin="5" />

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文