Опубликован: 05.08.2010 | Уровень: специалист | Доступ: свободно
Лекция 3:

Привязка WPF к реляционным данным

< Лекция 2 || Лекция 3: 12345 || Лекция 4 >

Вкладка Page4. Привязка интерфейса WPF к объекту данных с помощью класса ObjectDataProvider

До сих пор мы пользовались свойством DataContext для назначения источника привязки. WPF имеет еще один механизм назначения источника - через ресурс с помощью класса ObjectDataProvider. Класс позволяет создать ресурс, ссылающийся на коллекцию данных, возвращенную методом стороннего объекта, а потом привязать интерфейсный элемент или его свойство к этому ресурсу. Класс ObjectDataProvider может использоваться для привязки любых объектов, но мы рассмотрим его применение на предыдущем примере. Это позволит не делать подготовительную работу и не отвлекаться на новый код, а сосредоточиться только на нюансах привязки.

  • Добавьте в файле Window1.xaml к открывающему дескриптору окна Window строку подключения пространства имен приложения
<Window x:Class="DataBindingRelation.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300" 
    Title="WPF и типизированный DataSet" 
    MinHeight="300" 
    MinWidth="300"
    WindowStartupLocation="CenterScreen"
        
    xmlns:local="clr-namespace:DataBindingRelation"
    >

Конечно, подключение пространства имен можно поместить и в открывающий дескриптор вкладки Page4

<TabItem Header="Page4" xmlns:local="clr-namespace:DataBindingRelation">

но тогда его придется дублировать в каждой новой вкладке, где будут использоваться объекты процедурного кода. А можно поместить и во вкладке, и в окне, но мы этого делать не будем.

  • Добавьте в контейнер TabControl файла Window1.xaml разметку для новой вкладки Page4
<!-- Привязка к объекту с помощью класса ObjectDataProvider -->
            <TabItem Header="Page4">
                <TabItem.Resources>
                    <ObjectDataProvider 
                        x:Key="myObjectProvider"
                        ObjectType="{x:Type local:MyObject}"
                        MethodName="GetCollection" />
                </TabItem.Resources>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="2*" />
                            <ColumnDefinition Width="1.5*" />
                            <ColumnDefinition Width="2*" />
                        </Grid.ColumnDefinitions>
                        
                        <TextBlock Grid.Column="0" HorizontalAlignment="Center">ContactName</TextBlock>
                        <ListBox Grid.Row="1" Grid.Column="0"
                            Margin="0,0,0,3"
                            ScrollViewer.VerticalScrollBarVisibility="Auto"
                            ItemsSource="{Binding Source={StaticResource myObjectProvider},
                                                  Path=Customers}"
                            DisplayMemberPath="ContactName"
                            IsSynchronizedWithCurrentItem="True"
                            />
                        
                        <TextBlock Grid.Column="1" HorizontalAlignment="Center">OrderDate</TextBlock>
                        <ListBox Grid.Row="1" Grid.Column="1"
                            Margin="0,0,0,3"
                            ScrollViewer.VerticalScrollBarVisibility="Auto"
                            ItemsSource="{Binding Source={StaticResource myObjectProvider},
                                                  Path=Customers/CustomersOrders}"
                            DisplayMemberPath="OrderDate"
                            IsSynchronizedWithCurrentItem="True"
                            />
                        
                        <TextBlock Grid.Column="2" HorizontalAlignment="Center">ProductName</TextBlock>
                        <ListBox Grid.Row="1" Grid.Column="2"
                            Margin="0,0,0,3"
                            ScrollViewer.VerticalScrollBarVisibility="Auto"
                            ItemsSource="{Binding Source={StaticResource myObjectProvider},
                                                  Path=Customers/CustomersOrders/Orders_Order_Details_Extended}"
                            DisplayMemberPath="ProductName"
                            />
                    </Grid>
                    <Grid Grid.Row="1">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto" />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        
                        <TextBlock Grid.Row="0" Margin="0,0,5,0">ShipName:</TextBlock>
                        <TextBox Grid.Row="0" Grid.Column="1" 
                            Text="{Binding Source={StaticResource myObjectProvider}, 
                                           Path=Customers/CustomersOrders/ShipName, Mode=OneWay}"
                            Focusable="False"
                            Margin="5,0,0,0"
                            />
                        
                        <TextBlock Grid.Row="1">ShipAddress:</TextBlock>
                        <TextBox Grid.Row="1" Grid.Column="1" 
                            Text="{Binding Source={StaticResource myObjectProvider}, 
                                           Path=Customers/CustomersOrders/ShipAddress, Mode=OneWay}"
                            Focusable="False"
                            Margin="5,0,0,0"
                            />
                        
                        <TextBlock Grid.Row="2">ShipCity:</TextBlock>
                        <TextBox Grid.Row="2" Grid.Column="1" 
                            Text="{Binding Source={StaticResource myObjectProvider}, 
                                           Path=Customers/CustomersOrders/ShipCity, Mode=OneWay}"
                            Focusable="False"
                            Margin="5,0,0,0"
                            />
                        
                        <TextBlock Grid.Row="3">ShipCountry:</TextBlock>
                        <TextBox Grid.Row="3" Grid.Column="1" 
                            Text="{Binding Source={StaticResource myObjectProvider}, 
                                           Path=Customers/CustomersOrders/ShipCountry, Mode=OneWay}"
                            Focusable="False"
                            Margin="5,0,0,0"
                           />
                    </Grid>
                </Grid>
            </TabItem>
  • Добавьте в файл Window1.xaml.cs после класса Window1 новый класс с именем MyObject с единственным методом GetCollection(), возвращающим реляционный набор данных
