Отправляет email-рассылки с помощью сервиса Sendsay
  Все выпуски  

Visual C++ - расширенное программирование Пишем файловый менеджер. Часть 2.


Информационный Канал Subscribe.Ru


Visual C++ - расширенное программирование

Выпуск № 7
Cайт : SoftMaker.fatal.ru
Архив рассылки : SoftMaker.fatal.ru
Количество подписчиков : 38

В этом выпуске

От ведущего
MFC - простое и сложное
 Создание файлового менеджера (часть 2)

Подписчикам
Вопросы
Ответы

От ведущего


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

  Как всегда, Вы можете отправить свои пожелания, кликнув по этой ссылке.

Искренне Ваш. С уважением, Вахтуров Виктор.

MFC - простое и сложное [Создание файлового менеджера (часть 2)].

Сплиттеры (продолжение).

  Как я и обещал в прошлой рассылке, сегодня мы рассмотрим процесс доработки стандартного сплиттера MFC для придания ему расширенной функциональности, необходимой нам при написании файлового менеджера.

  В прошлой статье был отмечен такой недостаток стандартного сплиттера MFC как сохранение сплиттером не относительной, а абсолютной позиции его разделителей. Это означает, что при изменении размеров окна сплиттера (что происходит обычно при изменении размеров родительского окна сплиттера, которым в большинстве случаев является окно-раммка) его разделители не двигаются. Также, естесственно, не меняются и размеры разделяемых окон.

  В соответствии с решаемой нами задачей нам хотелось бы сделать так, чтобы сплиттер сохранял именно относительную позицию разделителя (или разделителей, но сейчас нас интересует случай только двух разделяемых окон). Вот этим и займемся.

  Во-первых, нам понадобится создать класс, наследованный от класса стандартного сплиттера MFC CSplitterWnd. Сделать это обычным путем через инструмент Class Wizard не получится по одной простой причине - в выпадающем списке базовых классов в диалоге создания нового класса есть запись 'splitter', но в результате выбора ее и последующей генерации файлов описания и реализации класса, созданный класс получается наследованным от класса CMDIChildWnd, в котором в качестве переменной- компоненты и содержится объект класса CSplitterWnd. Иными словами, в данном случае создается класс дочернего окна - рамки для многооконного приложения, содержащей сплиттер в качестве дочернего окна. Это совсем не подходит нам, поэтому проще будет создать класс, наследованный от класса CWnd, а потом просто произвести контекстную замену в файлах, сгенерированных средой разработки фразы CWnd на CSplitterWnd.

  Так и сделаем.

  В проекте я создал класс CVSplitter2 (смысл названия станет ясен позже), в файлах VSplitter2.cpp и VSplitter2.h произвел контекстную замену CWnd на CSplitterWnd, в результате чего получился класс сплиттера, унаследованный от класса стандартного сплиттера MFC. Пока он еще не имеет никакой дополнительной функциональности, но мы добавим ее чуть позже.

  Теперь заменим стандартный сплиттер MFC (который мы добавили в главное окно-рамку нашего приложения в прошлый раз) на наш, только что созданный. Для этого мы просто поменяем объявление объекта класса сплиттера, находящееся в классе CMainFrame (файл MainFrm.h) :

 
class CMainFrame : public CFrameWnd 
{ 
// ....... 
 
 CSplitterWnd m_wndSplitter; 
 
// ....... 
}

заменив его на объект класса нашего сплиттера :

 
class CMainFrame : public CFrameWnd 
{ 
// ....... 
 
 CVSplitter2 m_wndSplitter; 
 
// ....... 
}

