СПІЛЬНІ ЕЛЕМЕНТИ КЕРУВАННЯ 4 страница

{public: BOOL InitInstance();};

// t1.cpp: implementation of the CMain class.

#include

#include

#include "t1.h"

#include "resource.h"

#define ID_TREE 400002

#define NUM 5

CMain::CMain()

TVS_EDITLABELS

BEGIN_MESSAGE_MAP(CMain,CFrameWnd)

ON_WM_PAINT()

ON_COMMAND(ID_TREE_EXPAND,OnExpand)

ON_COMMAND(ID_TREE_COLLAPSE,OnCollapse)

END_MESSAGE_MAP()

BOOL CApp::InitInstance()

{m_pMainWnd = new CMain;

m_pMainWnd -> ShowWindow(m_nCmdShow);

m_pMainWnd -> UpdateWindow();

return TRUE;}

CApp App;

HTREEITEM hTreeCtrl[30];

HTREEITEM hTreeCurrent;

int i;

void CMain::InitTree()

{TV_INSERTSTRUCT tvs;

TV_ITEM tvi;

tvs.hInsertAfter=TVI_LAST;

tvi.mask=TVIF_TEXT;

tvi.pszText="One";

tvs.hParent=TVI_ROOT;

tvs.item=tvi;

hTreeCtrl[0]=m_Tree.InsertItem(&tvs);

hTreeCurrent=hTreeCtrl[0];

tvi.pszText="Two";

tvs.hParent=hTreeCtrl[0];

tvs.item=tvi;

hTreeCtrl[1]=m_Tree.InsertItem(&tvs);

tvi.pszText="Three";

tvs.hParent=hTreeCtrl[1];

tvs.item=tvi;

hTreeCtrl[2]=m_Tree.InsertItem(&tvs);

tvi.pszText="Four";

tvs.hParent=hTreeCtrl[2];

tvs.item=tvi;

hTreeCtrl[3]=m_Tree.InsertItem(&tvs);

tvi.pszText="Five";

tvs.hParent=hTreeCtrl[2];

tvs.item=tvi;

hTreeCtrl[4]=m_Tree.InsertItem(&tvs);

i=5;

}

void CMain::OnPaint()

TVIF_HANDLE;

m_Tree.GetItem(&tvi);

wsprintf(str2,"Current Selection %s",tvi.pszText);

dc.TextOut(2,220,str2,strlen(str2));

void CMain::OnExpand() {m_Tree.Expand(hTreeCurrent,TVE_EXPAND);}

void CMain::OnCollapse() {m_Tree.Expand(hTreeCurrent,TVE_COLLAPSE);}

5.10.7 Використання елементів перегляду дерев у діалогових вікнах

У п. 5.10.2 вже зазначувалося, що послідовність створення елементів перегляду дерев має дещо інший вигляд. Насамперед, він визначається можливістю розмістити та встановити розмір вікна перегляду дерева безпосередньо у шаблоні діалогового вікна. Крім того, необхідно зв’язати ресурси діалогового вікна з відповідним йому об’єктом, також викликати функцію ініціалізації дерева елементів. Усі ці особливості стануть предметом наступної програми. В ній передбачатиметься додавання та вилучення елементів дерева, редагування текстових поміток існуючих вузлів.



Додамо, також, що у дереві даних, яке використовуватиметься у прикладі, ми використаємо окрім текстових позначок також і графічні – для надання інтерфейсу програми більшої інформативності.

Визначимо такий порядок побудови програми:

1) створити новий проект типу “Win32 Application” з опцією “empty project”;

2) додати у проект ресурс діалогового вікна (ідентифікатор IDD_DIALOG1) із шаблоном ресурсу дерева (ідентифікатор IDC_TREE1, встановлено властивість “Edit labels”), кнопок “Додати” для додавання вузлів (ідентифікатор IDC_BUTTON1), “Вилучити” – для вилучення вузлів дерева (ідентифікатор IDC_BUTTON2), “Редагувати” – для редагування текстових позначок вузлів (ідентифікатор IDC_BUTTON3) із загальним виглядом, наведеним на рисунку 5.13.

Рисунок 5.13 – Вигляд ресурсів програми для діалогового вікна з деревом