namespace DataBindingRelation
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();
    
            Page1();
            Page2();
            Page3();
        }
    
        .........................................................
    }
    
    // Вкладка Page4. Объект с методом, возвращающим коллекцию привязки
    class MyObject
    {
        public DataSet GetCollection()
        {
            // Извлекаем в поле строку соединения из файла App.config
            String connectionString = System.Configuration.
                ConfigurationManager.ConnectionStrings["MyNorthwind"].ConnectionString;
    
            DataSet dataSet = new DataSet();// Создаем множественный набор данных
    
            // Заполняем множественный набор данных из БД
            using (OleDbConnection conn = new OleDbConnection(connectionString))
            {
                OleDbCommand selectCommand = conn.CreateCommand();
                OleDbDataAdapter adapter = new OleDbDataAdapter(selectCommand);
    
                // Загружает всю таблицу Customers
                selectCommand.CommandText = "SELECT * FROM Customers";
                adapter.Fill(dataSet, "Customers");
    
                // Загружает всю таблицу Orders
                selectCommand.CommandText = "SELECT * FROM Orders";
                adapter.Fill(dataSet, "Orders");
    
                // Загружаем представление по SQL-запросу
                selectCommand.CommandText = @"SELECT DISTINCTROW [Order Details].OrderID, [Order Details].ProductID, 
 Products.ProductName,
                    [Order Details].UnitPrice, [Order Details].Quantity, [Order Details].Discount,
                    CCur([Order Details].[UnitPrice]*[Quantity]*(1-[Discount])/100)*100 AS ExtendedPrice 
                    FROM Products INNER JOIN [Order Details] ON Products.ProductID = [Order Details].ProductID 
                    ORDER BY [Order Details].OrderID";
                adapter.Fill(dataSet, "Order_Details_Extended");
            }
    
            // Создание отношения между таблицами Customers и Orders
            dataSet.Relations.Add("CustomersOrders",
                dataSet.Tables["Customers"].Columns["CustomerID"],
                dataSet.Tables["Orders"].Columns["CustomerID"]);
    
            // Создание отношения между таблицами Orders и Order_Details_Extended
            // Немного другим способом
            DataColumn parentColumn = dataSet.Tables["Orders"].Columns["OrderID"];
            DataColumn childColumn = dataSet.Tables["Order_Details_Extended"].Columns["OrderID"];
            DataRelation relation = new DataRelation(
                "Orders_Order_Details_Extended",
                parentColumn, childColumn);
            dataSet.Relations.Add(relation);
    
            return dataSet;
        }
    }
}

