【问题标题】:Displaying Database Data in a Datagrid using WPF and WCF in an N-Tier Application在 N 层应用程序中使用 WPF 和 WCF 在 Datagrid 中显示数据库数据
【发布时间】:2020-07-13 23:11:13
【问题描述】:

已完成以下教程:https://docs.microsoft.com/en-us/visualstudio/data-tools/walkthrough-creating-an-n-tier-data-application?view=vs-2019

我正在尝试使用 WPF 而不是 Windows 窗体来使用相同的教程,但我一生都无法让它在 Datagrid 中显示数据库数据。除了 PresentationTier 之外,我的所有代码都是相同的(但使用我自己的数据库数据)。我的 .xaml 代码是(主要是从“数据源”选项卡中拖动表格的默认设置):

<Page
      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:DataEntityTier="clr-namespace:DataEntityTier;assembly=DataEntityTier" 
      x:Class="PresentationTier.ViewProducts"
      mc:Ignorable="d" 
      d:DesignHeight="450" d:DesignWidth="800"
      Title="Worker-ViewProducts" Background="White" Loaded="Page_Loaded">

    <Page.Resources>
        <DataEntityTier:WMSDataSet x:Key="wMSDataSet"/>
        <CollectionViewSource x:Key="productsViewSource" Source="{Binding products, Source={StaticResource wMSDataSet}}"/>
        <CollectionViewSource x:Key="warehousesViewSource" Source="{Binding warehouses, Source={StaticResource wMSDataSet}}"/>
    </Page.Resources>

    <Grid Margin="10" DataContext="{StaticResource productsViewSource}">
        <DataGrid x:Name="productsDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding Source={StaticResource productsViewSource}}" Margin="165,230,215,0" RowDetailsVisibilityMode="VisibleWhenSelected">
            <DataGrid.Columns>
                <DataGridTextColumn x:Name="product_idColumn" Binding="{Binding product_id}" Header="product id" IsReadOnly="True" Width="SizeToHeader"/>
                <DataGridTextColumn x:Name="account_idColumn" Binding="{Binding account_id}" Header="account id" Width="SizeToHeader"/>
                <DataGridTextColumn x:Name="titleColumn" Binding="{Binding title}" Header="title" Width="SizeToHeader"/>
                <DataGridTextColumn x:Name="skuColumn" Binding="{Binding sku}" Header="sku" Width="SizeToHeader"/>
            </DataGrid.Columns>
        </DataGrid>
        <DataGrid x:Name="warehousesDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding Source={StaticResource warehousesViewSource}}" Margin="165,0,215,230" RowDetailsVisibilityMode="VisibleWhenSelected">
            <DataGrid.Columns>
                <DataGridTextColumn x:Name="warehouse_idColumn" Binding="{Binding warehouse_id}" Header="warehouse id" IsReadOnly="True" Width="SizeToHeader"/>
                <DataGridTextColumn x:Name="nameColumn" Binding="{Binding name}" Header="name" Width="SizeToHeader"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Page>

而我的 xaml.cs 代码是:

using DataEntityTier;
using System.Windows;
using System.Windows.Controls;

namespace PresentationTier
{
    public partial class ViewProducts : Page
    {
        WMSDataSet wMSDataSet;

        public ViewProducts()
        {
            InitializeComponent();
            Loaded += Page_Loaded;
        }

        private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            wMSDataSet = new WMSDataSet();
            ServiceReference1.Service1Client DataSvc = new ServiceReference1.Service1Client();
            wMSDataSet.products.Merge(DataSvc.GetProducts());
            wMSDataSet.warehouses.Merge(DataSvc.GetWarehouses());
        }
    }
}

