C# 属性更改时,MVVM中的DataGridCheckBoxColumn不会更新

C# 属性更改时,MVVM中的DataGridCheckBoxColumn不会更新,c#,wpf,mvvm,C#,Wpf,Mvvm,在我正在开发的一个应用程序中,我有两个关键布尔值,用于指示订单是否可以被批准,以及订单是否已被批准。我想要的是,当ObservableCollection对象中的值更改时,两列上的复选框都会更新,以便用户知道订单是否已准备好批准,以及是否已批准 模型中的值会正确更新,viewmodel中的approve按钮命令会按预期设置approved boolean,但是我无法使复选框动态更新。目前,它仅通过更改为其他视图,然后再更改回订单视图进行更新 我仍在学习数据绑定和MVVM模型,所以我尝试过的部分内

在我正在开发的一个应用程序中,我有两个关键布尔值,用于指示订单是否可以被批准,以及订单是否已被批准。我想要的是,当ObservableCollection对象中的值更改时,两列上的复选框都会更新,以便用户知道订单是否已准备好批准,以及是否已批准

模型中的值会正确更新,viewmodel中的approve按钮命令会按预期设置approved boolean,但是我无法使复选框动态更新。目前,它仅通过更改为其他视图,然后再更改回订单视图进行更新

我仍在学习数据绑定和MVVM模型,所以我尝试过的部分内容是实验。到目前为止,我已经尝试了两种不同的方法来达到我想要的行为,但都没有奏效。以下是视图模型代码:

<UserControl x:Class="US_Wholesale_App_V2.Views.SPSOrderView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:US_Wholesale_App_V2.Views"
             xmlns:viewModels="clr-namespace:US_Wholesale_App_V2.ViewModels"
             xmlns:models="clr-namespace:US_Wholesale_App_V2.Models"
             mc:Ignorable="d" 
             d:DesignHeight="800" d:DesignWidth="1100">

    <UserControl.Resources>
        <DataTemplate DataType="{x:Type viewModels:SPSLineVM}">
            <local:SPSLineView/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type viewModels:CustomerInfoVM}">
            <local:CustomerInfoView/>
        </DataTemplate>
    </UserControl.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="AUTO" MinWidth="312"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="AUTO"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel
            Orientation="Horizontal"
            HorizontalAlignment="Right"
            Grid.Row="0"
            Grid.Column="0">
            <Button
                Content="Lines"
                Height="20"
                Width="75"
                Margin="5,5,5,10"
                Command="{Binding SPSNavCommand}"
                CommandParameter="Lines"/>
            <Button
                Content="Shipping"
                Height="20"
                Width="75"
                Margin="5,5,5,10"
                Command="{Binding SPSNavCommand}"
                CommandParameter="Shipping"/>
        </StackPanel>

        <DataGrid
            x:Name="OrderHeaderGrid"
            AutoGenerateColumns="False"
            Margin="5"
            Grid.Row="1"
            Grid.Column="0"
            ItemsSource="{Binding SpsData}"
            SelectedItem="{Binding SelectedOrder, Mode=TwoWay}"
            CanUserAddRows="False">
            <DataGrid.Columns>
                <DataGridTextColumn
                    Header="PO Number"
                    x:Name="PONumberCol"
                    Binding="{Binding PONumber}"/>
                <DataGridTextColumn
                    Header="Customer"
                    x:Name="CustomerCol"
                    Binding="{Binding Customer}"/>
                <DataGridTextColumn
                    Header="Retailer PO Number"
                    x:Name="RetailersPONumberCol"
                    Binding="{Binding RetailersPONumber}"/>
                <DataGridTextColumn
                    Header="DC Code"
                    x:Name="DCCodeCol"
                    Binding="{Binding DCCode}"/>
                <DataGridCheckBoxColumn
                    Binding="{Binding IsValid, Mode=OneWay}"
                    IsReadOnly="True"/>
                <DataGridTemplateColumn
                    Header="Approve">
                    <DataGridTemplateColumn.CellTemplate>
                        <DataTemplate>
                            <Button
                                Content="Approve"
                                Command="{Binding DataContext.ApproveButton, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGrid}}}"
                                CommandParameter="{Binding SelectedOrder}"/>
                        </DataTemplate>
                    </DataGridTemplateColumn.CellTemplate>
                </DataGridTemplateColumn>
                <DataGridCheckBoxColumn
                    Binding="{Binding IsApproved, Mode=OneWay}"
                    IsReadOnly="True"/>
            </DataGrid.Columns>
        </DataGrid>
        <ContentControl
            Grid.Row="1"
            Grid.Column="1"
            Margin="5">
            <ContentControl Content="{Binding SPSCurrentVM}"/>
        </ContentControl>
    </Grid>
