WPF MVVM中的绑定复选框问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WPF MVVM中的绑定复选框问题相关的知识,希望对你有一定的参考价值。

我在从VM获取复选框IsChecked绑定值的值时遇到问题。 (我正在使用MVVM Light)。

我的问题:当复选框IsChecked被更改时,它不会回复到我绑定到的VM属性。

下面是代码。

我有一个带有布尔值的类(在类文件中)。

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; }
}

这是我的复选框将绑定到的属性(在VM中)。

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

以下是我的“全选”复选框(在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 };
        }
    }
}

最后,下面是我绑定的XAML。

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

这是我在图片中所做的。 enter image description here

关于我哪里做错了的任何建议?谢谢。

答案

我不知道你的viewmodel基类被调用了什么,所以我只使用了自己的。我不知道你的Set()方法是如何工作的;这可能需要你的一点调整。那一定是你的工作;轮到你了。我这样写是因为向你解释逻辑比编写代码需要更长的时间。您应该阅读此代码并理解它,而不是简单地将其粘贴到您的项目中。

请注意,我使用传统的C#命名约定编写了这个。布尔属性不再具有b前缀。这意味着您必须从XAML中绑定的路径中删除该前缀。

另请注意,我将Rights_All重命名为All,并将其移至另一个视图模型。它现在是Rights视图模型的成员。这也需要更改绑定。

您应该考虑使用Flags枚举作为您的权利。这将简化代码,并使将来更容易添加其他权限。

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;
                }
            }
        }
    }
}
另一答案

以下是我的解决方案的答案(在得到@Ed Plunkett和@zaitsman的建议之后)我实现了INotifyProperty(通过使用MVVM Light方式)到我的模型中的一个类。

对于我的模型类。

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;
    }

对于我的VM。

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

而对于我的观点(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}" />

以上是关于WPF MVVM中的绑定复选框问题的主要内容,如果未能解决你的问题,请参考以下文章

WPF 事件实现MVVM中的Command绑定

使用 MVVM 绑定 WPF DataGridComboBoxColumn

WPF MVVM模式如何控制DataGrid的列隐藏和显示

使用 MVVM 和数据绑定更改每个片段中的工具栏标题

在 MVVM 中的 Datagrid 中绑定 WPF 组合框不保存更改

C#:WPF MVVM 中的按钮绑定