3) зберегти файл ресурсів та додати у проект;

4) викликати майстер класів (Class Wizard) та за його допомогою створити клас діалогового вікна CSDialog, відповідний створеним ресурсам;

5) також у Class Wizard, визначити об’єкти, відповідні ресурсу дерева (m_Tree класу CTreeCtrl) та полю редагування (m_Edit типу CEdit);

6) поставити за допомогою Class Wizard у відповідність кнопкам у діалоговому вікні такі функції: OnAdd() – для кнопки “Додати”, OnDelete() – для кнопки “Вилучити”, OnEdit() – для кнопки “Редагувати” (усі – на сторінці “Message map”, повідомлення – BN_CLICKED); також визначити обробку повідомлення WM_INITDIALOG – функцію OnInitDialog() для ініціалізації діалогового вікна (особливості подібних операцій детально описані у п. 5.7.4);

7) через поле структури проекту додати клас прикладки CApp (породжуваний з CWinApp, розташований у файлах SDialog1.h та SDialog1.cpp) та визначити в ньому функцію InitInstance() типу BOOL із таким кодом:

BOOL CApp::InitInstance()

{CSDialog a;



a.DoModal();

return TRUE;}

8) оголосити глобальний об’єкт прикладки: CApp App;

9) у файлі SDialog.cpp змінити директиви препроцесора #include на такі:

#include

#include

#include "resource.h"

#include "SDialog.h"

10) натиснувши “Alt+F7” змінити опції проекту на такі, що передбачають використання MFC, спробувати побудувати і виконати проект; у разі успішної побудови та виконання продовжити побудову, інакше – виправити помилки;

11) у клас CSDialog додати об’єкт списку зображень для зберігання пікто-грам, відповідних вузлам дерева:

CImageList m_treeImageList;

12) за допомогою редактора ресурсів створити пікторами вузлів: IDI_ICON1, IDI_ICON2, IDI_ICON2;

13) визначити глобальні змінні для зберігання вузлів дерева:

HTREEITEM hTreeCtrl[30]; // масив дескрипторів вузлів

HTREEITEM hTreeCurrent; // дескриптор поточного вузла

int i; // кількість вузлів дерева

14) у клас CSDialog додати функцію void InitTree() та визначити її код у вигляді, визначеному прикладом 5.10, здійснити виклик InitTree() з функції ініціалізації діалогового вікна OnInitDialog();

Приклад 5.10 – Ініціалізація дерева для діалогового вікна

void CSDialog::InitTree()

TVIF_IMAGE

15) реалізувати функції OnAdd(), OnDelete(), OnEdit() у спосіб, наведений у прикладі 5.11:

Приклад 5.11 – Реалізація функцій додавання, вилучення та редагування вузлів

void CSDialog::OnAdd()

{// TODO: Add your control notification handler code here

if(!m_Tree.GetSelectedItem())

MessageBox("Вузол не обрано",""); // жоден вузол дерева не обрано

else {i++; char str[20];

m_Edit.GetWindowText(str,sizeof(str));

if(strlen(str))

TVIF_IMAGE else MessageBox("Задайте ім’я",""); // ім’я вузла є порожнім

}}

void CSDialog::OnDelete()

{// TODO: Add your control notification handler code here

if(!m_Tree.GetSelectedItem()) // жоден вузол дерева не обрано

MessageBox("Вузол не обрано","");

else {hTreeCurrent=m_Tree.GetSelectedItem();

if(!m_Tree.GetParentItem(hTreeCurrent)) // заборона вилучати корінь дерева

MessageBox("Не можна вилучити корінь","");

else {m_Tree.DeleteItem(hTreeCurrent); i--;

hTreeCurrent=hTreeCtrl[i--];

InvalidateRect(NULL);

}}}

void CSDialog::OnEdit()

{ // TODO: Add your control notification handler code here

hTreeCurrent=m_Tree.GetSelectedItem();

m_Tree.EditLabel(hTreeCurrent);

}

16) забезпечити редагування вузлів дерева також і за допомогою натискання правої клавіші миші на будь-якому з вузлів, для чого у Class Wizard вибрати ідентифікатор об’єкта дерева IDC_TREE1 та його повідомлення NM_RCLICK, що дозволить додати функцію OnRclickTree1():