По сути, метод класса MyObject использует тот же самый код, который мы создавали для вкладки Page3. Здесь можно было бы кое-что подсократить, например, сразу упаковать код вкладки Page3 в метод и его использовать в примере вкладки Page4. Но не станем мелочиться на учебных примерах, а будем последовательны и дисциплинированы.

  • Запустите приложение - опять на вкладке Page4 получилась та же самая функциональность, что и в предыдущих примерах. Чудеса WPF продолжаются!!!

Свойства класса ObjectDataProvider
Свойство Тип Описание
ConstructorParameters System.Collections.IList Список параметров для передачи конструктору, если они есть
IsAsynchronous bool Устанавливает режим создания объекта и вызова метода в текущем или отдельном потоке
MethodName string Имя вызываемого метода, возвращающего объект или коллекцию привязки
MethodParameters System.Collections.IList Список параметров для передачи методу, если они есть
ObjectInstance object Задает или извлекает объект, используемый в качестве источника привязки
ObjectType System.Type Задает или извлекает тип объекта, содержащего метод привязки

Следует иметь в виду, что данные, поставляемые методом пользовательскому интерфейсу, можно только просматривать, то есть привязка всегда будет только для чтения.

< Лекция 2 || Лекция 3: 12345 || Лекция 4 >
Алексей Бабушкин
Алексей Бабушкин

При выполнении в лабораторной работе упражнения №1 , а именно при выполнении нижеследующего кода:

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using Microsoft.Xna.Framework.Graphics;

   

namespace Application1

{

    public partial class MainForm : Form

    {

        // Объявим поле графического устройства для видимости в методах

        GraphicsDevice device;

   

        public MainForm()

        {

            InitializeComponent();

   

            // Подпишемся на событие Load формы

            this.Load += new EventHandler(MainForm_Load);

   

            // Попишемся на событие FormClosed формы

            this.FormClosed += new FormClosedEventHandler(MainForm_FormClosed);

        }

   

        void MainForm_FormClosed(object sender, FormClosedEventArgs e)

        {

            //  Удаляем (освобождаем) устройство

            device.Dispose();

            // На всякий случай присваиваем ссылке на устройство значение null

            device = null;       

        }

   

        void MainForm_Load(object sender, EventArgs e)

        {

            // Создаем объект представления для настройки графического устройства

            PresentationParameters presentParams = new PresentationParameters();

            // Настраиваем объект представления через его свойства

            presentParams.IsFullScreen = false; // Включаем оконный режим

            presentParams.BackBufferCount = 1;  // Включаем задний буфер

                                                // для двойной буферизации

            // Переключение переднего и заднего буферов

            // должно осуществляться с максимальной эффективностью

            presentParams.SwapEffect = SwapEffect.Discard;

            // Устанавливаем размеры заднего буфера по клиентской области окна формы

            presentParams.BackBufferWidth = this.ClientSize.Width;

            presentParams.BackBufferHeight = this.ClientSize.Height;

   

            // Создадим графическое устройство с заданными настройками

            device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, DeviceType.Hardware,

                this.Handle, presentParams);

        }

   

        protected override void OnPaint(PaintEventArgs e)

        {

            device.Clear(Microsoft.Xna.Framework.Graphics.Color.CornflowerBlue);

   

            base.OnPaint(e);

        }

    }

}

Выбрасывается исключение:

Невозможно загрузить файл или сборку "Microsoft.Xna.Framework, Version=3.0.0.0, Culture=neutral, PublicKeyToken=6d5c3888ef60e27d" или один из зависимых от них компонентов. Не удается найти указанный файл.

Делаю все пунктуально. В чем может быть проблема?

Юрий Макушин
Юрий Макушин
Россия, Москва, РЭА им. Плеханова, 2004