при этом не забудем подключить соответствующий заголовочный файл VSplitter2.h .

  Теперь нам осталось 'всего лишь' добавить нужную нам функциональность.

  Тут я вынужден сделать маленькую оговорку. Дело в том, что сейчас нас интересует сплиттер, разделяющий всего два окна (больше нам и не надо), поэтому в данный момент мы решим задачу исходя именно из этого условия. Но при желании вы всегда сможете легко доработать уже существующий класс, добавив совсем немного кода, для работы сплиттера с количеством разделяемых окон больше двух. Кстати, именно поэтому класс и называется CVSplitter2 (это насчет 'двойки' на конце).

  Итак, начнем...

  Идея реализации задуманного нами весьма проста и напрашивается сама собой. Надо просто сохранять относительное значение разделителя в какой то момент времени, а потом, при изменении размеров окна сплиттера, вычислять и устанавливать эту же самую позицию. Посмотрим, во что это выльется.

  Для начала неплохо было бы исследовать класс стандартного сплиттера MFC на предмет выявления методов и виртуальных функций, которые могли бы пригодиться нам при решении поставленной задачи. И еще неплохо было бы в общих чертах представить механизм работы сплиттера, что поможет в дальнейших изысканиях, а также даст ответ на первый вопрос.

  Должен заметить также, что в данной статье мы рассмотрим лишь нужные для решения поставленной задачи фрагменты кода сплиттера и принципы его работы. Более основательное рассмотрение всех тонкостей и ньюансов будет произведено в статье, которая появится скоро на сайте рассылки (SoftMaker.fatal.ru).

  Итак, исходный код сплиттера находится в файле winsplit.cpp в подкаталоге MFC/SRC того каталога, куда установлен пакет Visual Studio. В процессе совершенствования нашего проекта мы часто будем обращаться к исходному коду MFC. И данный случай - не исключение.

  Среди документированых методов класса CSplitterWnd выделим несколько тех, что сразу бросаются в глаза и которыми мы скоро воспользуемся :

 
CSplitterWnd::GetRowInfo  
CSplitterWnd::SetRowInfo 
CSplitterWnd::GetColumnInfo 
CSplitterWnd::SetColumnInfo 
CSplitterWnd::RecalcLayout 
  Среди этих методов стоит выделить виртуальную функцию RecalcLayout. Ее необходимо вызывать после вызова методов, изменяющих состояние кологок и столбцов сплиттера. То есть ее вызов необходим после вызова методов CSplitterWnd::SetRowInfo или CSplitterWnd::SetColumnInfo.

  Но преступим к делу.

  В декларации класса CVSplitter2 объявим переменную для хранения процентного отношения ширины или высоты окна панели с координатами (0, 0) к ширине или высоте (соответственно), окна сплиттера :

  В VSplitter2.h добавим :

 
class CVSplitter2 : public CSplitterWnd 
{ 
 //... 
 
 protected: 
  double m_SpltPercent; 
 