void CSDialog::OnRclickTree1(NMHDR* pNMHDR, LRESULT* pResult)

{ // TODO: Add your control notification handler code here

hTreeCurrent=m_Tree.GetSelectedItem();

m_Tree.EditLabel(hTreeCurrent);

*pResult = 0;

}

Функція діє так само, як і OnEdit(), аде для її використання необхідно лише обрати вузол, текстову позначку якого необхідно відредагувати. Одна тількі неприємність: результат редагування за допомогою функцій OnEdit(), OnRclickTree1() після задання нової позначки не зберігається і програма не реагує на повідомлення про початок редагування вузла та закінчення процесу редагування (TVN_BEGINLABELEDIT та TVN_ENDLABELEDIT). Для уникнення проблеми додамо обробку цих повідомлень.

17) за допомогою Class Wizard для класу CSDialog оберемо обробку повідомлення WM_NOTIFY і таким чином оголосимо функцію:

BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);

Її необхідно реалізувати у зазначений у прикладі 5.12 спосіб:

Приклад 5.12 – Реалізація обробки повідомлення нотифікації у діалоговому вікні

BOOL CSDialog::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)

{// TODO: Add your specialized code here and/or call the base class

TV_DISPINFO* tv_dispInfo = (TV_DISPINFO*) lParam;

if (tv_dispInfo->hdr.code == TVN_BEGINLABELEDIT)

CEdit* pEdit = m_Tree.GetEditControl();

else if (tv_dispInfo->hdr.code == TVN_ENDLABELEDIT)

{ if (tv_dispInfo->item.pszText != NULL)

m_Tree.SetItemText(tv_dispInfo->item.hItem, tv_dispInfo->item.pszText);

}

return CDialog::OnNotify(wParam, lParam, pResult);

}

Як результат виконання зазначеної послідовності маємо отримати результат, наведений на рисунку 5.14.

Рисунок 5.14 – Вигляд діалогового вікна із реалізацією функцій дерева

Повний тест програми наведено у прикладі 5.13.

Приклад 5.13 – Текст програми із реалізацією дерева у діалоговому вікні

// SDialog.h : header file

class CSDialog : public CDialog

{public:

void InitTree();

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

// Dialog Data

//{{AFX_DATA(CSDialog)

enum { IDD = IDD_DIALOG1 };

CTreeCtrl m_Tree;

CImageList m_treeImageList;

CEdit m_Edit;

//}}AFX_DATA

// Overrides ClassWizard generated virtual function overrides

//{{AFX_VIRTUAL(CSDialog)

protected:

virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support

virtual BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult);

//}}AFX_VIRTUAL

// Implementation

protected:

// Generated message map functions

//{{AFX_MSG(CSDialog)

virtual BOOL OnInitDialog();

afx_msg void OnAdd();

afx_msg void OnDelete();

afx_msg void OnEdit();

afx_msg void OnRclickTree1(NMHDR* pNMHDR, LRESULT* pResult);

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

};

class CApp : public CWinApp

{public: BOOL InitInstance();

CApp();

virtual ~CApp();

};

// SDialog.cpp : implementation file

#include

#include

#include "resource.h"

#include "SDialog.h"

HTREEITEM hTreeCtrl[30];

HTREEITEM hTreeCurrent;

int i;

CSDialog::CSDialog(CWnd* pParent /*=NULL*/) : CDialog(CSDialog::IDD, pParent)

{//{{AFX_DATA_INIT(CSDialog) // NOTE: the ClassWizard will add member initialization here

//}}AFX_DATA_INIT

}

void CSDialog::DoDataExchange(CDataExchange* pDX)

{CDialog::DoDataExchange(pDX);

//{{AFX_DATA_MAP(CSDialog)

DDX_Control(pDX, IDC_TREE1, m_Tree);

DDX_Control(pDX, IDC_EDIT1, m_Edit);

//}}AFX_DATA_MAP

}

BEGIN_MESSAGE_MAP(CSDialog, CDialog)

//{{AFX_MSG_MAP(CSDialog)

ON_BN_CLICKED(IDC_BUTTON1, OnAdd)

ON_BN_CLICKED(IDC_BUTTON2, OnDelete)

ON_BN_CLICKED(IDC_BUTTON3, OnEdit)

ON_NOTIFY(NM_RCLICK, IDC_TREE1, OnRclickTree1)

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

BOOL CSDialog::OnInitDialog()

{CDialog::OnInitDialog(); // TODO: Add extra initialization here

InitTree();

return TRUE; }

CApp::CApp(){}

CApp::~CApp(){}

BOOL CApp::InitInstance()

{CSDialog a;

a.DoModal();

return TRUE;}

CApp App;

void CSDialog::OnAdd()

{// TODO: Add your control notification handler code here

if(!m_Tree.GetSelectedItem())

MessageBox("Node isn't selected","");

else {i++; char str[20];

m_Edit.GetWindowText(str,sizeof(str));

if(strlen(str))

hTreeCurrent=m_Tree.GetSelectedItem();

TV_INSERTSTRUCT tvs;

TV_ITEM tvi;

tvs.hInsertAfter=TVI_LAST;

tvi.mask=TVIF_TEXT else MessageBox("You should set unzero name ","");

}}

void CSDialog::OnDelete()

{// TODO: Add your control notification handler code here

if(!m_Tree.GetSelectedItem()) MessageBox("Node isn't selected","");

else {hTreeCurrent=m_Tree.GetSelectedItem();

if(!m_Tree.GetParentItem(hTreeCurrent)) MessageBox("You can't delete the root","");

else {m_Tree.DeleteItem(hTreeCurrent); i--;

hTreeCurrent=hTreeCtrl[i--];

InvalidateRect(NULL);

}}}

void CSDialog::OnEdit()

{hTreeCurrent=m_Tree.GetSelectedItem();

m_Tree.EditLabel(hTreeCurrent);}

void CSDialog::InitTree()

m_treeImageList.Create(16, 16, FALSE, 3, 0);

HICON hIcon = LoadIcon(AfxGetResourceHandle(),MAKEINTRESOURCE(IDI_ICON1));

m_treeImageList.Add(hIcon);

hIcon = LoadIcon(AfxGetResourceHandle(),MAKEINTRESOURCE(IDI_ICON2));

m_treeImageList.Add(hIcon);

hIcon = LoadIcon(AfxGetResourceHandle(),MAKEINTRESOURCE(IDI_ICON3));

m_treeImageList.Add(hIcon);

m_Tree.SetImageList(&m_treeImageList, TVSIL_NORMAL);

TV_INSERTSTRUCT tvs;

TV_ITEM tvi;

tvs.hInsertAfter=TVI_LAST;

tvi.mask=TVIF_TEXT

void CSDialog::OnRclickTree1(NMHDR* pNMHDR, LRESULT* pResult)

{// TODO: Add your control notification handler code here

hTreeCurrent=m_Tree.GetSelectedItem();

m_Tree.EditLabel(hTreeCurrent);

*pResult = 0;}

BOOL CSDialog::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)

{// TODO: Add your specialized code here and/or call the base class

TV_DISPINFO* tv_dispInfo = (TV_DISPINFO*) lParam;

if (tv_dispInfo->hdr.code == TVN_BEGINLABELEDIT)

CEdit* pEdit = m_Tree.GetEditControl();

else if (tv_dispInfo->hdr.code == TVN_ENDLABELEDIT)

{ if (tv_dispInfo->item.pszText != NULL)

m_Tree.SetItemText(tv_dispInfo->item.hItem, tv_dispInfo->item.pszText);

}

return CDialog::OnNotify(wParam, lParam, pResult);

}

5.11 Використання рядків стану

Рядок стану (або “status bar control”) є горизонтальним вікном, яке звичайно відображується у нижній частині батьківського вікна, у якому відображується інформація про стан параметрів програми. Наприклад, програма Microsoft Word відображує поточну інформацію у формі, наведеній на рис. 5.15.

Рисунок 5.15 – Приклад використання рядка стану

У MFC рядок стану забезпечується за допомогою класу CStatusBarCtrl. Для створення рядка стану необхідно виконати декілька кроків:

1) створити об’єкт типу CStatusBarCtrl у класі вікна програми;

2) викликати функцію Create() для створення вікна рядка стану, приєднати до нього об’єкт класу:

Функція Create() класу CStatusBarCtrl має такий вигляд:

BOOL CStatusBar::Create(CWnd*pParentWnd, DWORD dwStyle = WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, UINT nID=AFX_IDW_STATUS_BAR );

де pParentWnd – покажчик на батьківське вікно Windows, dwStyle - стиль рядка стану, nID – ідентифікатор дочірнього вікна панелі інструментів. Окрім стан-дартних стилів для рядка стану визначаються додаткові стилі:

Таблиця 5.12 – Стилі елемента рядка стану

Константа стилю Коментар
CСS_TOP розміщення рядка стану у верхній частині вікна
CCS_BOTTOM розміщення рядка стану у нижній частині вікна
CCS_NODIVIDER відсутність розподільників між окремими полями
CCS_NOHILITED відсутність виділення коло йором поточного поля
CCS_NOPARENTALIGN відсутність автоматичного вирівнювання при оновленні
CCS_NORESIZE відсутність автоматичної зміни розміру

Клас CStatusBarCtrl забезпечується рядом необхідних функцій, серед них зазначимо деякі.

Кількість полів у рядку стану визначає функція SetParts():

BOOL CStatusBarCtrl::SetParts( int nParts, int* pWidths );

де nParts – кількість частин рядка (не може перевищувати 255), pWidths – адреса цілочисельного масиву, який визначає позиції правого краю кожної частини (значення -1 означає, що позиція правого краю частини перевищує правий край керування).

Для кожного з визначених полів рядка функція SetText() визначає текстове заповнення:

BOOL CStatusBarCtrl::SetText ( LPCTSTR lpszText, int nPane, int nType );

де lpszText – покажчик на рядок, что встановлюється у полі; nPane – номер поля, до якого встановлюється текст; nType – режим відображення тексту (0 – текст відображується “натиснутим” у полі, SBT_NOBORDERS – текст не має рамки, SBT_OWNERDRAW – текст відображується батьківським вікном, SBT_POPOUT – текст відображується в опуклому вигляді).

Для використання у MFC-програмі елемента відображення рядка стану необхідно виконати певну послідовність кроків:

1) створити об’єкт рядка стану;

2) визначити кількість полів рядка стану;

3) визначити текст кожного поля рядка;

4) оновити текст під час змін у роботі програми.

Якщо для програми із створенням регулятора гучності звуку (приклад 5.6) передбачити відображення поточних значень гучності у каналах через рядок стану, у програму вносяться такі зміни:

1) у класі діалогового вікна оголошується об’єкт рядка стану:

class CSDialog : public CDialog

{public: CStatusBarCtrl m_Status;

………………………….

};

2) у функції ініціалізації діалогового вікна фізично створити рядок стану:

BOOL CSDialog::OnInitDialog()

CDialog::OnInitDialog();

m_sl1.SetPos(50);

m_sl2.SetPos(50);

m_sl3.SetPos(50);

CRect r;

GetClientRect(&r);

int parts[3]; // три поля

for(int i=1;i<=3;i++)parts[i-1]=r.right/3*i; // визначення координат трьох полів

m_Status.Create(WS_VISIBLE

3) модифікувати обробник OnVScroll() для відображення у рядку стану поточних значень регуляторів лівого, правого каналів, загального регулятора гучності звуку:

void CSDialog::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)

{ DWORD volume;

char str[255];

UINT uRetVal, uNumDevs;

if (uNumDevs = waveOutGetNumDevs())

{uRetVal = waveOutGetVolume(0, (LPDWORD)&volume);

if(pScrollBar==(CScrollBar*)&m_sl1)

{DWORD curVol=MAKELONG((DWORD)0xFFFF/100*m_sl1.GetPos(),

HIWORD(volume));

waveOutSetVolume(0,curVol);

wsprintf(str,"Лівий канал: %d",m_sl1.GetPos()); // формування тексту поля 0

m_Status.SetText(str,0,0); // визначення тексту поля 0

}

if(pScrollBar==(CScrollBar*)&m_sl2)

{DWORD curVol=MAKELONG(LOWORD(volume),

(DWORD)0xFFFF/100*m_sl2.GetPos()); waveOutSetVolume(0,curVol);

wsprintf(str,"Правий канал: %d",m_sl2.GetPos());

m_Status.SetText(str,1,0);

}

if(pScrollBar==(CScrollBar*)&m_sl3)

{m_sl1.SetPos(m_sl3.GetPos());

m_sl2.SetPos(m_sl3.GetPos());

DWORD curVol=MAKELONG((DWORD)0xFFFF/100*m_sl1.GetPos(),

(DWORD)0xFFFF/100*m_sl2.GetPos());

waveOutSetVolume(0,curVol);

wsprintf(str,"Лівий канал: %d",m_sl1.GetPos());

m_Status.SetText(str,0,0); // запис у перше поле

wsprintf(str,"Правий канал: %d",m_sl2.GetPos());

m_Status.SetText(str,1,0); // запис у друге поле

wsprintf(str,"Загальний: %d",m_sl3.GetPos());

m_Status.SetText(str,2,0); // запис у третє поле

}}

CDialog::OnVScroll(nSBCode, nPos, pScrollBar);

}

