【问题标题】:Databinding failing in UserControlUserControl 中的数据绑定失败
【发布时间】:2020-02-13 06:18:20
【问题描述】:

我一直在尝试创建一个小型应用程序来放置在 POS 界面上,并允许用户访问一些外部自定义 C# 应用程序无法通过标准 POS 软件访问的信息。其中之一是收据查找,在演示后被要求扩展以检查在线订单详细信息。

我遇到的问题是数据绑定到存储信息的对象。最初,当该功能只有一个视图和视图模型时,它可以正常工作。在创建 2 个用户控件视图以呈现不同的信息后,每个视图都有相应的视图模型,新视图不显示任何数据。

这里是Model继承的基类:

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

VM 继承的类:

using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace RGLibrary
{
    public class BindableBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected bool SetProperty<T>(ref T member, T val, [CallerMemberName] string propertyName = null)
        {
            if (Equals(member, val))
                return false;

            member = val;
            OnPropertyChanged(propertyName);
            return true;
        }

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

    }
}

这是核心 VM 类:

using RGLibrary;
using Store_Launcher.Model;

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace Store_Launcher.ViewModel
{
    public class ReceiptLookupVM: BindableBase
    {
        private string _ReceiptNumber;
        private ReceiptLookupModel _ReceiptDetails;
        public ReceiptLookupModel ReceiptDetails
        {
            get { return _ReceiptDetails; }
            set { _ReceiptDetails = value; OnPropertyChanged("ReceiptDetails"); }
        }
        public RGDBConnect rms = new RGDBConnect("");
        public string ReceiptNumber
        {
            get { return _ReceiptNumber; }
            set { _ReceiptNumber = value; OnPropertyChanged("ReceiptNumber"); }
        }

        private OnlineOrderDetailsVM orderDetailsVM = new OnlineOrderDetailsVM();
        private ReceiptDetailsVM receiptDetailsVM = new ReceiptDetailsVM();

        private BindableBase _CurrentMode;
        public BindableBase CurrentMode
        {
            get { return _CurrentMode; }
            set { SetProperty(ref _CurrentMode, value); }
        }



        public ReceiptLookupVM()
        {
            ReceiptDetails = new ReceiptLookupModel();
            ReceiptNumber = "";
            if (System.Diagnostics.Debugger.IsAttached)
                rms = new RGDBConnect(ConfigurationManager.AppSettings["rmstest"]);
            else
                rms = new RGDBConnect(ConfigurationManager.AppSettings["rms"]);
            CheckCommand = new MyICommand<string>(OnCheck);
            CurrentMode = receiptDetailsVM;
        }
        public MyICommand<string> CheckCommand { get; private set; }
        private void OnCheck(string command)
        {
            ReceiptDetails.Receipt = _ReceiptNumber;

            string query = "rg_launcher_receiptref_ext '" + ReceiptDetails.Receipt + "'";
            try
            {
                DataTable results = rms.ExecuteSelect(query);
                if (results.Rows.Count == 1)
                {
                    DataRow resultRow = results.Rows[0];
                    if (resultRow["tran_type"].ToString() == "SALE")
                    {
                        ReceiptDetails.SaleCode = resultRow["sale_code"].ToString();
                        ReceiptDetails.Customer = resultRow["name"].ToString();
                        ReceiptDetails.CustomerID = resultRow["customer_id"].ToString();
                        ReceiptDetails.Items = resultRow["units"].ToString();
                        ReceiptDetails.Value = resultRow["value"].ToString();
                        ReceiptDetails.Stylist = resultRow["stylist"].ToString();
                        ReceiptDetails.TransactionType = ReceiptLookupModel.TransType.RetailOrder;
                        receiptDetailsVM.UpdateDetails(ReceiptDetails);
                        CurrentMode = receiptDetailsVM;
                    }
                    else if (resultRow["tran_type"].ToString() == "WEB ORDER")
                    {
                        ReceiptDetails.SaleCode = resultRow["sale_code"].ToString();
                        ReceiptDetails.ReceiptNumber = resultRow["receipt_ref"].ToString();
                        ReceiptDetails.Customer = resultRow["name"].ToString();
                        ReceiptDetails.CustomerID = resultRow["customer_id"].ToString();
                        ReceiptDetails.Items = resultRow["units"].ToString();
                        ReceiptDetails.Value = resultRow["value"].ToString();
                        ReceiptDetails.TransactionType = ReceiptLookupModel.TransType.OnlineOrder;
                        orderDetailsVM.UpdateDetails(ReceiptDetails);
                        CurrentMode = orderDetailsVM;
                    }
                    else
                    {
                        MessageBox.Show(
                            "Unable to determine the transaction type for this number. Please contact IT for assistance",
                            "Receipt Lookup: Unknown order number",
                            MessageBoxButton.OK,
                            MessageBoxImage.Warning);
                        ReceiptDetails = new ReceiptLookupModel();
                        receiptDetailsVM.UpdateDetails(ReceiptDetails);
                        CurrentMode = receiptDetailsVM;
                    }
                }
                else if (results.Rows.Count == 0)
                {
                    MessageBox.Show(
                        "Unable to find this receipt number in the system. Please make sure that the receipt number has been entered correctly.",
                        "Receipt Lookup: Unable to find sale",
                        MessageBoxButton.OK,
                        MessageBoxImage.Exclamation);
                    ReceiptDetails = new ReceiptLookupModel();
                    receiptDetailsVM.UpdateDetails(ReceiptDetails);
                    CurrentMode = receiptDetailsVM;
                }
                else
                {
                    MessageBox.Show(
                        "An error has occured and the system is unable to properly locate this receipt number in the system. Please contact IT for assistance",
                        "Receipt Lookup: Unable to find sale",
                        MessageBoxButton.OK,
                        MessageBoxImage.Warning);
                    ReceiptDetails = new ReceiptLookupModel();
                    receiptDetailsVM.UpdateDetails(ReceiptDetails);
                    CurrentMode = receiptDetailsVM;
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(
                    e.Message,
                    "Receipt Lookup: An error has occurred",
                    MessageBoxButton.OK,
                    MessageBoxImage.Warning);
                MessageBox.Show(
                    "An error has occured and the system is unable to properly locate this receipt number in the system. Please check to make sure your computer is currently connected to the internet. Contact IT for further assistance",
                    "Receipt Lookup: Unable to lookup receipt number",
                    MessageBoxButton.OK,
                    MessageBoxImage.Exclamation);
                ReceiptDetails = new ReceiptLookupModel();
                receiptDetailsVM.UpdateDetails(ReceiptDetails);
                CurrentMode = receiptDetailsVM;
            }
        }
    }
}

下面是对应的视图:

<Window x:Class="Store_Launcher.Views.ReceiptLookupView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Store_Launcher.Views"
        xmlns:viewmodel="clr-namespace:Store_Launcher.ViewModel"
        mc:Ignorable="d"
        Title="Rodd &amp; Gunn Launcher: Receipt Lookup" Height="195" Width="450"
        ShowInTaskbar="True" ResizeMode="NoResize" Topmost="True" >
    <Window.DataContext>
        <viewmodel:ReceiptLookupVM/>
    </Window.DataContext>
    <Window.Resources>
        <DataTemplate DataType="{x:Type viewmodel:OnlineOrderDetailsVM}">
            <local:OnlineOrderDetailsView/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type viewmodel:ReceiptDetailsVM}">
            <local:ReceiptDetailsView/>
        </DataTemplate>
    </Window.Resources>
    <Window.CommandBindings>
        <CommandBinding Command="ApplicationCommands.Close"
                        Executed="CloseCommandHandler"/>
    </Window.CommandBindings>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="AUTO"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="AUTO"/>
            <RowDefinition Height="AUTO"/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0"
                    Grid.Column="0"
                    Orientation="Horizontal"
                    Margin="5"
                    VerticalAlignment="Center">
            <TextBlock Text="Receipt Number: "/>
            <TextBox Width="100"
                     Text="{Binding ReceiptNumber, Mode=TwoWay}"/>
        </StackPanel>

        <!-- New User Control XAML to switch between brick and mortar, and online order modes -->

        <UserControl
            Margin="5"
            Height="115"
            Width="230"
            Grid.Column="1">
            <ContentControl Content="{Binding CurrentMode}"/>
        </UserControl>

        <!-- Original Grid XAML -->

        <!--<Grid Grid.Row="0"
              Grid.Column="1"
              Margin="5"
              DataContext="{Binding ReceiptDetails}">
            <Grid.RowDefinitions>
                <RowDefinition Height="AUTO"/>
                <RowDefinition Height="AUTO"/>
                <RowDefinition Height="AUTO"/>
                <RowDefinition Height="AUTO"/>
                <RowDefinition Height="AUTO"/>
                <RowDefinition Height="AUTO"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="AUTO"/>
                <ColumnDefinition Width="AUTO"/>
            </Grid.ColumnDefinitions>
            <TextBlock
                Grid.Row="0"
                Grid.Column="0"
                Text="Sale Code: "/>
            <TextBlock
                Grid.Row="0"
                Grid.Column="1"
                Text="{Binding SaleCode}"/>
            <TextBlock
                Grid.Row="1"
                Grid.Column="0"
                Text="Customer ID:  "/>
            <TextBlock
                Grid.Row="1"
                Grid.Column="1"
                Text="{Binding CustomerID}"/>
            <TextBlock
                Grid.Row="2"
                Grid.Column="0"
                Text="Customer: "/>
            <TextBlock
                Grid.Row="2"
                Grid.Column="1"
                Text="{Binding Customer}"/>
            <TextBlock
                Grid.Row="3"
                Grid.Column="0"
                Text="Items: "/>
            <TextBlock
                Grid.Row="3"
                Grid.Column="1"
                Text="{Binding Items}"/>
            <TextBlock
                Grid.Row="4"
                Grid.Column="0"
                Text="Value: "/>
            <TextBlock
                Grid.Row="4"
                Grid.Column="1"
                Text="{Binding Value}"/>
            <TextBlock
                Grid.Row="5"
                Grid.Column="0"
                Text="Stylist: "/>
            <TextBlock
                Grid.Row="5"
                Grid.Column="1"
                Text="{Binding Stylist}"/>
        </Grid>-->

        <StackPanel
            Grid.Column="0"
            Grid.Row="1"
            Grid.ColumnSpan="2"
            Orientation="Horizontal"
            HorizontalAlignment="Center">
            <Button
                Width="100"
                Height="20"
                Margin="5"
                Content="CHECK"
                Command="{Binding CheckCommand}"/>
            <Button
                Width="100"
                Height="20"
                Margin="5"
                Content="CLOSE"
                Command="ApplicationCommands.Close"/>
        </StackPanel>


    </Grid>
