开发者

How can I handle multiple CheckBoxes in the MVVM pattern?

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 SportsResponse开发者_Go百科Command
    {
        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


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.


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>


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.


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