Результат роботи модифікованої програми наведений на рисунку 5.16.

Рисунок 5.16 – Регулятор гучності із рядком стану

5.12 Використання закладок

5.12.1 Загальна інформація про закладки

Елемент закладка (tab control) зовнішньо нагадує сторінки записника, у якому доступ до кожної сторінки діалогового вікна здійснюється її простим вибором. Редактор ресурсів Visual С++ надає можливості візуального визначення вигляду елемента (зображений на рисунку 5.17).

Рисунок 5.17 – Вигляд елемента закладка

Кожна сторінка закладки може відображати інші елементи керування, і таким чином дозволяє об’єднувати редагування декількох сторінок із властивостями програмних об’єктів. Подібними, але більш розвиненими елементами керування є сторінки властивостей, що ми розглянемо пізніше.

У MFC закладка реалізована за допомогою класу CTabCtrl, що так само як і інші спільні елементи керування породжується від класу CWnd і є спадкоємцем його головних властивостей.

Послідовність створення закладки як елемента головного вікна програми зазвичай складається з двох кроків:

1) створити об’єкт закладки у класі головного вікна;

2) використати функцію Create() класу CTabCtrl для визначення основних властивостей елемента закладки.

Функція Create() класу CTabCtrl має такий прототип:

BOOL CTabCtrl :: Create(DWORD dwStyle, RECT& rect, CWnd* pParentWnd, UINT nID);

де dwStyle – стиль закладки, rect – її розташування та розмір, pParentWnd – покажчик на батьківське вікно (має відрізнятися від NULL), nID – ідентифікатор ресурсу.

Під час створення закладки у головному вікні програми використовують стандартні стилі WS_CHILD, WS_VISIBLE, WS_BORDER а також ряд специфічних для об’єкта закладки. Деякі з властивих закладці стилів наведено у таблиці 5.13.

Таблиця 5.13 – Основні стилі елемента закладка

Константа стилю Коментар
TCS_BUTTONS надає закладкам вигляд стандартних кнопок
TCS_FIXEDWIDTH фіксує ширину усіх закладок
TCS_FOCUSNEVER виключає отримання закладкою фокусу
TCS_FOCUSONBUTTONDOWN закладка отримує фокус натисканням лівої клавіші миші (використовується виключно разом з TCS_BUTTONS)
TCS_FORCEICONLEFT визначає положення піктограми ліворуч напису закладки
TCS_FORCELABELLEFT вирівнює напис та піктограму за лівим краєм
TCS_MULTILINE можливість використання декількох рядків закладок
TCS_OWNERDRAWFIXED визначає, що батьківське вікно відображає усі закладки
TCS_RIGHTJUSTIFY вирівнює напис та піктограму за правим краєм
TCS_SHAREIMAGELISTS надає можливість використання списків зображень й іншим елементам керування
TCS_TOOLTIPS використання у закладці спливаючих підказок
TCS_TABS стандартний стиль: закладки у своїй власній формі, а не у стилі кнопок
TCS_SINGLELINE відображує усі закладки в одному рядку
TCS_RAGGEDRIGHT закладки займають не всю ширину вікна
WS_TABSTOP визначає перелік елементів керування, що можуть використовуватися для переміщення між закладками


6628040361461545.html
6628108962830778.html
    PR.RU™