</Window>

这是订单详情模型:

using RGLibrary;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Store_Launcher.Model
{
    public class ReceiptLookupModel: Observable_Object
    {
        private string _Receipt;
        private string _SaleCode;
        private string _ReceiptNumber;
        private string _CustomerID;
        private string _Customer;
        private string _Items;
        private string _Value;
        private string _Stylist;
        private TransType? _TransactionType;
        public string Receipt
        {
            get { return _Receipt = (_Receipt ?? ""); }
            set { _Receipt = value; OnPropertyChanged("Receipt"); }
        }
        public string SaleCode
        {
            get { return _SaleCode = (_SaleCode ?? ""); }
            set { _SaleCode = value; OnPropertyChanged("SaleCode"); }
        }
        public string ReceiptNumber
        {
            get { return _ReceiptNumber = (_ReceiptNumber ?? ""); }
            set { _ReceiptNumber = value; OnPropertyChanged("ReceiptNumber"); }
        }
        public string CustomerID
        {
            get { return _CustomerID = (_CustomerID ?? ""); }
            set { _CustomerID = value; OnPropertyChanged("CustomerID"); }
        }
        public string Customer
        {
            get { return _Customer = (_Customer ?? ""); }
            set { _Customer = value; OnPropertyChanged("Customer"); }
        }
        public string Items
        {
            get { return _Items = (_Items ?? "0"); }
            set { _Items = value; OnPropertyChanged("Items"); }
        }
        public string Value
        {
            get { return _Value = (_Value ?? "$0.00"); }
            set { _Value = value; OnPropertyChanged("Value"); }
        }
        public string Stylist
        {
            get { return _Stylist = (_Stylist ?? ""); }
            set { _Stylist = value; OnPropertyChanged("Stylist"); }
        }
        public TransType? TransactionType
        {
            get { return _TransactionType = (_TransactionType ?? TransType.None); }
            set { _TransactionType = value; OnPropertyChanged("TransactionType"); }
        }


        public enum TransType
        {
            OnlineOrder,
            RetailOrder,
            None
        }
    }
}