【问题讨论】:

    标签: c# .net wpf wcf n-tier-architecture


    【解决方案1】:

    您在错误的WMSDataSet 实例上运行。

    您的数据绑定引用页面的ResourceDictionary 中定义的实例。但是您实际上是在 Loaded 事件处理程序中初始化第二个实例。

    解决方案是在代码隐藏中实例化 WMSDataSet,然后将其添加到 ResourceDictionary(或将其分配给可绑定属性),或者检索 XAML 实例并对其进行初始化。
    第二种解决方案需要WMSDataSet 对象实现INotifyPropertyChanged,以便更改数据绑定通知属性以更新绑定目标。我建议通过DependencyProperty 公开WMSDataSet

    查看Products.xaml.cs

    public partial class ViewProducts : Page
    {
      public static readonly DependencyProperty WmsDataSourceProperty = DependencyProperty.Register(
        "WmsDataSource",
        typeof(WMSDataSet),
        typeof(ViewProducts),
        new PropertyMetadata(default(WMSDataSet)));
    
      public WMSDataSet WmsDataSource
      {
        get => (WMSDataSet) GetValue(ViewProducts.WmsDataSourceProperty);
        set => SetValue(ViewProducts.WmsDataSourceProperty, value);
      }
    
    
      public ViewProducts()
      {
        InitializeComponent();
        Loaded += Page_Loaded;
      }
    
      private void Page_Loaded(object sender, RoutedEventArgs e)
      {
        wmsDataSet = new WMSDataSet();
        ServiceReference1.Service1Client dataSvc = new ServiceReference1.Service1Client();
        wmsDataSet.products.Merge(dataSvc.GetProducts());
        wmsDataSet.warehouses.Merge(dataSvc.GetWarehouses());
      
        this.WmsDataSource = wmsDataSet;
      }
    }
    

    查看Products.xaml

    <Page>
      <Page.Resources>
        <CollectionViewSource x:Key="ProductsViewSource" Source="{Binding RelativeSource={RelativeSource AncestorType=local:ViewProducts}, Path=WmsDataSource.products}" />
        <CollectionViewSource x:Key="WarehousesViewSource" Source="{Binding RelativeSource={RelativeSource AncestorType=local:ViewProducts}, Path=WmsDataSource.warehouses}" />
      </Page.Resources>
    
      <!-- When you set the DataContext to 'ViewProducts.WmsDataSource', you can directly bind to it
           using {Binding} (without specifying the Binding.Source) -->
      <Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=local:ViewProducts}, Path=WmsDataSource}">
        <DataGrid ItemsSource="{Binding products}">
        </DataGrid>
    
        <DataGrid ItemsSource="{Binding warehouses}">
        </DataGrid>
      </Grid>
    </Page>
    

    备注

    您的代码中有一些错误。你做了很多多余的东西,因为你做了两次,后者覆盖了前一个声明,引入了错误或意外行为(例如空白控件):

    当然已经提到了WMSDataSet的两个实例的用法。
    在 XAML 中实例化:

    <DataEntityTier:WMSDataSet x:Key="wMSDataSet"/>
    

    和代码隐藏:

    this.wMSDataSet = new WMSDataSet();
    

    然后你设置父元素的DataContext 只是为了在你的绑定表达式中忽略它:

    <Grid DataContext="{StaticResource productsViewSource}">
      <DataGrid ItemsSource="{Binding Source={StaticResource productsViewSource}}">
    

    代替:

    <Grid DataContext="{StaticResource productsViewSource}">
      <DataGrid ItemsSource="{Binding}">
    

    由于hte common parent Grid(两个DataGrid 元素)的内容不共享 相同数据上下文,它更令人困惑而不是有用将他们的DataContext 设置为一个共同的来源。而是选择定义以下版本:

    <Grid>
      <DataGrid ItemsSource="{Binding Source={StaticResource productsViewSource}}" />
      <DataGrid ItemsSource="{Binding Source={StaticResource warehousesViewSource}}" />
    </Grid>
    

    或在我的解决方案的重构版本中:

    <Grid DataContext="{Binding RelativeSource={RelativeSource AncestorType=local:ViewProducts}, Path=WmsDataSource}">
      <DataGrid ItemsSource="{Binding products}" />
      <DataGrid ItemsSource="{Binding warehouses}" />
    </Grid>
    

    您还订阅了两次Loaded 事件,这导致事件处理程序也被调用了两次。您首先在 XAML 中订阅PageLoaded

    <Page Loaded="Page_Loaded">
    

    在代码隐藏中:

    public ViewProducts()
    {
      InitializeComponent();
      Loaded += Page_Loaded;
    }
    

    只选择一个。

    【讨论】:

    • 感谢您提供信息丰富的回复!真的很感激。但是,我不完全理解“WmsDataSource”,它是如何工作的?当我尝试实现您的代码时,会导致很多错误。
    • WmsDataSource 是一个 DependencyProperty,它用作替换 XAML 实例 wMSDataSet 的绑定源。通过将GridDataContext 设置为WmsDataSource,您还可以消除对CollectionViewSource 定义的需要,因为您可以直接绑定到WmsDataSource.productsWmsDataSource.warehouses。我修复了代码中的一些复制和粘贴错误,例如 WmsDataSource 属性声明的名称。对不起那个。请告诉我您的问题,以便我可以帮助您解决这些问题。
    • 太棒了,我想这已经解决了!非常感谢。
    猜你喜欢
    • 2012-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-03
    • 2010-11-12
    • 1970-01-01
    • 2010-12-04
    相关资源
    最近更新 更多