</UserControl>

如果有用的话,这里还有模型的代码。RGLibrary项目只是我需要的常见类的库,比如SQL连接器或FTP方法

using RGLibrary;

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.ComponentModel;
using System.Threading.Tasks;

namespace US_Wholesale_App_V2.Models
{
    public class SPSOrderModel : Observable_Object, IDataErrorInfo
    {
        private bool _IsApproved;
        private string _PONumber;
        private string _RetailersPONumber;
        private string _PODate;
        private string _ShipDate;
        private string _CancelDate;
        private string _POPurpose;
        private string _POType;
        private string _VendorNumber;
        private string _Customer;
        private string _DCCode;
        private string _DepartmentNo;
        private ObservableCollection<SPSLineModel> _Lines;
        private BillingCustomerModel _BillingInfo;
        private ShippingCustomerModel _ShippingInfo;
        private ShippingChargesModel _Charges;


        public bool IsApproved
        {
            get { return _IsApproved; }
            set { _IsApproved = value; }
        }
        public string PONumber
        {
            get { return _PONumber; }
            set { _PONumber = value; }
        }
        public string RetailersPONumber
        {
            get { return _RetailersPONumber; }
            set { _RetailersPONumber = value; }
        }
        public string PODate
        {
            get { return _PODate; }
            set { _PODate = value; }
        }
        public string ShipDate
        {
            get { return _ShipDate; }
            set { _ShipDate = value; }
        }
        public string CancelDate
        {
            get { return _CancelDate; }
            set { _CancelDate = value; }
        }
        public string POPurpose
        {
            get { return _POPurpose; }
            set { _POPurpose = value; }
        }
        public string POType
        {
            get { return _POType; }
            set { _POType = value; }
        }
        public string VendorNumber
        {
            get { return _VendorNumber; }
            set { _VendorNumber = value; }
        }
        public string Customer
        {
            get { return _Customer; }
            set { _Customer = value; }
        }
        public string DCCode
        {
            get { return _DCCode; }
            set { _DCCode = value; }
        }
        public string DepartmentNo
        {
            get { return _DepartmentNo; }
            set { _DepartmentNo = value; }
        }
        public ObservableCollection<SPSLineModel> Lines
        {
            get { return _Lines; }
            set { _Lines = value; }
        }
        public BillingCustomerModel BillingInfo
        {
            get { return _BillingInfo; }
            set { _BillingInfo = value; }
        }
        public ShippingCustomerModel ShippingInfo
        {
            get { return _ShippingInfo; }
            set { _ShippingInfo = value; }
        }
        public ShippingChargesModel Charges
        {
            get { return _Charges; }
            set { _Charges = value; }
        }

        public string Error
        {
            get
            {
                var errorMsg = new StringBuilder();
                if (string.IsNullOrWhiteSpace(DepartmentNo))
                    errorMsg.AppendLine("The department number for this order cannot be blank");
                if (string.IsNullOrWhiteSpace(PODate))
                    errorMsg.AppendLine("You must enter a purchase order date");
                else if (!DateValidate("PODate", PODate))
                    errorMsg.AppendLine("The PO date format must be MM/DD/YYY");
                if (string.IsNullOrWhiteSpace(CancelDate))
                    errorMsg.AppendLine("You must enter a cancellation date");
                else if (!DateValidate("CancelDate", CancelDate))
                    errorMsg.AppendLine("The cancellation date format must be MM/DD/YYY");
                if (string.IsNullOrWhiteSpace(ShipDate))
                    errorMsg.AppendLine("You must enter a shipment date");
                else if (!DateValidate("ShipDate", ShipDate))
                    errorMsg.AppendLine("The shipment date format must be MM/DD/YYY");
                if (string.IsNullOrWhiteSpace(DCCode))
                    errorMsg.AppendLine("You must enter a DC code");
                if (Lines.Any(p => !p.IsValid))
                    errorMsg.AppendLine("You must correct all errors in the purchase order lines, or remove any lines with errors");
                if (SumDispatch(Lines) < 1)
                    errorMsg.AppendLine("The total dispatch quantity must be at least 1");
                return errorMsg.ToString();
            }
        }

