Фильтры в наборах записей

   
На этом шаге мы рассмотрим алгоритм задания фильтра.

   
Мы упоминали, что набор записей можно фильтровать, задав в переменной-члене m_strFilter объекта набора
записей строку с выражением WHERE. Далее показано, как применить эту методику так, чтобы приложение
MyDBApp отображало выбранные из таблицы авторов записи, фильтруя их по
идентификатору штата. Вы создадите пункт меню, позволяющий пользователю задавать для фильтрования набора
записей код штата из таблицы авторов.

  • Обновление пользовательского интерфейса MyDBApp.
  • Работая с проектом MyDBApp, откройте в редакторе меню IDR_MAINFRAME.
    Удалите пункт Edit - в MyDBApp он не нужен.
  • На месте Edit создайте новый пункт меню &Filter, в котором разместите одну команду с именем
    &State, присвоив ей идентификатор ID_FILTER_STATE. Закройте редактор меню.


    Рис.1. Создание пункта меню &Filter

  • В редакторе панелей инструментов удалите из панели IDR_MAINFRAME кнопки, соответствующие командам удаленного
    меню Edit (ID_EDIT_CUT, ID_ EDIT_COPY и ID_EDIT_PASTE). Как Вы помните, кнопки удаляются
    перемещением их за пределы панели. Закройте редактор панелей инструментов.


    Рис.2. Панель инструментов без удаленных кнопок

  • Откройте ClassWizard и щелкните вкладку Message Maps. Выберите класс CMyDBAppDoc и
    создайте функцию-обработчик команд для идентификатора ID_ FILTER_STATE. Оставьте заданное по
    умолчанию имя функции OnFilterState().


    Рис.3. Создание функции-обработчика

  • Щелкните ОК, чтобы закрыть ClassWizard и сохранить все изменения.

   
Дальше Вы создадите диалоговое окно выбора штата Select State, в котором будут перечислены все коды штатов, указанные в таблице авторов.

  • Создание диалогового окна Select State.
  • В ResourceView щелкните правой кнопкой мыши узел Dialog и в контекстном меню выберите
    Insert Dialog, чтобы создать новый шаблон диалога. Ему нужно присвоить идентификатор IDD_STATEDIALOG и заголовок Select State.


    Рис.4. Задание нового диалога

  • Добавьте в диалоговое окно список и назначьте ему идентифитор IDC_ STATELIST.
    Расположите управляющие элементы так, как показано на рисунке 5.


    Рис.5. Внешний вид диалогового окна

  • Нажав CTRL+W, откройте ClassWizard и создайте класс диалога для шаблона IDD_STATEDIALOG. Назовите его CStateDialog.
    Рис.6. Создание нового класса
  • В ClassWizard на вкладке Member Variables выберите идентификатор элемента управления IDC_STATELIST. Щелкнув
    Add Variable, добавьте переменную типа CString с именем m_strState, предназначенную для хранения
    выбранного в списке значения. Еще раз щелкните Add Variable, чтобы добавить переменную CListBox
    с именем m_statelist, которая будет представлять список.


    Рис.7. Добавление переменной m_statelist


    Рис.8. ClassWizard после добавления переменных

  • На вкладке ClassWizard Message Maps в поле Object IDs щелкните
    CStateDialog, а в поле Messages выберите WM_INITDIALOG.
    Щелкните Add Function, чтобы перегрузить виртуальную функцию OnInitDialog() в Вашем классе.


    Рис.9. Подготовка к перегрузке функции

  • Щелкните Edit Code, чтобы отредактировать тело функции. Замените строку, закомментированную //TODO следующим кодом:
    CDatabase aDB;
    try 
     {
       aDB.OpenEx("DSN=MyDSN");
       // Если применяется авторизация SQL Server, 
       // укажите данные учетной записи, например 
       // aDB.OpenEx("ODBC;DSN=MyDSN;UID=sa;PWD=");
      CRecordset aRS(&aDB);
      aRS.Open(CRecordset::forwardOnly,"SELECT DISTINCT state FROM authors");
      while(!aRS.IsEOF()) 
     {
        CString strValue;
        aRS.GetFieldValue(short(0), strValue); 
        m_statelist.AddString(strValue); 
        aRS.MoveNext(); 
     }
      m_statelist.InsertString(0, "All records");
      aRS.Close(); 
      aDB.Close();
     }
     catch(CDBException *ex)
     {
       TCHAR buf[255];
       ex->GetErrorMessage(buf,255); 
       CString strPrompt(buf); 
       AfxMessageBox(strPrompt); 
     }	
    

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

   
Функция CRecordset::MoveNext() просматривает набор записей, пока CRecordset::IsEOF() не возвратит
TRUE. В обязанности функции CRecordset::GetFieldValue() входит выборка значения поля "штат" в каждой строке.
Она позволяет динамически выбирать из набора записей значение определенного поля по его индексу (который
отсчитывается от нуля). Поскольку в данном случае функция возвращает только одно поле, его значение можно получить по индексу 0.
Обратите внимание, как эта функция путем задания соответствующих переменных RFX позволяет непосредственно применять класс
CRecordset и не создавать специальный производный класс.

   
Значения, извлеченные из строк набора, помещаются в поле со списком диалогового окна. Функция
CListBox::InsertString() добавляет в начало списка пункт All records (Все записи).

   
Обратите внимание, что код работы с базой данных помеще блок try, а обработчик catch извлекает из
объекта CDBException информацию, которая сообщается пользователю.

   
И наконец, Вы создадите функцию OnFilterState(), которая будет выполнять пункт State из меню Filter.
Она открывает диалоговое окно Select State и фильтрует записи в соответствии с кодом штата, выбранным пользователем.

  • Создание функции OnFilterState().
  • В самом начале файла MyDBAppDoc.cpp добавьте следующую строку:
      #include "StateDialog.h"
    
  • Найдите созданную ранее функцию CMyDBAppDoc::OnFilterState() и замените строку комментария //TODO следующим кодом:
    CStateDialog aSD;
    if (aSD.DoModal() == IDOK)
    {
      if (aSD.m_strState == "All records")
         m_myDBAppSet.m_strFilter = ""; 
      else
         m_myDBAppSet.m_strFilter.Format("state = '%s'", aSD.m_strState);
      m_myDBAppSet.Requery();
      POSITION pos = GetFirstViewPosition(); 
      if (pos != NULL)
      {
        CView* pView = GetNextView(pos); 
        ASSERT_VALID(pView); 
        pView->UpdateData(FALSE); 
      } 
    }
    

   
Функция CRecordset::Requery() обновляет набор записей после изменения фильтра, после чего текущей
становится первая первая запись набоpa. Важно удостовериться, что Вы вызываете функцию UpdateData() для
обновления отображаемых значений в соответствии с данными новой текущей записи.

   
Вы можете реализовать возможность задавать фильтр набора на основе параметров времени выполнения, добавив
вопросительные знаки (?) в качестве знаков подстановки:

  m_myDBAppSet.m_strFilter = "state = ?";

   
Далее, во время работы приложения Вы можете обратиться к механизму RFX для замены знаков подстановки конкретными значениями. Подробнее
о параметрах в наборах записей - в разделе "Recordset: parameterizing a Recordset (ODBC)" справочной системы
Visual C++. Параметры намного эффективнее простой замены строки фильтра. При работе с параметрами базе
данных нужно обработать только один SQL-оператор SELECT, а в случае набора с фильтром без параметров
его придется выполнять при каждом изменении фильтра.

   
Текст этого приложения можно взять здесь (44,6 Кб).

   
Со следующего шага мы начнем знакомиться с ADO.



Вы можете оставить комментарий, или Трекбэк с вашего сайта.

Оставить комментарий