如何处理MVVM模式中的多个CheckBox?

时间:2011-09-26 15:09:51

标签: c# wpf xaml mvvm checkbox

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

4 个答案:

答案 0 :(得分:3)

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

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属性将视图模型与视图相关联(这很可能会出现在WindowUserControl代码中,尽管有很多方法实现这一点)。

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

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

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

作为最终评论,您不应该真正需要绑定Command属性 - 您只需编写在视图模型上的属性设置器中运行所需的任何代码。

答案 1 :(得分:1)

我强烈建议您阅读此http://msdn.microsoft.com/en-us/magazine/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());
        }

    }

现在查看 记得为您的Window设置datacontext public MainWindow()         {

        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>

答案 2 :(得分:0)

如果您只是希望ViewModel中的属性在IsChecked更改时更新,请将Iskecked的Binding替换为ViewModel中的boolean属性,该属性会在其“set”上引发NotifyPropertyChanged。< / p>

现在,如果你想在每次IsChecked改变3个CheckBoxes之一时执行一个动作:

首先,将您的CommandParameter替换为"{Binding RelativeSource={RelativeSource Mode=Self}}"

在ViewModel(应该实现INotifyPropertyChanged)中,创建一个ICommand(SportsResponseCommand),它在参数中包含CheckBox。

在命令的方法中,检查CheckBox的内容,以及“IsChecked”属性,然后用它们做你的东西。

如果您有其他问题,请与我们联系。

答案 3 :(得分:0)

您可以使用此

指定视图模型
 //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

<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" />