简体   繁体   English

UserControl 中的数据绑定失败

[英]Databinding failing in UserControl

I have been trying to create a small application to sit over a POS interface and allow the user to access some information not accessible through the standard POS software by external custom C# applications.我一直在尝试创建一个小应用程序来放置在 POS 界面上,并允许用户访问一些外部自定义 C# 应用程序无法通过标准 POS 软件访问的信息。 One of these was a receipt lookup, which after a demonstration was asked to be expanded to also check online order details.其中之一是收据查找,在演示后要求扩展以检查在线订单详细信息。

The problem I am having is with the databinding to the object which is storing the information.我遇到的问题是与存储信息的对象的数据绑定。 Originally when there was a single view and viewmodel for the function it worked correctly.最初,当该函数只有一个视图和视图模型时,它可以正常工作。 After creating 2 usercontrol views to present different information, with corresponding viewmodels for each view, the new views do not show any data.创建2个用户控件视图来呈现不同的信息后,每个视图都有对应的视图模型,新视图不显示任何数据。

Here is the base class inherited by the 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
    }
}

The inherited class by the VMs:虚拟机继承的类:

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

    }
}

Here is the core VM class:这是核心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;
            }
        }
    }
}

Here is the corresponding view:下面是对应的视图:

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

Here is the order details model:这是订单详细信息模型:

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

The online orders viewmodel:在线订单视图模型:

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;

        }
    }
}

Here is the order details view:这是订单详细信息视图:

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

The thing I am confused about is that in the receiptLookup view, when I use the commented out grid instead of usercontrol section that binds to the model object in the receiptLookup viewmodel correctly but the usercontrol binding does not seem to work.我感到困惑的是,在receiptLookup 视图中,当我使用注释掉的网格而不是usercontrol 部分正确绑定到receiptLookup 视图模型中的模型对象时,但usercontrol 绑定似乎不起作用。

I have tried a number of things to resolve this.我已经尝试了很多方法来解决这个问题。 Originally instead of using a method inside the orderdetails viewmodel to set the model's object value I was just setting it to be the same as the one generated in the receiptlookup viewmodel.最初,我没有使用 orderdetails 视图模型中的方法来设置模型的对象值,而是将其设置为与收据查找视图模型中生成的相同。

In addition I have tried to use diag:PresentationTraceSources.TraceLevel=High in the binding to diagnose if there is a binding error based on other questions that have been asked previously.此外,我尝试在绑定中使用 diag:PresentationTraceSources.TraceLevel=High 来根据之前询问的其他问题诊断是否存在绑定错误。 I think that it is binding to something because there is no error about the binding failing, and it lists a hash for an object that is binding to.我认为它绑定到某些东西,因为绑定失败没有错误,并且它列出了绑定到的对象的哈希。

Further to this I have tried to remove the datacontext from both the orderdetails view's usercontrol and also from the datagrid's datacontext, and specify it in the binding path but that did not change any results either.除此之外,我还尝试从 orderdetails 视图的 usercontrol 和 datagrid 的 datacontext 中删除 datacontext,并在绑定路径中指定它,但这也没有改变任何结果。

Another question was given the advice to add an x:name to their views, so I have done the same without any changes.另一个问题得到了在他们的视图中添加 x:name 的建议,所以我没有做任何更改就做了同样的事情。 I have also tried adding NotifyOnSourceUpdate=True to each textblock's binding and Mode=TwoWay but neither on their own or the combination of both has helped.我还尝试将 NotifyOnSourceUpdate=True 添加到每个文本块的绑定和 Mode=TwoWay 中,但它们本身或两者的组合都没有帮助。

I have used a similar usercontrol sub-section of the main view in a few previous applications at this point so I am really quite stuck as to why it is not working in this case.在这一点上,我在一些以前的应用程序中使用了主视图的类似 usercontrol 子部分,所以我真的很困惑为什么它在这种情况下不起作用。 I have tried to add all the relevant code but if you need me to list anything else please let me know in your comment.我已尝试添加所有相关代码,但如果您需要我列出其他任何内容,请在您的评论中告诉我。

If you have a UserControl in a DataTemplate like如果您在 DataTemplate 中有一个 UserControl,例如

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

the UserControl is supposed to inherit its DataContext from its parent object, which is a ContentControl or a ContentPresenter here. UserControl 应该从其父对象(此处为 ContentControl 或 ContentPresenter)继承其 DataContext。 The DataContext holds an instance of the type specified by the DataTemplate's DataType property (ie the Content of the ContentControl or ContentPresenter). DataContext 持有由 DataTemplate 的DataType属性指定的类型的实例(即 ContentControl 或 ContentPresenter 的Content )。

This property value inheritance only works if you do not explicity set the property, as in属性值继承仅在您未显式设置属性时才有效,如

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

This DataContext value has higher precedence than the inherited value.此 DataContext 值的优先级高于继承的值。 The result is that the UserControl in the DataTemplate always binds to its private DataContext object, but never to the one provided by the template.结果是 DataTemplate 中的 UserControl 始终绑定到其私有 DataContext 对象,但从不绑定到模板提供的对象。

So just remove the DataContext assignment from the UserControl's XAML.因此,只需从 UserControl 的 XAML 中删除 DataContext 分配。 Controls should never explicitly set their own DataContext, hence never have private view model objects.控件永远不应该显式设置它们自己的 DataContext,因此永远不要有私有视图模型对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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