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

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

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

Вкладка Page2. Привязка к таблице Employees через коллекцию объектов

В данном примере задачу вкладки Page1 мы повторим несколько другим способом. Предварительную загрузку данных из хранилища мы поместим не в объект DataTable, а сразу в библиотечную коллекцию List<T> объектов промежуточного класса. И уже к этой коллекции будем привязывать пользовательский интерфейс. Для разнообразия, при заполнении объектов данных коллекции используем ADO.NET -класс OleDbDataReader, поскольку отображаемые данные пока будем только просматривать.

Коллекция объектов-данных List<T> будет служить тонкой оболочкой над извлеченными данными БД и существовать самостоятельно. В нее поместим данные, которые сразу явно преобразуем к нужному типу. Для отображения коллекций данных к ним можно привязывать списковые интерфейсные элементы WPF типа ListBox, ComboBox, ListView, а также элементы для отображения иерархических данных типа Menu, TreeView и др. Такие интерфейсные элементы порождены классом ItemsControl и их еще называют элементами управления элементами


Соответственно, в качестве коллекций, привязываемых к перечисленным элементам управления элементами WPF, можно использовать неотображаемые объекты, наследующие интерфейс System.Collections. ICollection (например, List<T>, ObservableCollection<T>, ArrayList, Array - массив), которые к инфраструктуре WPF могут и не относиться.

В отличие от элементов управления элементами существуют элементы, способные отображать только один объект, например TextBlock или TextBox. Их еще называют элементами управления содержимым (нечем управлять кроме единственного объекта внутри себя).

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

Следует заметить, что WPF, как и Windows Forms, тоже способна непосредственно привязывать интерфейсные элементы к извлеченным наборам данных DataTable и DataSet инфраструктуры ADO.NET, без всяких там классов-посредников. В этом мы убедимся чуть позднее, а пока так...

  • В панели Solution Explorer выделите узел Code и командой Add/Class добавьте в него файл Employee.cs с одноименным классом для хранения приведенных данных, который заполните так
using System;
    
namespace DataBindingTable
{
    // Класс свойств доступа к данным таблицы Employees
    public class Employee
    {
        //********************************************************
        // Cвойства доступа для привязки и базовые поля  
        //********************************************************
        int employeeID;
        public int EmployeeID
        {
            get { return employeeID; }
        }
        string fullName;
        public string FullName
        {
            get { return fullName; }
        }
        string address;
        public string Address
        {
            get { return address; }
        }
        string birthDate;
        public string BirthDate
        {
            get { return birthDate; }
        }
        string region;
        public string Region
        {
            get { return region; }
        }
    
        // Конструктор с параметрами
        public Employee(int employeeID,
            string fullName, string address,
            string birthDate, string region)
        {
            this.employeeID = employeeID;
            this.fullName = fullName;
            this.address = address;
            this.birthDate = birthDate;
            this.region = region;
        }
    
        // Конструктор по умолчанию 
        //public Employee() { }
    }
}

Класс Employee будет служить тонкой оболочкой для загруженных из хранилища данных. Каждый экземпляр этого класса будет хранить типизированные данные одной считанной из хранилища записи. Конструктор по умолчанию создавать не стали для разнообразия, чтобы подчеркнуть, что использовать будем только параметризованный конструктор.

  • В файле StoreNorthwindDB.cs добавьте в класс StoreNorthwindDB метод с именем ReadTableEmployees() для заполнения библиотечной коллекции объектов типизированных данных
//*********************************************************
        // Метод извлечения данных из таблицы Employees
        // хранилища базы данных в объект-коллекцию List<T>,
        // который называется типизированным списком
        //*********************************************************
        private List<Employee> employees = null;// Типизированный список
        public List<Employee> ReadTableEmployees()
        {
            // Читаем данные из хранилища только один раз
            if (employees != null)
                return employees;
    
            // Настраиваем доступ к хранилищу
            String cmdText = "SELECT EmployeeID, " +
                    "(LastName + ', ' + FirstName) AS FullName, " +
                    "Address, BirthDate, Region FROM Employees";
            OleDbConnection conn = new OleDbConnection(connectionString);
            OleDbCommand cmd = new OleDbCommand(cmdText, conn);
            cmd.CommandType = CommandType.Text;
    
            // Заполняем коллекцию данными из хранилища
            employees = new List<Employee>();
            try
            {
                conn.Open();
                OleDbDataReader reader = cmd.ExecuteReader();
                while (reader.Read())
                {
                    // Создать объект Employee, являющийся
                    // оболочкой для текущей записи таблицы Employees
                    Employee employee = new Employee(
                        Convert.ToInt32(reader["EmployeeID"]),
                        Convert.ToString(reader["FullName"]),
                        Convert.ToString(reader["Address"]),
                        Convert.ToString(reader["BirthDate"]),
                        Convert.ToString(reader["Region"]));
    
                    // Добавить в коллекцию
                    employees.Add(employee);
                }
            }
            catch
            {
                MessageBox.Show("Ошибка загрузки данных");
            }
            finally
            {
                conn.Close(); // Закрываем соединение
            }
    
            return employees;
        }

Метод ExecuteReader() устанавливает связь с нужной таблицей предварительно открытого соединения с БД в соответствии с настройками SQL -запроса и курсор данных таблицы вначале установлен на первую запись. Метод Read() считывает текущую запись таблицы и перемещает курсор на следующую запись. Цикл чтения записей таблицы продолжается до тех пор, пока курсор данных не установится за последней записью. Тогда метод Read() возвратит false и цикл чтения данных прервется. Пока данные считываются построчно, соединение с БД должно быть открытым. В отличие от метода OleDbDataAdapter.Fill(), соединение с БД при применении класса OleDbDataReader приходится открывать и закрывать вручную. За этим нужно строго следить, иначе данные для других пользователей останутся блокированными.

  • Добавьте в контейнер TabControl новую вкладку с именем Page2 с привязками интерфейсных элементов к общедоступным свойствам объекта данных Employee
<!-- Привязка к таблице Employees через коллекцию объектов -->
            <TabItem Header="Page2">
                <Grid
                    Name="gridEmployees2"
                    >
                    <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="listEmployees2"
                        SelectionChanged="listEmployees2_SelectionChanged"
                        />
                    
                    <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>

В выражениях привязки свойства Text элементов TextBox опущено указание источника данных, поэтому WPF будет искать его в свойстве DataContext ближайших родительских элементов. В процедурном коде мы определим источник в свойстве DataContext для элемента gridEmployees2.

  • Добавьте в класс Window1 файла Window1.xaml.cs следующий блок кода, управляющий привязкой интерфейсных элементов вкладки Page2
#region Вкладка Page2
        List<Employee> employees2;// Ссылка на типизированный список
        private void Page2()
        {
            // Загружаем данные и настраиваем источник привязки
            employees2 = App.StoreNorthwindDB.ReadTableEmployees();
            listEmployees2.SelectedIndex = 0;
            listEmployees2.Focus();
            gridEmployees2.DataContext = employees2[0];
            listEmployees2.ItemsSource = employees2;
            listEmployees2.DisplayMemberPath = "FullName";
        }
    
        private void listEmployees2_SelectionChanged(
            object sender, SelectionChangedEventArgs e)
        {
            gridEmployees2.DataContext = 
                employees2[listEmployees2.SelectedIndex];
        }
        #endregion

Здесь мы опять для удобства сгруппировали код в секцию #region. Обработчик события SelectionChanged меняет значение свойства DataContext сетки при выделении элементов списка. Тем самым будет меняться источник, к которому в разметке привязаны элементы TextBox, и соответственно будут меняться значения свойств Text, показывая новые данные.

  • Поместите в обработчик Window_Loaded() класса Window1 вызов функции Page2()
private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            Page1();
            Page2();
        }
  • Запустите приложение и испытайте работу вкладки Page2 - получим точно такую же функциональность, что и для Page1

  • Разберитесь с кодом рассмотренного примера
< Лекция 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" или один из зависимых от них компонентов. Не удается найти указанный файл.

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