在线订单视图模型:

using RGLibrary;
using Store_Launcher.Model;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace Store_Launcher.ViewModel
{
    public class OnlineOrderDetailsVM: BindableBase
    {
        private ReceiptLookupModel _OrderDetails;
        public ReceiptLookupModel OrderDetails
        {
            get { return _OrderDetails; }
            set { _OrderDetails = value; OnPropertyChanged("OrderDetails"); }
        }
        public OnlineOrderDetailsVM()
        {
            OrderDetails = new ReceiptLookupModel();
        }

        public void UpdateDetails(ReceiptLookupModel SQLData)
        {
            ReceiptLookupModel _data = new ReceiptLookupModel();
            _data.Customer = SQLData.Customer;
            _data.CustomerID = SQLData.CustomerID;
            _data.Items = SQLData.Items;
            _data.Receipt = SQLData.Receipt;
            _data.ReceiptNumber = SQLData.Receipt;
            _data.SaleCode = SQLData.SaleCode;
            _data.Stylist = SQLData.Stylist;
            _data.TransactionType = SQLData.TransactionType;
            _data.Value = SQLData.Value;
            OrderDetails = _data;

        }
    }
}

这里是订单详情视图:

<UserControl x:Name="OnlineOrderDetailsUC"
             x:Class="Store_Launcher.Views.OnlineOrderDetailsView"
             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:Store_Launcher.Views"  
             xmlns:viewmodel="clr-namespace:Store_Launcher.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="115" d:DesignWidth="230">
    <UserControl.DataContext>
        <viewmodel:OnlineOrderDetailsVM/>
    </UserControl.DataContext>

    <Grid Grid.Row="0"
          Grid.Column="1"
          Margin="5"
          DataContext="{Binding OrderDetails, NotifyOnSourceUpdated=True}">
        <Grid.RowDefinitions>
            <RowDefinition Height="AUTO"/>
            <RowDefinition Height="AUTO"/>
            <RowDefinition Height="AUTO"/>
            <RowDefinition Height="AUTO"/>
            <RowDefinition Height="AUTO"/>
            <RowDefinition Height="AUTO"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="AUTO"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <TextBlock
                Grid.Row="0"
                Grid.Column="0"
                Text="Sale Code: "/>
        <TextBlock
                Grid.Row="0"
                Grid.Column="1"
                Text="{Binding SaleCode}"/>
        <TextBlock
                Grid.Row="1"
                Grid.Column="0"
                Text="Receipt No:  "/>
        <TextBlock
                Grid.Row="1"
                Grid.Column="1"
                Text="{Binding ReceiptNumber}"/>
        <TextBlock
                Grid.Row="2"
                Grid.Column="0"
                Text="Customer ID:  "/>
        <TextBlock
                Grid.Row="2"
                Grid.Column="1"
                Text="{Binding CustomerID}"/>
        <TextBlock
                Grid.Row="3"
                Grid.Column="0"
                Text="Customer: "/>
        <TextBlock
                Grid.Row="3"
                Grid.Column="1"
                Text="{Binding Customer}"/>
        <TextBlock
                Grid.Row="4"
                Grid.Column="0"
                Text="Items: "/>
        <TextBlock
                Grid.Row="4"
                Grid.Column="1"
                Text="{Binding Items}"/>
        <TextBlock
                Grid.Row="5"
                Grid.Column="0"
                Text="Value: "/>
        <TextBlock
                Grid.Row="5"
                Grid.Column="1"
                Text="{Binding Value}"/>
    </Grid>
