简体   繁体   中英

Binding checkbox issue in WPF MVVM

I am facing a problem in getting the value from VM for checkbox IsChecked binding value. (I'm using MVVM Light).

My issue : When checkbox IsChecked is changed, it is not firing back to my VM property that I bind to.

Below is the code.

I have a class with boolean values (in a class file).

public class Rights
{
    public bool bSales { get; set; }
    public bool bProduct { get; set; }
    public bool bZone { get; set; }
    public bool bPercentage { get; set; }
    public bool bUser { get; set; }
}

And this is the property that my checkboxes will bind to (in VM).

private Rights user_Rights;
public Rights User_Rights
{
    get { return user_Rights; }
    set { Set(ref user_Rights, value); }
}

And below is the property for my 'Select All' check box (in VM).

private bool? rights_All;
public bool? Rights_All
{
    get { return rights_All; }
    set
    {
        Set(ref rights_All, value);

        if (value == true)
        {
            User_Rights = new Rights() { bSales = true, bProduct = true, bPercentage = true, bZone = true, bUser = true };
        }
        else if(value == false)
        {
            User_Rights = new Rights() { bSales = false, bProduct = false, bPercentage = false, bZone = false, bUser = false };
        }
    }
}

And finally, below is my XAML for the binding.

<CheckBox Content="Sales PIC" IsChecked="{Binding User_Rights.bSales,Mode=TwoWay}" />
<CheckBox Content="Product" IsChecked="{Binding User_Rights.bProduct,Mode=TwoWay}" />
<CheckBox Content="Zone" IsChecked="{Binding User_Rights.bZone,Mode=TwoWay}" />
<CheckBox Content="Percentage" IsChecked="{Binding User_Rights.bPercentage}" />
<CheckBox Content="User" IsChecked="{Binding User_Rights.bUser}" />
<CheckBox Content="Select All" IsChecked="{Binding Rights_All}" />

Here is what I am doing in picture. 在此处输入图片说明

Any suggestion on where did I do wrong? Thanks.

I don't know what your viewmodel base class is called, so I just used my own. I don't know how your Set() method works; this may take a little adaptation on your part. That'll have to be your job; it's your turn. I wrote this because explaining the logic to you would take longer than writing the code. You should read this code and understand it, rather than simply pasting it into your project.

Note that I've written this using conventional C# naming conventions. The boolean properties no longer have the b prefix. This means you have to remove that prefix from the paths in the bindings in your XAML.

Note also that I renamed Rights_All to All , and moved it to a different viewmodel . It is now a member of the Rights viewmodel. This also will require a change to your bindings.

You should consider using a Flags enum for your rights. This would simplify the code a bit, and make it easier to add additional rights in the future.

public class Rights : ViewModelBase
{
    private bool _sales;
    public bool Sales {
        get { return _sales; }
        set { SetRightFlag(ref _sales, value); }
    }

    private bool _product;
    public bool Product
    {
        get { return _product; }
        set { SetRightFlag(ref _product, value); }
    }

    private bool _zone;
    public bool Zone
    {
        get { return _zone; }
        set { SetRightFlag(ref _zone, value); }
    }

    private bool _percentage;
    public bool Percentage
    {
        get { return _percentage; }
        set { SetRightFlag(ref _percentage, value); }
    }

    private bool _user;
    public bool User
    {
        get { return _user; }
        set { SetRightFlag(ref _user, value); }
    }

    //  This logic needs to happen in five different setters, so I put it in a 
    //  method. 
    private bool SetRightFlag(ref bool field, bool value, [System.Runtime.CompilerServices.CallerMemberName] string propName = null)
    {
        if (field != value)
        {
            Set(ref field, value, propName);
            UpdateAll();
            return true;
        }
        return false;
    }

    //  I made this its own method as well, for cleanliness and clarity, even though 
    //  it's only called once. 
    protected void UpdateAll()
    {
        //  Don't call the All setter from here, because it has side effects.
        if (User && Percentage && Zone && Product && Sales)
        {
            _all = true;
            OnPropertyChanged(nameof(All));
        }
        else if (!User && !Percentage && !Zone && !Product && !Sales)
        {
            _all = false;
            OnPropertyChanged(nameof(All));
        }
        else if (All.HasValue)
        {
            _all = null;
            OnPropertyChanged(nameof(All));
        }
    }

    private bool? _all = null;
    public bool? All
    {
        get { return _all; }
        set {
            if (_all != value)
            {
                Set(ref _all, value);
                if (_all.HasValue)
                {
                    User = Percentage = Zone = Product = Sales = (bool)_all;
                }
            }
        }
    }
}

Here is the answer to my solution (after getting suggestion from @Ed Plunkett and @zaitsman) I implemented INotifyProperty (by using MVVM Light way) to one of the classes in my Model.

For my Model Class.

public class Rights: ViewModelBase
{
    public Rights()
    {
        _bSalesPIC = false;
        _bZone = false;
        ... (etc)
        _bAll = false;
    }

    private bool _bSalesPIC;
    public bool bSalesPIC
    {
        get { return _bSalesPIC; }
        set
        {
            Set(ref _bSalesPIC, value);
            TriggerAll();
        }
    }

    private bool _bZone;
    public bool bZone
    {
        get { return _bZone; }
        set
        {
            Set(ref _bZone, value);
            TriggerAll();
        }
    }

    private bool? _bAll;
    public bool? bAll
    {
        get { return _bAll; }
        set
        {
            Set(ref _bAll , value);

            if (value == true)
            {
                _bSalesPIC = true;
                _bZone = true;
                RaisePropertyChanged("bSalesPIC");
                RaisePropertyChanged("bZone");
            }
            else if (value == false)
            {
                _bSalesPIC = false;
                _bZone = false;

                RaisePropertyChanged("bSalesPIC");
                RaisePropertyChanged("bZone");
            }
        }
    }

    private void TriggerAll()
    {
        if (_bSalesPIC && _bZone && etc)
            bAll = true;
        else if (!_bSalesPIC && !_bZone && etc)
            bAll = false;
        else
            bAll = null;
    }

For my VM.

    private Rights user_Rights;
    public Rights User_Rights
    {
        get { return user_Rights; }
        set { Set(ref user_Rights, value); }
    }

And for my View (XAML).

    <CheckBox Content="Sales PIC" IsChecked="{Binding User_Rights.bSalesPIC}" />
    <CheckBox Content="Sales Input" IsChecked="{Binding User_Rights.bSalesInput}" />
    <CheckBox Content="Product" IsChecked="{Binding User_Rights.bProduct}" />
    <CheckBox Content="Zone" IsChecked="{Binding User_Rights.bZone}" />
    <CheckBox Content="Percentage" IsChecked="{Binding User_Rights.bPercentage}" />
    <CheckBox Content="User" IsChecked="{Binding User_Rights.bUser}" />
    <CheckBox Content="Select All" IsChecked="{Binding User_Rights.bAll}" />

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM