Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Visual1.doc
Скачиваний:
8
Добавлен:
07.03.2016
Размер:
4.35 Mб
Скачать

8.3. Ускладнений приклад зі списками

Приклад роботи знаходиться у папці DISK\dialog\dialog3.

Створимо додаток з назвою List_Box. Створимо клас діалогу з назвою CListDemo. Додамо у діалогове вікно через редактор ресурсів 2 елемента CListBox, 8 кнопок CButton (рис. 8.5). Елементи CListBox будуть відрізнятись між собою тим що у елементі List2 будуть встановлені властивості сортованого списку (LBS_SORT) та множинного вибору елементів (LBS_EXTENDEDSEL) а у елементі List1 цих властивостей не буде. Для програмування цих елементів додамо 2 змінні List1 та List2 типу CListBox, які повяжемо через майстер із елементами діалогу, а також функції відгуку OnCop_1_2, OnCop_2_1, OnButton3 - OnButton8 для кнопок. Клас буде мати наступний вид:

class CListDemo : public CDialog

{

.............................................................

//{{AFX_DATA(CListDemo)

enum { IDD = IDD_DIALOG1 };

CListBox* li;

CListBox List1;

CListBox List2;

//}}AFX_DATA

.............................................................

virtual void DoDataExchange(CDataExchange* pDX);

// DDX/DDV support

.............................................................

//{{AFX_MSG(CListDemo)

afx_msg void OnCop_1_2();

afx_msg void OnCop_2_1();

afx_msg void OnButton3();

afx_msg void OnButton4();

afx_msg void OnButton5();

afx_msg void OnButton6();

afx_msg void OnButton7();

afx_msg void OnButton8();

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

Рис. 8.5. Діалогове вікно CListDemo

Перші дві функції OnCop_1_2 і OnCop_2_1 будуть виконувати копіювання вибраних данних з одного елементу CListBox у інший.

//Копіювання інформації з 1-го лістбоксу в 2-й

void CListDemo::OnCop_1_2()

{

CString str;

int index = List1.GetCurSel();

if (index > -1)

{

List1.GetText(index,str);

List2.AddString(str);

List1.DeleteString(index);

}

}

//Копіювання інформації з 2-го лістбоксу у 1-й

void CListDemo::OnCop_2_1()

{

CString str;

int i;

if(List2.GetSelCount() > 0)

for(i = List2.GetCount()-1;i>=0;i--)

if (List2.GetSel(i))

{

List2.GetText(i,str);

List1.AddString(str);

List2.DeleteString(i);

}

}

Так як у елементі List1 не встановлено множинного вибору елементів то функція OnCop_1_2 буде мати більш простий код ніж OnCop_2_1. Алгоритм роботи функції OnCop_1_2 складається з таких кроків: 1) визначити обраний елемент у 1-му списку, 2) отримати текст із обраного елементу, 3) вставити текст у 2-й список, 4) видалити текст з 1-го списку. Функція GetCurSel повертає індекс вибраного елементу, якщо елемент не вибрано то отримуємо -1. Функція GetText повертає текст у рядкову змінну. Функція AddString додає у список рядок тексту. Функція DeleteString видаляє вказаний елемент списку. Алгоритм роботи функції OnCop_2_1 буде відрізнятись наявністтю циклу, оскільки вибраних елементів може бути декілька. У плані реалізації відмінності полягають при використанні функцій GetSelCount (для визначення кількості вибраних елементів) і GetSel, яка повертає значення 1, якщо елемент вибраний та 0 у протилежному випадку. Функція GetCount визначає загальну кількість рядків у списку.

Функції відгуку OnButton3 і OnButton4 видаляють вибрані елемент(и) із списків, використовуючи користувацьку функцію DelListItem:

void CListDemo::OnButton3()

{

DelListItem(1);

}

void CListDemo::OnButton4()

{

DelListItem(2);

}

//Видалення елементу/елементів із 1-го/2-го лістбокса

void CListDemo::DelListItem(int kod)

{

li = &List2;

if (kod == 1) li = &List1;

if (li->GetSelCount())

for(int i = li->GetCount()-1;i>=0;i--)

if (li->GetSel(i)) li->DeleteString(i);

}

Вхідним параметром для функції DelListItem є змінна kod, яка характеризує список. Якщо kod дорівнює 1 будемо працювати з першим списком, якщо kod дорівнює 2 – з другим списком. У першому випадку вказівці li привласнюється адреса першого списку List1, у другому випадку – адреса другого списку List2.

Для універсального видалення довільної кількості вибраних елементів використовується цикл. Зверніть увагу на те, що цикл йде у зворотному порядку. Це пояснюється тим, що при видаленні певного елемента індекси усіх наступних елементів зменшуються на 1. Тому цикл по видаленню елементів доцільно організовувати починаючи з кінця із зменшенням індексу. Перше значення змінної циклу дорівнює li->GetCount()-1, тому що індексація рядків починається з 0.

Функції OnButton5 і OnButton6 додають елементи у списки, використовуючи користувацьку функцію AddItem:

void CListDemo::OnButton5()

{

AddItem(1);

}

void CListDemo::OnButton6()

{

AddItem(2);

}

//Додавання елемента у 1-й/2-й лістбокс

void CListDemo::AddItem(int kod)

{

CInputDialog dd("Enter item to add to the list","Hello!");

li = &List2;

if (kod == 1) li = &List1;

if (dd.DoModal() == 1)

li->AddString(dd.str3);

}

Клас CInputDialog призначений для введення даних до списку.

Ключовою властивістю елементу Listbox є властивість Itemdata (реалізована функціями GetItemData() та SetItemData()). Відповідна властивість містить цілі числа для кожного рядка елементу списку. Кожен елемент, що відображається елементом Listbox складається з двох частин: рядки та числа.

Властивість Itemdata – це значення типу DWORD (long), яка може зберігати, числову інформацію, що відноситься до пункту списку, але що не відображається в списку. Так у видимому полі можна зберігати список імен співробітників, а в полі Itemdata – дані про їх зарплату. Доступ до записів про кожного співробітника можна отримувати по імені, а зарплату відображати в елементі Edit, поміщеному на той же діалог.

Проте цінність властивості Itemdata полягає не в зберіганні додаткової інформації, що відноситься до пункту списку. Припустимо, необхідно підтримувати список імен і адрес. Зберігати всі ці записи в елементі Listbох непрактично: елемент має бути дуже широким, щоб вміщати весь рядок з ім'ям, адресою, містом, номером телефону тощо.

Раціонально зберігати в елементі Listbox імена, використовуючи їх як ключі для доступу до елементів масиву, в якому зберігається решта інформації. Кожен запис зберігається в елементі масиву, відповідному одному з пунктів елементу Listbox. Якщо встановити значення властивості Itemdata рівним індексу відповідного елементу масиву даних, то при необхідності можна отримати швидкий доступ до записів масиву. Такий підхід демонструється в наступному прикладі.

Створимо клас діалогу з назвою CKeyList. Додамо у діалогове вікно через редактор ресурсів елемент CListBox, 4 текстових поля CEdit (рис. 8.6):

class CKeyList : public CDialog

{

// Construction

public:

CKeyList(CWnd* pParent = NULL); // standard constructor

CString DataArray[1000][4];

int ArrayIndex;

bool vr;

int Search(CString KeyField);

void ClearFields();

void ShowButtons();

void ShowRecord();

// Dialog Data

//{{AFX_DATA(CKeyList)

enum { IDD = IDD_DIALOG3 };

CEdit txtTitle;

CEdit txtAuthor;

CEdit txtPublisher;

CEdit txtISBN;

CListBox List1;

//}}AFX_DATA

.............................................................

// Generated message map functions

//{{AFX_MSG(CKeyList)

virtual BOOL OnInitDialog();

afx_msg void OnButton1();

afx_msg void OnButton2();

afx_msg void OnButton3();

afx_msg void OnButton4();

afx_msg void OnSelchangeList1();

afx_msg void OnChangeEdit1();

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

Рис. 8.6. Діалогове вікно CKeyList

Список (ListBox) у даній програмі використовується для зберігання ключів, тобто ISBN книг. Решту полів можна зберегти в масиві. На рис. 8.7. показаний спосіб зберігання ключів в елементі ListBox, а відповідних даних – в масиві. Елементи масиву Itemdata вказують на відповідні ним елементи в масиві даних, який може містити безліч полів.

Рис. 8.7. Масив Itemdata пов'язує пункти списку елементу ListBox з елементами масиву DataArray

Тепер необхідно пов'язати номери ISBN з відповідною інформацією в масиві. У елементі ListBox цей зв'язок забезпечується за допомогою властивості Itemdata, що є масивом чисел на кожен пункт списку. Кожен пункт списку має значення (що відображається в списку), до якого можна звернутися за допомогою функції GetText(), і пов'язане з ним значення, що не відображається в списку, але до нього можна звернутися за допомогою функцій SetItemData(), GetItemData().

Так інформація, яка пов’язана з першим елементом в списку зберігається в четвертому елементі масиву. Цей індекс визначається за допомогою функції List1.GetItemData(0). Стиль Sort списку включено, тому користувач побачить ключі відсортованими, що дозволить йому легко знайти будь-який пункт списку List1. Програмісту ж немає необхідності піклуватися про підтримку порядку серед елементів масиву, оскільки доступ до них здійснюється за допомогою ключів в списку. Тому можна вважати, що масив також відсортований.

Програма дозволяє вводити дані в різні поля. Після закінчення роботи користувач повинен натиснути кнопку ОК для підтвердження змін. Новий запис додається до масиву DataArray(), оголошеному таким чином:

CString DataArray[1000][4];

Елемент DataArray[i, 0] зберігає ISBN книги, елемент DataArray[i, 1] – список видавців DataArray[i, 2] – список авторів книги, DataArray[i, 3] – назви книг. Параметр i є індексом, що зберігається у полі ItemData списку. Нижче приведений код, що виконується при натисненні кнопки ОК.

// Кнопка Ok для додавання запису

void CKeyList::OnButton3()

{

CString Key;

int idx,position;

txtISBN.GetWindowText(Key);

Key.TrimLeft();

Key.TrimRight();

if (Key.GetLength()<1)

{

MessageBox("Key field must be non-empty");

return;

}

position = Search(Key);

if (position>-1)

if(MessageBox("Key exists. Replace existing record?","Okey",MB_YESNO)==IDYES)

List1.DeleteString(position);

else

{

txtISBN.SetFocus();

return;

}

ArrayIndex++;

idx = List1.AddString(Key);

List1.SetItemData(idx,ArrayIndex);

DataArray[ArrayIndex][0] = Key;

txtPublisher.GetWindowText(DataArray[ArrayIndex][1]);

txtAuthor.GetWindowText(DataArray[ArrayIndex][2]);

txtTitle.GetWindowText(DataArray[ArrayIndex][3]);

List1.SetCurSel(idx);

ShowRecord();

ShowButtons();

}

Програма читає дані, введені користувачем в різні поля, проводить пошук в списку за допомогою функції Search() і визначає, чи існує запис з таким ключем. Далі вона вставляє ключ в елемент ListBox зберігає решту полів в масиві DataArray[]. Оскільки стиль Sort списку List1 включено, введений пункт автоматично вставляється у відповідну позицію списку. Змінна ArrayIndex оголошена в класі і вказує на останній елемент масиву. При додаванні кожного нового елементу значення змінної ArrayIndex збільшується на 1. Значення змінної ArrayIndex зберігається в полі ItemData в позиції, відповідній новому доданому елементу. Функція AddString повертає індекс нового доданого в список елементу:

idx = List1.AddString(Key);

Цей індекс використовується в операторові для вибірки елементу масиву ItemData, відповідного новому пункту списку.

List1.SetItemData(idx,ArrayIndex);

Масив ItemData списку List1 містить ключі масиву даних, тому за наявності ключа ISBN можна легко здійснити вибірку назви книги або її авторів. Кожного разу при натисканні на пункті списку елементу ListBox програма повертає значення поля ItemData цього пункту і використовує його як індекс для доступу до поля назви книги в масиві DataArray[]. Нижче представлений код, що здійснює вибірку полів запису при виборі нового пункту списку.

//Відгук на вибір елементу списку

void CKeyList::OnSelchangeList1()

{

ShowRecord();

}

//Функція виведення записів в елементах управління

void CKeyList::ShowRecord()

{

if (List1.GetCurSel()<0)

{

txtISBN.SetWindowText("");

txtPublisher.SetWindowText("");

txtAuthor.SetWindowText("");

txtTitle.SetWindowText("");

return;

}

CString str;

long ItemIndex;

ItemIndex = List1.GetItemData(List1.GetCurSel());

List1.GetText(List1.GetCurSel(),str);

txtISBN.SetWindowText(str);

txtPublisher.SetWindowText(DataArray[ItemIndex][1]);

txtAuthor.SetWindowText(DataArray[ItemIndex][2]);

txtTitle.SetWindowText(DataArray[ItemIndex][3]);

}

Якщо в списку не було вибрано жодного пункту, то програма очищає вміст всіх полів. При виборі пункту програма відображає поля запису у відповідних текстових полях, викликаючи для цього підпрограму ShowRecord(), яка приховує кнопки ОК і Cancel, а потім відображає кнопки Delete і AddNew. Це відбувається після додавання користувачем нового запису і натисненні на кнопці ОК. Можна також відмовитися від введення нового запису, натиснувши на пункті списку.

Функція Search() призначена для локалізації (визначення місця) заданого пункту в списку. Елемент ListBox дозволяє визначити, чи існує вказаний пункт в списку, і дізнатися його індекс (якщо він існує). Для цього необхідно скористатися методом FindString, який знайде потрібне значення та поверне індекс шуканого елементу.

Нижче представлений код функції Search().

int CKeyList::Search(CString KeyField)

{

return List1.FindString(-1,KeyField);

}

Для локалізації пункту списку елементу ListBox можна надати користувачеві елемент Edit в полі якого можна ввести шуканий рядок. У подію Change елементу Edit вставте код, що визначає викликом функції Search() місцезнаходження пункту, ключ якого введений в поле елементу Edit. Для програмування обробника події Change елементу Edit, який містить ISBN, використовується наступний код.

//Відгук на зміну поля ISBN

void CKeyList::OnChangeEdit1()

{

if(vr) return;

CString str,str1;

int position;

txtISBN.GetWindowText(str);

str.TrimLeft();

str.TrimRight();

position = Search(str);

if (position>-1 && str.GetLength() > 3)

{

List1.SetCurSel(position);

vr = 1;

ShowRecord();

vr = 0;

return;

}

txtPublisher.SetWindowText("");

txtAuthor.SetWindowText("");

txtTitle.SetWindowText("");

}

Користувач може знаходити пункт в списку за допомогою миші або вводячи його ключ в поле ISBN. Пошук ISBN відбувається по першим чотирьом символам (str.GetLength() > 3).

Виклик діалогових вікон зробимо у функціях:

void CList_BoxDoc::OnListboxListdemo()

{

CListDemo dd;

dd.DoModal();

}

void CList_BoxDoc::OnListboxKeylist()

{

CKeyList dd;

dd.DoModal();

}

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]