        public bool IsValid
        {
            get
            {
                bool check = true;
                if (IsHeaderValid == false)
                    check = false;
                if (BillingValid == false)
                    check = false;
                if (ShippingValid == false)
                    check = false;
                if (ChargesValid == false)
                    check = false;
                if (LinesValid == false)
                    check = false;
                return check;
            }
        }

        public bool BillingValid
        {
            get
            {
                if (BillingInfo != null)
                    return BillingInfo.IsValid;
                else
                    return false;
            }
        }

        public bool ShippingValid
        {
            get
            {
                if (ShippingInfo != null)
                    return ShippingInfo.IsValid;
                else
                    return false;
            }
        }

        public bool ChargesValid
        {
            get
            {
                if (Charges != null)
                    return Charges.IsValid;
                else
                    return false;
            }
        }

        public bool LinesValid
        {
            get
            {
                bool check = true;
                if (Lines.Any(l => l.IsValid == false))
                    check = false;
                return check;
            }
        }

        public bool IsHeaderValid
        {
            get
            {
                bool check = true;
                if (string.IsNullOrWhiteSpace(PODate))
                    check = false;
                if (!DateValidate("PODate", PODate))
                    check = false;
                if (string.IsNullOrWhiteSpace(CancelDate))
                    check = false;
                if (!DateValidate("CancelDate", CancelDate))
                    check = false;
                if (string.IsNullOrWhiteSpace(ShipDate))
                    check = false;
                if (!DateValidate("ShipDate", ShipDate))
                    check = false;
                if (string.IsNullOrWhiteSpace(DCCode))
                    check = false;
                if (Lines.Any(p => !p.IsValid))
                    check = false;
                if (SumDispatch(Lines) < 1)
                    check = false;
                if (string.IsNullOrWhiteSpace(DepartmentNo))
                    check = false;
                return check;
            }
        }

        public string this[string name]
        {
            get
            {
                string result = null;
                if (name == "PODate")
                {
                    if (string.IsNullOrWhiteSpace(PODate))
                        result = "You must enter a date";
                    else if (!DateValidate("PODate", PODate))
                        result = "Date format must be MM/DD/YYY";
                }
                if (name == "CancelDate")
                {
                    if (string.IsNullOrWhiteSpace(CancelDate))
                        result = "You must enter a date";
                    else if (!DateValidate("CancelDate", CancelDate))
                        result = "Date format must be MM/DD/YYY";
                }
                if (name == "ShipDate")
                {
                    if (string.IsNullOrWhiteSpace(ShipDate))
                        result = "You must enter a date";
                    else if (!DateValidate("ShipDate", ShipDate))
                        result = "Date format must be MM/DD/YYY";
                }
                if (name == "DCCode")
                {
                    if (string.IsNullOrWhiteSpace(DCCode))
                        result = "You must enter a DC code";
                }
                return result;
            }
        }


        private static bool DateValidate(string sender, object value)
        {
            bool check = true;
            if (sender == "PODate" || sender == "CancelDate" || sender == "ShipDate")
            {
                string _date = value.ToString();
                string _expression = @"(([0]\d)|(11|12))\/(([012]\d)|(30|31))\/(20)\d{2}";
                Regex _Regex = new Regex(_expression);
                Match _match = _Regex.Match(_date);
                if (!_match.Success)
                    check = false;
            }

            return check;
        }

        private static int SumDispatch(ObservableCollection<SPSLineModel> lines)
        {
            int dispatchQty = 0;
            foreach (SPSLineModel line in lines)
            {
                dispatchQty += line.DispatchQty;
            }
            return dispatchQty;
        }
    }
}

