Опубликован: 05.08.2010 | Доступ: свободный | Студентов: 2010 / 47 | Оценка: 4.50 / 4.40 | Длительность: 60:26:00
Лекция 2:

Привязка WPF к таблице данных ADO.NET

< Лекция 1 || Лекция 2: 123456 || Лекция 3 >

Вкладка Page4. Привязка к таблице Employees через набор данных DataSet

Источником для свойства DataContext можно назначить набор DataSet инфраструктуры ADO.NET, а не DataTable. DataSet считается аналогом реляционной БД, только находится в оперативной памяти. Поскольку объект DataSet способен одновременно инкапсулировать много различных таблиц-объектов DataTable (и воспроизводить отношения между ними), то в выражениях привязки интерфейсных элементов XAML следует делать уточнения, к какой именно таблице набора привязываться. Рассмотрим это на примере вкладки Page4.

Загрузим в набор данных ADO.NET три таблицы сразу, которые в дальнейшем, при необходимости, можно будет использовать для рассмотрения других способов привязки. Но в текущей вкладке Page4 привяжем для отображения только одну таблицу Employees, чтобы глубже почувствовать разницу с предыдущими способами на одинаковом результате. Мы уже неоднократно убеждались в том, что .NET Framework позволяет решать одну и ту же задачу разными способами - поистине мощная библиотека.

  • Дополните класс StoreNorthwindDB в одноименном файле новым методом ReadDataSet() со следующим содержимым
//*********************************************************
        // Метод извлечения нетипизированного набора 
        // данных из хранилища данных Northwind.mdb
        //*********************************************************
        DataSet ds = null;// Ссылка на нетипизированный набор DataSet
        public DataSet ReadDataSet()
        {
            // Загрузим набор данных только один раз
            if (ds != null)
                return ds;
    
            ds = new DataSet();// Создаем множественный набор данных
    
            // Заполняем множественный набор данных из БД
            using (OleDbConnection conn = new OleDbConnection(connectionString))
            {
                OleDbCommand selectCommand = conn.CreateCommand();
                OleDbDataAdapter adapter = new OleDbDataAdapter(selectCommand);
    
                // Загружаем всю таблицу Employees 
                selectCommand.CommandText = "SELECT * FROM Employees";
                adapter.Fill(ds);// Сам открывает, загружает и закрывает
                // Назначаем загруженным данным в наборе такое же имя,
                // как и в хранилище, чтобы в дальнейшем не путаться
                ds.Tables[0].TableName = "Employees";
    
                // Загружает всю таблицу Customers
                selectCommand.CommandText = "SELECT * FROM Customers";
                // Назначаем загружаемым данным в наборе имя, в этом способе
                // надо переопределить дежурное имя Table до заполнения набора
                adapter.TableMappings.Add("Table", "Customers");// Должен стоять перед
                adapter.Fill(ds);                               // Должен стоять после
    
                // Загружает всю таблицу Orders
                selectCommand.CommandText = "SELECT * FROM Orders";
                // Загружаем и сразу этой порции данных назначаем имя в наборе
                adapter.Fill(ds, "Orders");
    
                // Для проверки способов именования в панели Output режима Debug
                System.Diagnostics.Debug.WriteLine(ds.Tables[0].TableName);
                System.Diagnostics.Debug.WriteLine(ds.Tables[1].TableName);
                System.Diagnostics.Debug.WriteLine(ds.Tables[2].TableName);
    
                return ds;
            }
        }

Чтобы развеять сомнения в том, что таблицы действительно загружены в набор данных и получили заданные имена, в конце метода ReadDataSet() добавлен отладочный код, который работает только для режима Debug. Он выводит результат в панель Output оболочки и его удалять необязательно, потому что в рабочем режиме Release он сам отключится.

  • Чтобы проверить работу метода, вставьте его вызов в обработчик Window_Loaded() класса Window1 файла Window1.xaml.cs так
private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Page1();
            Page2();
            Page3();
            App.StoreNorthwindDB.ReadDataSet();
        }
  • Запустите проект и проверьте отладочный вывод в панели Output (панель можно открыть командой меню оболочки View/Output )

В конце отладочной информации найдем строки

Employees

Customers

Orders

Можно для пробы поменять имена таблиц TableName в наборе данных объектной модели метода и убедиться, что код верно реагирует на эти изменения. При этом строки самих SQL -запросов трогать не следует, поскольку это зона ответственности самого хранилища данных, а не объектной модели ADO.NET.

Теперь осталось сделать вкладку Page4 для размещения интерфейсных элементов отображения данных и привязать к ним заполненный набор данных.

  • Добавьте в контейнер TabControl файла Window1.xaml копию вкладки Page3 и сделайте очевидные переименования, чтобы разметка новой вкладки Page4 стала такой
