Отображение данных базы данных в Datagrid с использованием WPF и WCF в многоуровневом приложении - PullRequest
0 голосов
/ 14 июля 2020

Выполнив следующий учебник: https://docs.microsoft.com/en-us/visualstudio/data-tools/walkthrough-creating-an-n-tier-data-application?view=vs-2019

Я пытаюсь заставить тот же учебник работать с WPF вместо Windows Forms, но не могу на всю жизнь Я получаю его для отображения данных базы данных в 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());
        }
    }
}

1 Ответ

0 голосов
/ 14 июля 2020

Вы работаете с неправильным WMSDataSet экземпляром.

Ваши привязки данных ссылаются на экземпляр, определенный в ResourceDictionary страницы. Но на самом деле вы инициализируете второй экземпляр в обработчике событий Loaded.

Решение состоит в том, чтобы либо создать экземпляр WMSDataSet в коде программной части, а затем добавить его в ResourceDictionary (или назначить его для связываемое свойство) или для получения экземпляра XAML и его инициализации. Второе решение потребовало бы, чтобы объект WMSDataSet реализовал INotifyPropertyChanged, чтобы внести изменения в свойство уведомления о привязке данных для обновления цели привязки. Я рекомендую выставить WMSDataSet через DependencyProperty:

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

ViewProducts.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 общий родительский 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, что приводит к тому, что обработчик события также вызывается дважды. Сначала вы подписываетесь на PageLoaded в XAML:

<Page Loaded="Page_Loaded">

и в коде программной части:

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

Выберите только один.

...