使用RGLibrary;
使用制度;
使用System.Collections.Generic;
使用System.Collections.ObjectModel;
使用System.Linq;
使用系统文本;
使用System.Text.RegularExpressions;
使用系统组件模型;
使用System.Threading.Tasks;
名称空间US_批发_应用_V2.Models
{
公共类SPSOrderModel:Observable_对象,IDataErrorInfo
{
私人文件获得批准;
私有字符串_PONumber;
私有字符串_retailersponnumber;
私有字符串_PODate;
私有字符串_ShipDate;
私有字符串_CancelDate;
专用字符串_POPurpose;
私有字符串_POType;
私有字符串_VendorNumber;
私人字符串(用户),;
私有字符串\u DCCode;
私人字符串(部门编号;;
私人可观测采集线;
私人计费客户模式(BillingInfo);;
私人ShippingCustomerModel\u ShippingInfo;
私人运输费用模式费用;
公共文件被批准
{
获取{return\u已批准;}
设置{u IsApproved=value;}
}
公共字符串PONumber
{
获取{return\u PONumber;}
设置{u PONumber=value;}
}
公共字符串RetailerSponNumber
{
获取{return\u retailersponnumber;}
设置{u retailersponnumber=value;}
}
公共字符串播客
{
获取{return\u PODate;}
设置{u PODate=value;}
}
公共字符串发货日期
{
获取{return\u ShipDate;}
设置{u ShipDate=value;}
}
公共字符串取消日期
{
获取{return\u CancelDate;}
设置{u CancelDate=value;}
}
公共字符串用途
{
获取{return\u POPurpose;}
设置{u POPurpose=value;}
}
公共字符串类型
{
获取{return\u POType;}
设置{u POType=value;}
}
公共字符串VendorNumber
{
获取{return\u VendorNumber;}
设置{u VendorNumber=value;}
}
公共字符串客户
{
获取{return\u Customer;}
设置{u Customer=value;}
}
公共字符串DCCode
{
获取{return\u DCCode;}
设置{u DCCode=value;}
}
公共字符串部门号
{
获取{return\u DepartmentNo;}
设置{u DepartmentNo=value;}
}
公共可观测收集线
{
获取{返回_行;}
设置{u行=值;}
}
公共计费客户模式计费信息
{
获取{return\u BillingInfo;}
设置{u BillingInfo=value;}
}
公共发货客户模型发货信息
{
获取{return\u ShippingInfo;}
设置{u ShippingInfo=value;}
}
公共运输费用模式费用
{
获取{return\u Charges;}
设置{u Charges=value;}
}
公共字符串错误
{
得到
{
var errorMsg=新的StringBuilder();
if(string.IsNullOrWhiteSpace(DepartmentNo))
errorMsg.AppendLine(“此订单的部门号不能为空”);
if(string.IsNullOrWhiteSpace(PODate))
errorMsg.AppendLine(“您必须输入采购订单日期”);
否则如果(!DateValidate(“PODate”,PODate))
errorMsg.AppendLine(“采购订单日期格式必须为MM/DD/YYY”);
if(string.IsNullOrWhiteSpace(CancelDate))
errorMsg.AppendLine(“您必须输入取消日期”);
否则如果(!DateValidate(“CancelDate”,CancelDate))
errorMsg.AppendLine(“取消日期格式必须为MM/DD/YYY”);
if(string.IsNullOrWhiteSpace(发货日期))
errorMsg.AppendLine(“您必须输入装运日期”);
否则,如果(!DateValidate(“ShipDate”,ShipDate))
errorMsg.AppendLine(“装运日期格式必须为MM/DD/YYY”);
using System;
using System.ComponentModel;
using System.Diagnostics;

namespace RGLibrary
{
    public abstract class Observable_Object : INotifyPropertyChanged

    {
        #region INotifyPropertyChanged Members

        /// <summary>
        /// Raised when a property on this object has a new value.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises this object's PropertyChanged event.
        /// </summary>
        /// <param name="propertyName">The property that has a new value.</param>
        protected virtual void OnPropertyChanged(string propertyName)
        {
            this.VerifyPropertyName(propertyName);

            if (this.PropertyChanged != null)
            {
                var e = new PropertyChangedEventArgs(propertyName);
                this.PropertyChanged(this, e);
            }
        }

        #endregion // INotifyPropertyChanged Members

        #region Debugging Aides

        /// <summary>
        /// Warns the developer if this object does not have
        /// a public property with the specified name. This
        /// method does not exist in a Release build.
        /// </summary>
        [Conditional("DEBUG")]
        [DebuggerStepThrough]
        public virtual void VerifyPropertyName(string propertyName)
        {
            // Verify that the property name matches a real,
            // public, instance property on this object.
            if (TypeDescriptor.GetProperties(this)[propertyName] == null)
            {
                string msg = "Invalid property name: " + propertyName;

                if (this.ThrowOnInvalidPropertyName)
                    throw new Exception(msg);
                else
                    Debug.Fail(msg);
            }
        }

        /// <summary>
        /// Returns whether an exception is thrown, or if a Debug.Fail() is used
        /// when an invalid property name is passed to the VerifyPropertyName method.
        /// The default value is false, but subclasses used by unit tests might
        /// override this property's getter to return true.
        /// </summary>
        protected virtual bool ThrowOnInvalidPropertyName { get; private set; }

        #endregion // Debugging Aides
    }
}
        public bool IsApproved
        {
            get { return _IsApproved; }
            set { _IsApproved = value; OnPropertyChanged("IsApproved"); }
        }