<!-- Привязка к таблице Employees через объект DataSet из ADO.NET -->
            <TabItem Header="Page4">
                <Grid
                    DataContext="{Binding ElementName=listEmployees4, Path=SelectedItem}"
                    >
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="Auto" />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                        <RowDefinition Height="Auto" />
                    </Grid.RowDefinitions>
                    
                    <ListBox 
                        Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                        Margin="0,0,0,3"
                        ScrollViewer.VerticalScrollBarVisibility="Auto"
                        Name="listEmployees4"
                        ItemsSource="{Binding}" DisplayMemberPath="FullName"
                        />
                    
                    <TextBlock Grid.Row="1" Margin="0,0,5,0">EmployeeID:</TextBlock>
                    <TextBox Grid.Row="1" Grid.Column="1" 
                        Text="{Binding Path=EmployeeID, Mode=OneWay}"
                        Focusable="False"
                        />
                    
                    <TextBlock Grid.Row="2">FullName:</TextBlock>
                    <TextBox Grid.Row="2" Grid.Column="1" 
                        Text="{Binding Path=FullName, Mode=OneWay}"
                        Focusable="False"
                        />
                    
                    <TextBlock Grid.Row="3">Address:</TextBlock>
                    <TextBox Grid.Row="3" Grid.Column="1" 
                        Text="{Binding Path=Address, Mode=OneWay}"
                        Focusable="False"
                        />
                    
                    <TextBlock Grid.Row="4">BirthDate:</TextBlock>
                    <TextBox Grid.Row="4" Grid.Column="1" 
                        Text="{Binding Path=BirthDate, Mode=OneWay}"
                        Focusable="False"
                        />
                    
                    <TextBlock Grid.Row="5">Region:</TextBlock>
                    <TextBox Grid.Row="5" Grid.Column="1" 
                        Text="{Binding Path=Region, Mode=OneWay}"
                        Focusable="False"
                       />
                </Grid>
            </TabItem>

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

#region Вкладка Page4
        private void Page4()
        {
            // Настраиваем списковый элемент ListBox
            listEmployees4.SelectedIndex = 0;
            listEmployees4.Focus();
    
            // Назначаем источником набор данных DataSet
            listEmployees4.DataContext =
                App.StoreNorthwindDB.ReadDataSet();
        }
        #endregion
  • Примените в обработчике события Loaded класса Window1 вызов функции Page4()
private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Page1();
            Page2();
            Page3();
            //App.StoreNorthwindDB.ReadDataSet();
            Page4();
        }
  • Измените дескриптор списка вкладки Page4 следующим образом
Модификация элемента ListBox вкладки Page2 файла Window1.xaml
Было
<ListBox 
                        Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                        Margin="0,0,0,3"
                        ScrollViewer.VerticalScrollBarVisibility="Auto"
                        Name="listEmployees4"
                        ItemsSource="{Binding}" DisplayMemberPath="FullName"
                        />
Стало
<ListBox 
                        Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                        Margin="0,0,0,3"
                        ScrollViewer.VerticalScrollBarVisibility="Auto"
                        Name="listEmployees4"
                        ItemsSource="{Binding Path=Employees}" DisplayMemberPath="FullName"
                        />
  • Запустите приложение - вкладка Page4 стала работать, но только частично

Текстовые поля TextBox функционируют (кроме поля FullName ) и список тоже, только в нем не отображается информация, установленная в настройках DisplayMemberPath="FullName".

  • При запущенном приложении загляните в панель Output оболочки

Мы видим, что механизм привязки хоть и не возбуждает исключений, но под управлением оболочки все-таки сигнализирует об ошибке, выдавая серию предупреждений, примерно таких

System.Windows.Data Error: 39 : BindingExpression path error: 
  'FullName' property not found on 'object' ''DataRowView' (HashCode=48835636)'.
     BindingExpression:Path=FullName; DataItem='DataRowView' (HashCode=48835636);
    target element is 'TextBox' (Name=''); target property is 'Text' (type 'String')
  • Установите в разметке настроек списка ListBox вкладки Page4 другое значение отображаемого столбца объектной модели источника
Модификация элемента ListBox вкладки Page2 файла Window1.xaml
Было
<ListBox 
                        Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                        Margin="0,0,0,3"
                        ScrollViewer.VerticalScrollBarVisibility="Auto"
                        Name="listEmployees4"
                        ItemsSource="{Binding Path=Employees}" DisplayMemberPath="FullName"
                        />
Стало
<ListBox 
                        Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"
                        Margin="0,0,0,3"
                        ScrollViewer.VerticalScrollBarVisibility="Auto"
                        Name="listEmployees4"
                        ItemsSource="{Binding Path=Employees}" DisplayMemberPath="FirstName"
                        />
  • Запустите приложение - список стал отображать столбец FirstName, который был загружен из хранилища, а текстовый элемент для поля FullName, которое мы хотели показать, попрежнему остается пустым

Причина тут в том, что физически столбца с именем FullName ни в хранилище, ни в объектной модели набора данных просто не сущестует. Он является вычислимым, то есть формируется налету при извлечении данных. Ранее мы его могли использовать потому, что заранее придумали в выражении SQL -запроса и он попадал в коллекцию источника уже заполненным.

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

При выполнении в лабораторной работе упражнения №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" или один из зависимых от них компонентов. Не удается найти указанный файл.

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