</UserControl>

我感到困惑的是,在receiptLookup 视图中,当我使用注释掉的网格而不是正确绑定到receiptLookup 视图模型中的模型对象的用户控件部分时,用户控件绑定似乎不起作用。

我已经尝试了很多方法来解决这个问题。最初,我没有使用 orderdetails 视图模型中的方法来设置模型的对象值,而是将其设置为与在receiptlookup 视图模型中生成的相同。

此外,我尝试在绑定中使用 diag:PresentationTraceSources.TraceLevel=High 根据之前提出的其他问题来诊断是否存在绑定错误。我认为它正在绑定到某些东西,因为绑定失败没有错误,并且它列出了绑定到的对象的哈希值。

除此之外,我还尝试从 orderdetails 视图的用户控件和数据网格的 datacontext 中删除 datacontext,并在绑定路径中指定它,但这也没有改变任何结果。

另一个问题是建议在他们的视图中添加一个 x:name,所以我在没有任何更改的情况下做了同样的事情。我还尝试将 NotifyOnSourceUpdate=True 添加到每个文本块的绑定和 Mode=TwoWay 但它们本身或两者的组合都没有帮助。

此时,我在之前的几个应用程序中使用了主视图的类似用户控件子部分,所以我真的很困惑为什么它在这种情况下不起作用。我已尝试添加所有相关代码,但如果您需要我列出其他任何内容,请在您的评论中告诉我。

【问题讨论】:

    标签: c# wpf mvvm data-binding


    【解决方案1】:

    如果您在 DataTemplate 中有一个 UserControl,例如

    <DataTemplate DataType="{x:Type viewmodel:OnlineOrderDetailsVM}">
        <local:OnlineOrderDetailsView/>
    </DataTemplate>
    

    UserControl 应该从它的父对象继承它的 DataContext,这里是 ContentControl 或 ContentPresenter。 DataContext 包含由 DataTemplate 的 DataType 属性(即 ContentControl 或 ContentPresenter 的 Content)指定类型的实例。

    这个property value inheritance 仅在您没有明确设置属性时才有效,如

    <UserControl.DataContext>
        <viewmodel:OnlineOrderDetailsVM/>
    </UserControl.DataContext>
    

    此 DataContext 值的优先级高于继承的值。结果是 DataTemplate 中的 UserControl 始终绑定到其私有 DataContext 对象,但从不绑定到模板提供的对象。

    所以只需从 UserControl 的 XAML 中删除 DataContext 分配。控件不应该显式设置自己的 DataContext,因此永远不会有私有视图模型对象。

    【讨论】:

    • 我有一种感觉,它会像这样简单。感谢您的解决方案和解释!
    猜你喜欢
    • 2012-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-12
    • 2012-09-09
    • 2013-02-05
    相关资源
    最近更新 更多