 //... 
}

  В конструкторе класса проинициализируем эту переменную :

 
 CVSplitter2::CVSplitter2() 
 { 
  m_SpltPercent = 0.5; 
  ... 

  Теперь неплохо бы было сделать так, чтобы значение переменной m_SpltPercent обновлялась при перемещении разделителя сплиттера.

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

  Такие функции есть. Их прототипы выглядят так :

 
virtual void TrackRowSize(int y, int row); 
virtual void TrackColumnSize(int x, int col); 

  Это защищенные виртуальные функции-члены класса. Весьма полезные, но недокументированные. Но мы выведем их из тени. Пускайработают на нас.

  Опишем их в нашем классе. Но в их реализации, будем пока лишь вызывать реализацию базового класса :

 
void CVSplitter2::TrackRowSize(int y, int row) 
{ 
 CSplitterWnd::TrackRowSize(y, row); 
} 
 
void CVSplitter2::TrackColumnSize(int x, int col) 
{ 
 CSplitterWnd::TrackColumnSize(x, col); 
} 

  Теперь все очень просто - надо узнать размер окна сплиттера и вычислить процентное отношение передаваемого значения (x или y - мы ведь помним, что делаем наш сплиттер только для случая двух разделяемых окон) к высоте или ширине окна.

  Для нахождения геометрических размеров клиентской области окна обычно используют функцию GetClientRect, но более правильно в нашем случае воспользоваться функцией-членом класса CSplitterWnd:;GetInsideRect (она также не описана в документации). Она возвращает результат аналогично GetClientRect, но учитывает общие полосы прокрутки, которые могут быть созданы при помощи функции CSplitterWnd::CreateScrollBarCtrl. К тому же, GetInsideRect используется самим сплиттером практически во всех случаях. Поэтому, если мы хотим, чтобы наш класс был универсальным, надо использовать именно эту функцию.

  Следующее, что надо учесть при расчете значения переменной m_SpltPercent - это толщину разделителя сплиттера. Общая толщина разделителя содержится в переменных-компонентах класса m_cxSplitterGap и m_cySplitterGap.

  Теперь напишем новую реализацию только что созданных функций :

 
void CVSplitter2::TrackRowSize(int y, int row) 
{ 
 ASSERT(row == 0); 
  
 CRect rc; 
 
 GetInsideRect(rc); 
  
 m_SpltPercent = double(y) / double(rc.Height() - m_cySplitterGap); 
 
 CSplitterWnd::TrackRowSize(y, row); 
} 
 
void CVSplitter2::TrackColumnSize(int x, int col) 
{ 
 ASSERT(col == 0); 
 
 CRect rc; 
 
 GetInsideRect(rc); 
 
 m_SpltPercent = double(x) / double(rc.Width() - m_cxSplitterGap); 
 
 CSplitterWnd::TrackColumnSize(x, col); 
} 

  Хотя в реальной жизни будет вызываться либо TrackRowSize, либо TrackColumnSize, так как сплиттер будет либо вертикильным либо горизонтальным, но мы реализовали обе функции из соображений универсальности (вдруг понадобится также горизонтальный сплиттер).

  Давайте теперь сделаем решительный шаг к наращиванию функциональности нашего класса. Мы переопределим виртуальную функцию RecalcLayout, создав следующую ее реализацию (опять учтя возможность создания горизонтального сплиттера) :

 
void CVSplitter2::RecalcLayout() 
{ 
 CRect rc; 
 
 GetInsideRect(rc); 
 
 if(GetRowCount() > 1) 
 { 
  int cyIdeal = int(m_SpltPercent * (rc.Height() - m_cySplitterGap)); 
 
  if(cyIdeal < 0) 
   cyIdeal = 0; 
 
  SetRowInfo(0, cyIdeal, 0); 
 } 
 else 
 { 
  int cxIdeal = int(m_SpltPercent * (rc.Width() - m_cxSplitterGap)); 
 
  if(cxIdeal < 0) 
   cxIdeal = 0; 
 
  SetColumnInfo(0, cxIdeal, 0); 
 } 
 
 CSplitterWnd::RecalcLayout(); 
} 

  Пока в теле этой функции мы вызываем реализацию базового класса CSplitterWnd::RecalcLayout().

  Здесь стоит отметить, что сплиттер сам вызывает метод RecalcLayout в следующих случаях :

- при изменении размеров окна самого сплиттера (из функции CSplitterWnd::OnSize),
- при изменении состояния (функции CSplitterWnd::SplitRow, CSplitterWnd::SplitColumn, CSplitterWnd::DeleteRow, CSplitterWnd::DeleteColumn),
- при окончании перетаскивания разделителя (CSplitterWnd::StopTracking),
- при изменении разрешения дисплея (CSplitterWnd::OnDisplayChange).

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

  Здорово.

  Теперь сделаем методы для установки и получения значения процентного положения разделителя сплиттера (фактически переменной m_SpltPercent).

  Добавим еще две функции :

 
double CVSplitter2::GetSplitPercent(); и 
void CVSplitter2::SetSplitPercent(double percent); 

  С функцией CVSplitter2::GetSplitPercent() все просто - она должна лишь возвращать значение m_SpltPercent, поэтому сделаем ее inline - функцией и реализацию поместим в VSplitter2.h :

 
inline double CVSplitter2::GetSplitPercent() 
{ return m_SpltPercent; } 

  Реализация CVSplitter2::SetSplitPercent чуть посложнее :

 
void CVSplitter2::SetSplitPercent(double percent) 
{ 
 m_SpltPercent = percent; 
 
 if(::IsWindow(GetSafeHwnd())) 
 { 
  if(GetDlgItem(IdFromRowCol(0, 0))) 
   RecalcLayout(); 
 } 
} 

  Здесь мы устанавливаем значение переменной, и если окно сплиттера уже создано, а также созданы дочерние окна (проверяем только на существование одного окна), то вызываем RecalcLayout() для установки новых положений окон.

  На этом, в принципе, можно и остановиться, но...

  Вы не заметили, с какими стилями в нашем сплиттере созданы списки - объекты класса CListCtrl ? Я хочу обратить ваше внимание на то, что расширенного стиля WS_EX_CLIENTEDGE у списков нет, однако выглядит все так, как будто у списков установлен этот стиль. Я могу немного прояснить эту ситуацию. Дело в том, что сплиттер MFC просто 'дорисовывает' бордюр клиентской области в своих панелях, уменьшая размеры дочерних разделяемых окон на толщину рисуемого бордюра.

  Для нас это неудовлетворительно по нескольким причинам.

  Причина первая. Хотелось бы, чтобы наше приложение всегда вписывалось в стиль оформления интерфейса Windows вне зависимости от версии ОС, а сплиттер рисует бордюр клиентской области (в функции CSplitterWnd::OnDrawSplitter) при помощи двух вызовов функции CDC::Draw3dRect, что совсем не гарантирует соответствия этому требованию. Поэтому хотелось бы предоставлять операционной системе самой отрисовывать элементы пользовательского интерфейса.

  Причина вторая. Сплиттер MFC ВСЕГДА рисует псевдо-бордюр вокруг разделяемых окон, а нам это совсем не подходит, так как хотелось бы иметь возможность самим задавать вид панели сплиттера. То есть хотелось бы, чтобы панель сплиттера выглядела в соответствии со стилем окна, находящегося в ней. Тут, забегая вперед, скажу, что в нашем сплиттере мы будем инициализировать не просто списки, как сейчас, а достаточно сложную иерархию окон, корнем которой будет класс окна, наследованный от класса CWnd, и являющийся 'контейнером' для составляющий частей интерфейсной панели файлового браузера. Так вот, для создания красивого интерфейса необходимо, чтобы это окно отображалось как окно не имеющее стиля WS_EX_CLIENTEDGE, что при использовании существующего варианта сплиттера невозможно.

  Давайте же сделаем так, чтобы наш сплиттер не отрисовывал границы вокруг окон, которые он разделяет.

  Для этого есть вполне 'законное', документированное и стандартное средство - виртуальная функция класса CSplitterWnd::OnDrawSplitter. Прототип ее выглядит так :

 
virtual void OnDrawSplitter(CDC* pDC, ESplitType nType, const CRect& rect); 

  Она отвечает за всю перерисовку частей сплиттера. В качестве параметра nType в функцию передается значение, определяющее то, какую часть сплиттера надо перерисовать.

  Тип данных ESplitType объявлен так :

 
enum ESplitType { splitBox, splitBar, splitIntersection, splitBorder }; 

  функция OnDrawSplitter и константы типа ESplitType описаны в MSDN, поэтому скажу лишь, что при отрисовке бордюра панели в эту функцию передается параметр nType, равный splitBorder. Поэтому переопределим эту функцию в своем классе. А в ее реализации будем вызывать унаследованный метод только в тех случаях, когда параметр nType не будет равен splitBorder.

 
void CVSplitter2::OnDrawSplitter(CDC *pDC, ESplitType nType, const CRect &rect) 
{ 
 if(nType != splitBorder) 
  CSplitterWnd::OnDrawSplitter(pDC, nType, rect); 
} 

  Снова скомпилируем и запустим приложение.

  Здорово. Рамки вокруг окон действительно нет, но появилась еще одна небольшая проблема. Состоит она в том, что, как я писал выше, сплиттер уменьшает размер разделяемых им окон на ширину рисуемого им бордюра и теперь на том месте, где раньше рисовался бордюр, появилась неперерисовавшаяся область.

  На самом деле сплиттер не всегда уменьшает размер разделяемых окон. Если у окна стоит расширенный стиль WS_EX_CLIENTEDGE, то окно выравнивается по размеру панели сплиттера, но поверх него все равно рисуется бордюр.

  Как же убрать бордюр ?

  В классе CSplitterWnd объявлены две переменные : m_cxBorder и m_cyBorder - это толщина горизонтальной и вертикальной части бордюра соответственно. Они инициализируются в конструкторе класса и далее нигде не изменяются.

  Давайте посмотрим на код конструктора :

 
CSplitterWnd::CSplitterWnd() 
{ 
 AFX_ZERO_INIT_OBJECT(CWnd); 
 
 // default splitter box/bar sizes (includes borders) 
 
 if (!afxData.bWin4) 
 { 
  m_cxSplitter = m_cySplitter = 4; 
  m_cxBorderShare = m_cyBorderShare = 1; 
  m_cxSplitterGap = m_cySplitterGap = 4 + 1 + 1; 
  ASSERT(m_cxBorder == 0 && m_cyBorder == 0); 
 } 
 else 
 { 
  m_cxSplitter = m_cySplitter = 3 + 2 + 2; 
  m_cxBorderShare = m_cyBorderShare = 0; 
  m_cxSplitterGap = m_cySplitterGap = 3 + 2 + 2; 
  m_cxBorder = m_cyBorder = 2; 
 } 
 
#ifdef _DEBUG 
 if (GetSystemMetrics(SM_CXBORDER) != 1 || 
  GetSystemMetrics(SM_CYBORDER) != 1) 
 { 
  TRACE0("Warning: CSplitterWnd assumes 1 pixel border.\n"); 
  // will look ugly if borders are not 1 pixel wide and 1 pixel high 
 } 
#endif 
} 

  Мы видим, что здесь же инициализируются еще несколько переменных. Причем, переменные инициализируются в зависимости от версии Windows, под управлением которой работает программа (в поле bWin4 структуры afxData, которая, кстати, также недокументирована, содержится TRUE, если младший байт возвращаемого значения API - функции GetVersion() больше или равен 4, что соответствует 32-х битным версиям операционных систем (Windows 9x, Windows NT/2000/XP)).

  Как видно, в случае младших версий Windows, m_cxBorder и m_cyBorder будут равны 0.

  Давайте обнулим эти переменные и для остальных случаев, добавив код в конструктор нашего класса CVSplitter2 :

 
CVSplitter2::CVSplitter2() 
{ 
 m_SpltPercent = 0.5; 
 
 if(afxData.bWin4) 
 { 
  m_cxBorder = m_cyBorder = 0; 
 } 
} 

  Тут есть одна тонкость. В нашем проекте не объявлена структура afxData. Но зато, она объявлена в исходном коде MFC в файле afximpl.h Поэтому нам надо подключить его в файле VSplitter2.cpp :

 
 #include <..\\src\\afximpl.h> 
 ... 

  Скомпилируем и запустим приложение снова. Как видим, неперерисовываемые области исчезли, но сам разделитель сплиттера стал гораздо толще.


  Поэтому мы изменим значения еще нескольких переменных в нашем конструкторе :

 
CVSplitter2::CVSplitter2() 
{ 
 m_SpltPercent = 0.5; 
 
 if(afxData.bWin4) 
 { 
  m_cxSplitter = m_cySplitter = 3; 
  m_cxBorderShare = m_cyBorderShare = 0; 
  m_cxSplitterGap = m_cySplitterGap = 3; 
  m_cxBorder = m_cyBorder = 0; 
 } 
} 

  Вкратце поясню значения переменных :

m_cxSplitter, m_cySplitter - размер разделителя сплиттера
m_cxBorderShare, m_cyBorderShare - дополнительное расстояние с каждой стороны разделителя.
m_cxSplitterGap, m_cySplitterGap - общее расстояние между панелями сплиттера
  Вот теперь все в порядке !!! Аккуратный сплиттер, все как надо.

  Давайте теперь совершим еще один небольшой тест. Давайте установим окнам, находящимся в сплиттере стиль WS_EX_CLIENTEDGE - чтобы точно убедиться, что сплиттер работает правильно.

  Допишем пару строчек в CMainFrame::OnCreateClient :

 
m_wndLeftPane.ModifyStyleEx(0, WS_EX_CLIENTEDGE); 
m_wndRightPane.ModifyStyleEx(0, WS_EX_CLIENTEDGE); 

  Очень странно, но стиль окон как будто совсем не изменился. Мало того, разделитель сплиттера теперь не выглядит так безупречно :

  Что же произошло ? Если воспользоваться утилитой Spy из комплекта поставки Visual Studio и посмотреть с помощью нее размеры окон, находящихся в сплиттере, то можно увидеть, что окна имеют большие размеры чем панели сплиттера. При этом их неклиентская часть выходит за пределы окна сплиттера и отсекается при перерисовке.

  Это, конечно, серьезный недостаток.

  Давайте исправим положение, но сначала разберемся (хотя бы в общих чертах) с тем, как же стандартный сплиттер MFC производит переразмещение разделяемых им окон.

  Ранее мы уже рассмотрели функцию CSplitterWnd::RecalcLayout. Мы уже переопределили ее (CVSplitter2::RecalcLayout) в соответствии со своими потребностями. В самом конце тела этой функции мы вызываем ее базовую реализацию. Так вот. Эта самая базовая реализация (CSplitterWnd::RecalcLayout) и производит переразмещение всех разделяемых окон сплиттера.

  Не буду здесь приводить код этой функции (он достаточно велик для этого). При желании вы можете самостоятельно взглянуть на ее исходный код, открыв файл winsplit.cpp из подкаталога MFC/SRC каталога, в котором установлен пакет Visual Studio.

  Взглянув на код функции можно увидеть, что основная часть работы по переразмещению дочерних разделяемых окон производится не в функции RecalcLayout, а в функциях _AfxDeferClientPos и _AfxLayoutRowCol, вызываемых из RecalcLayout.

  'Неправильное' увеличение окон, имеющих стиль WS_EX_CLIENTEDGE производится в функции _AfxDeferClientPos.

  Действительно, в этой функции есть строки :

 
 // adjust for 3d border (splitter windows have implied border) 
 if ((pWnd->GetExStyle() & WS_EX_CLIENTEDGE) || 
  pWnd->IsKindOf(RUNTIME_CLASS(CSplitterWnd))) 
  rect.InflateRect(afxData.cxBorder2, afxData.cyBorder2); 

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

  Решение возникшей снова проблемы опять напрашивается само собой. Надо просто убрать ненужные строки.

  Но, к сожалению, функции _AfxDeferClientPos и _AfxLayoutRowCol - не виртуальные функции класса CSplitterWnd, это даже вообще не методы этого класса. Это глобальные функции, которые не продекларированы ни в одном заголовочном файле. Они просто реализованы в winsplit.cpp. А это означает, что для внесения изменений нам придется переписать все дерево функций. То есть полностью реализовать функциональность (с нужными изменениями) _AfxDeferClientPos и _AfxLayoutRowCol а также полностью реализовать все механизмы RecalcLayout в классе CVSplitter2.

  Сделать это довольно просто. Просто возьмем нужный нам код из 'исходников' MFC и реализуем на его основе нужную нам функциональность.

  В результате получаем (внеся нужные изменения в проект) еще две функции - члена класса CVSplitter2.

 
 static void AFXAPI _VSplitLayoutRowCol(CSplitterWnd::CRowColInfo* pInfoArray, 
  int nMax, int nSize, int nSizeSplitter); 
 
 static void AFXAPI _VSplitDeferClientPos(AFX_SIZEPARENTPARAMS* lpLayout, 
  CWnd* pWnd, int x, int y, int cx, int cy, BOOL bScrollBar); 

и дополнительный код в функции RecalcLayout.

  Здесь я не привожу код этих функций. Вы всегда можете посмотреть файлы проекта.

  В процессе внесения этих изменений мы вынуждены подключить еще один заголовочный файл afxpriv.h для того, чтобы иметь возможность использовать еще одну недокументированную функцию - AfxRepositionWindow.

  Также теперь в CVSplitter2::RecalcLayout мы исключили вызов родительского метода по той причине, что вся функциональность вынесена в наш класс - наследник.

  Сплиттер почти готов. Но чтобы все действительно хорошо работало, переопределим еще две виртуальные функции (для этого понадобится также позаимствовать из файла winsplit.cpp описание типа HitTestValue) :

 
CVSplitter2::GetHitRect 
CVSplitter2::StopTracking 

  Как и прежде, возьмем за основу код MFC и немного его изменим, скорректировав его с учетом нулевых значений переменных m_cxSplitter и m_cySplitter и поставленной задачи.

  Все. Сплиттер готов. Установим снова окнам-спискам стиль WS_EX_CLIENTEDGE и опробуем сплиттер в действии.

    На сегодня Все.

Автор статьи : Вахтуров Виктор.  

Исходный код проекта, рассматриваемого в статье вы можете найти на сайте рассылки SoftMaker.fatal.ru на главной странице проекта.

Подписчикам

  Дабы заранее разрешить возможные недоразумения, прошу Вас помнить, что Вопросы публикуются в рассылке только один раз. Поэтому, если Вам не ответили в этой рассылке, или ваш вопрос не был опубликован, пришлите его еще раз. Не стоит отвечать на вопрос, который был задан в предыдущей рассылке (за исключением случая, когда он снова опубликован в этой).
Для того, чтобы задать свой вопрос, пришлите письмо по этой ссылке.
Для того, чтобы ответить на вопрос, надо кликнуть по ссылке "ответить", расположенной под текстом вопроса.
Обо всех ошибках, замеченных в данной рассылке, а также своих предложениях и пожеланиях пишите сюда.

Вопросы

  Для того, чтобы задать свой вопрос, кликните этой ссылке.
Вы можете задавать любые вопросы, касающиеся программирования на языке C и C++. Это могут быть вопросы касающиеся как конструкций языка, применения библиотек классов, шаблонов (таких как MFC или STL), использования компиляторов, так и самой философии программирования на C или C++. Здесь нет ограничений - спрашивайте и получайте ответы.

  К сожалению, вопросов сегодня нет.

Ответы

  В данной рассылке нет ответов, так как не было задано вопросов в предыдущей.
Задавайте свои вопросы, и Вы обязятельно получите ответ.

Всего доброго. До встречи в следующей рассылке.

Любой из материалов, опубликованных в этой рассылке, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.


http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу

В избранное