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

Visual C++ - расширенное программирование Создание файлового менеджера часть 4.


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


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

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

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

От ведущего


  Здравствуйте, уважаемые подписчики !

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

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

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

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

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

Часть 4.
Панели инструментов (часть 2).

  Сегодня мы снова продолжим написание нашего файлового менеджера, пополняя и совершенствуя его пользовательский интерфейс.

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

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


  Итак, приступим.

Панель истории команд.

  Давайте рассмотрим сначала процесс создания панели истории команд нашего файлового менеджера. В нашем 'образце для подражания' - программе Total Commander эта панель расположена в нижней части главного окна программы - внизу, сразу над панелью команд (cоздание панели команд было рассмотрено предыдущей статье : http://SoftMaker.fatal.ru/subscr/vcext/year2003/side_008.htm

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

  В библиотеке MFC есть класс CDialogBar, позволяющий реализовать панель на базе шаблона диалога. Данная возможность как нельзя лучше подходит нам. Мы понаследуем класс нашей панели (назовем его CHistDialogBar) от класса CDialogBar. Как обычно, получить сразу файлы, сгенерированные инструментом ClassWizard не удасться, поэтому для класса CHistDialogBar в ClassWizard выберем в качестве базового класса класс CWnd, а потом в сгенерированных файлах произведем контекстную замену фразы 'CWnd' на 'CDialogBar'.

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

Для удобства использования создадим функцию-член класса CHistDialogBar::Create. Вот ее реализация :

 
 
 BOOL CHistDialogBar::Create(CWnd *pParentWnd) 
 { 
 if(!CDialogBar::Create(pParentWnd, IDD_HISTORY_DLG_BAR, 
  CBRS_ALIGN_BOTTOM, AFX_IDW_TOOLBAR + 256 - 2)) 
  return FALSE; 
 
 UpdateData(FALSE); 
 
 return TRUE; 
 } 

в описание класса CMainFrame добавим переменную-компоненту m_wndHistoryDlgBar класса CHistDialogBar. В функции CMainFrame::OnCreate добавим код для создания окна панели.

Пока все довольно просто.


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

Для управления положением окон элементов управления создадим объекты соответствующих классов MFC.

Добавим (вручную) в описание класса CHistDialogBar переменные :

 
 
 CStatic m_wndPathStatic; 
 CComboBox m_wndHistoryCombo; 

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

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

Добавим в класс CHistDialogBar функцию-член CHistDialogBar::DoDataExchange. В теле этой функции произведем вызовы функций механизма DDX :

 
 
 DDX_Control(pDX, IDC_HIST_DLG_BAR_HISTORY_STATIC, m_wndPathStatic); 
 DDX_Control(pDX, IDC_HIST_DLG_BAR_HISTORY_COMBO, m_wndHistoryCombo); 

с помощью чего решим поставленную задачу.

  Для переразмещения элементов управления определим и реализуем в классе CHistDialogBar обработчик сообщения WM_SIZE, в который добавим немного кода для управления положением элементов :

 
 
 void CHistDialogBar::OnSize(UINT nType, int cx, int cy)  
 { 
 CDialogBar::OnSize(nType, cx, cy); 
 
 if( ::IsWindow(m_wndPathStatic.GetSafeHwnd()) && 
  ::IsWindow(m_wndHistoryCombo.GetSafeHwnd())) 
 { 
  int x = cx / 3; 
 
  HDWP hDwp = ::BeginDeferWindowPos(2); 
 
  if(hDwp != NULL) 
  { 
   CRect rcControl; 
 
   m_wndPathStatic.GetWindowRect(&rcControl); 
   ScreenToClient(&rcControl); 
 
   ::DeferWindowPos(hDwp, m_wndPathStatic.GetSafeHwnd(), NULL, 
    0, rcControl.top, x - 3, rcControl.Height(),  
    SWP_NOCOPYBITS | SWP_NOZORDER | SWP_NOOWNERZORDER); 
 
   m_wndHistoryCombo.GetWindowRect(&rcControl); 
   ScreenToClient(&rcControl); 
 
   rcControl.left = x + 3; 
   rcControl.right = cx - 1; 
 
   ::DeferWindowPos(hDwp, m_wndHistoryCombo.GetSafeHwnd(), NULL, 
    rcControl.left, rcControl.top, rcControl.Width(), rcControl.Height(), 
    SWP_NOCOPYBITS | SWP_NOZORDER | SWP_NOOWNERZORDER); 
 
   ::EndDeferWindowPos(hDwp); 
  } 
 } 
 } 

Как видно, здесь мы воспользовались группой функций : BeginDeferWindowPos/DeferWindowPos/EndDeferWindowPos для одновременного перемещения обоих окон.

Теперь имеем такую нижнюю панель :

  Панель истории команд готова.

Расширяем функциональность CToolBarCtrl.

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

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

Очевидно, естесственным решением было бы реализовать данную панель, использовав стандартные элементы управления Windows - панели инструментов ToolBar. Так мы и поступим. Правда, их придется немного доработать.

Сначала, мы реализуем панель с двумя группами кнопок (панель с одной группой кнопок можно реализовать, просто использовав класс CToolBar MFC, но это ведь слишком просто для нас).

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

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

Таким образом мы пришли к тому, что надо иметь возможность вычислить высоту элемента управления ToolBar в зависимости от его ширины.

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

Мы просто позаимствуем код из исходников класса CToolBar MFC, немного изменив и дополнив его.

  Так как наши панели будут располагаться только горизонтально, мы напишем код только для этого случая, уменьшая тем самым объем исполняемого модуля нашей программы.


  Создадим класс (назовем его CVToolBarCtrl), базовым классом которого будет являться класс CToolBarCtrl. И дополним этот класс нужными нам возможностями.

Код следующих функций класса CVToolBarCtrl мы напишем на основе исходного кода MFC (в следствие того, что объем кода этих функций не очень мал, я приведу здесь только наиболее интересные фрагменты и описания принципов работы) :

 
 
 void CVToolBarCtrl::CalcInsideRect(CRect& rect) const 
 void CVToolBarCtrl::SetBorders(int cxLeft, int cyTop, int cxRight, int cyBottom) 
 CSize CVToolBarCtrl::CalcSize(TBBUTTON* pData, int nCount) 
 int CVToolBarCtrl::WrapToolBar(TBBUTTON* pData, int nCount, int nWidth) 
 CSize CVToolBarCtrl::SizeToolBar(TBBUTTON* pData, int nCount, int nLength) 

Функция CVToolBarCtrl::CalcInsideRect изменяет значения переменных-свойств переданного ей по ссылке в качестве параметра объекта класса CRect, рассчитывая тем самым прямоугольник, доступный для отображения кнопок элемента управления в клиентской области на основе координат полного прямоугольника клиентской области. Иными словами, эта функция уменьшает прямоугольник, ссылка на который передана в качестве параметра в зависимости от установленных величин отступов от краев элемента (которые хранятся в переменных-компонентах m_cxLeftBorder, m_cyTopBorder, m_cxRightBorder, m_cyBottomBorder)

Функция CVToolBarCtrl::SetBorders просто устанавливает значения переменных m_cxLeftBorder, m_cyTopBorder, m_cxRightBorder, m_cyBottomBorder.

Функция CVToolBarCtrl::CalcSize(TBBUTTON* pData, int nCount) рассчитывает линейные размеры элемента управления в пикселах на основании данных массива структур TBBUTTON, содержащих информацию о кнопках элемента. В оригинальном коде в этой функции использовалось значение внутренней переменной библиотеки MFC _afxComCtlVersion - про эту переменную я писал в одной из предыдущих статей. Вместо ее значения будем подставлять результат, возвращаемый напиасанной нами ранее функцией _VGetComCtlVersion.
Здесь также употребляется константа VERSION_IE4, описанная в заголовочном файле afximpl.h. Для использования ее, а также и некоторых других констант, подключим этот заголовочный файл.

Функция CVToolBarCtrl::WrapToolBar производит расстановку стиля TBSTATE_WRAP кнопкам ToolBar - а таким образом, чтобы 'подогнать' панель под желаемую ширину, переданную как параметр.

Функция CVToolBarCtrl::SizeToolBar производит многократные вызовы WrapToolBar, 'пытаясь подобрать' наилучшее расположение кнопок.

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

 
 
 int CVToolBarCtrl::WrapToolBar(TBBUTTON* pData, int nCount, int nWidth) 
 { 
 ASSERT(pData != NULL && nCount > 0); 
 
 int x = 0; 
  
 int nResult = 0; 
  
 CSize sizeButton(DefWindowProc(TB_GETBUTTONSIZE, 0, 0)); 
 
 VERIFY(sizeButton.cx || sizeButton.cy); 
 
 for (int i = 0; i < nCount; i++) 
 { 
  pData[i].fsState &= ~TBSTATE_WRAP; 
 
  if (pData[i].fsState & TBSTATE_HIDDEN) 
   continue; 
 
  int dx, dxNext; 
  
  if(pData[i].fsStyle & TBSTYLE_SEP) 
  { 
   dx = pData[i].iBitmap; 
   dxNext = dx; 
  } 
  else 
  { 
   dx = sizeButton.cx; 
   dxNext = dx - CX_OVERLAP; 
  } 
 
  if (x + dx > nWidth) 
  { 
   BOOL bFound = FALSE; 
    
   for (int j = i; j >= 0  &&  !(pData[j].fsState & TBSTATE_WRAP); j--) 
   { 
    // Find last separator that isn't hidden 
    // a separator that has a command ID is not 
    // a separator, but a custom control. 
    
    if( (pData[j].fsStyle & TBSTYLE_SEP) && 
     (pData[j].idCommand == 0) && 
     !(pData[j].fsState & TBSTATE_HIDDEN)) 
    { 
     x = 0; 
      
     i = j; 
 
     bFound = TRUE; 
 
     pData[j].fsState |= TBSTATE_WRAP; 
      
     nResult++; 
      
     break; 
    } 
   } 
 
   if(!bFound) 
   { 
    for (int j = i - 1; j >= 0 && !(pData[j].fsState & TBSTATE_WRAP); j--) 
    { 
     // Never wrap anything that is hidden, 
     // or any custom controls 
    
     if( (pData[j].fsState & TBSTATE_HIDDEN) || 
      ((pData[j].fsStyle & TBSTYLE_SEP) && 
      (pData[j].idCommand != 0))) 
      continue; 
 
     x = 0; 
      
     i = j; 
      
     bFound = TRUE; 
      
     pData[j].fsState |= TBSTATE_WRAP; 
      
     nResult++; 
      
     break; 
    } 
    if(!bFound) 
     x += dxNext; 
   } 
  } 
  else 
   x += dxNext; 
 } 
 
 return nResult + 1; 
 } 
 
 CSize CVToolBarCtrl::SizeToolBar(TBBUTTON* pData, int nCount, int nLength) 
 { 
 ASSERT(pData != NULL && nCount > 0); 
 
 int nMin, nMax, nTarget, nCurrent, nMid; 
 
 // Wrap ToolBar as specified 
 
 nMax = nLength; 
 
 nTarget = WrapToolBar(pData, nCount, nMax); 
 
 // Wrap ToolBar vertically 
 
 nMin = 0; 
 
 nCurrent = WrapToolBar(pData, nCount, nMin); 
 
 if(nCurrent != nTarget) 
 { 
  while (nMin < nMax) 
  { 
   nMid = (nMin + nMax) / 2; 
 
   nCurrent = WrapToolBar(pData, nCount, nMid); 
 
   if(nCurrent == nTarget) 
    nMax = nMid; 
   else 
   { 
    if(nMin == nMid) 
    { 
     WrapToolBar(pData, nCount, nMax); 
     break; 
    } 
 
    nMin = nMid; 
   } 
  } 
 } 
 
 CSize size = CalcSize(pData, nCount); 
 
 WrapToolBar(pData, nCount, size.cx); 
 
 return size; 
 } 

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

 
 
 CSize CVToolBarCtrl::SizeToolBarFromWidth(int nWidth) 
 { 
 int nCount; 
 
 TBBUTTON* pData = NULL; 
 
 CSize sizeResult(0, 0); 
 
 { 
  nCount = DefWindowProc(TB_BUTTONCOUNT, 0, 0); 
  
  if(nCount != 0) 
  { 
   pData = new TBBUTTON[nCount]; 
 
   for(int i = 0; i < nCount; i++) 
    VERIFY(DefWindowProc(TB_GETBUTTON, i, (LPARAM) &pData[i])); 
  } 
 } 
 
 if(pData != NULL) 
 { 
  sizeResult = SizeToolBar(pData, nCount, nWidth); 
  delete [] pData; 
 } 
 
 CRect rect; 
 
 rect.SetRectEmpty(); 
 
 CalcInsideRect(rect); 
 
 rect.NormalizeRect(); 
 
 sizeResult.cx += rect.Width(); 
 sizeResult.cy += rect.Height(); 
 
 return sizeResult; 
 } 

которая и будет перераспологать кнопки ToolBar - а и возвращать так необходимые нам размеры всего окна ToolBar.

  Здесь я также должен упомянуть о том, что при написании функции CVToolBarCtrl::CalcSize нам пришлось ввести в проект еще одну глобальную функцию (я писал о ней вначале). При вычислении размеров кнопок ToolBar - a может возникнуть необходимость узнать ширину дополнительной области около кнопки, создаваемой ToolBar - ом, когда кнопка имеет стиль TBSTYLE_DROPDOWN. В самой библиотеке такая функция есть, но она не экспортируется. Она реализована в файле BARTOOL.CPP и недоступна при использовании MFC в виде динамических библиотек.

Поэтому, как и ранее создадим подобную функцию в нашем проекте, воспользовавшись кодом MFC :

 
 
 static int _vDropDownWidth = -1; 
 
 int VAPI _VGetDropDownWidth() 
 { 
 // return cached version if already determined... 
 
 if (_vDropDownWidth != -1) 
  return _vDropDownWidth; 
 
 // otherwise calculate it... 
  
 HDC hDC = GetDC(NULL); 
  
 ASSERT(hDC != NULL); 
  
 HFONT hFont; 
  
 if((hFont = CreateFont(GetSystemMetrics(SM_CYMENUCHECK), 0, 0, 0, 
  FW_NORMAL, 0, 0, 0, SYMBOL_CHARSET, 0, 0, 0, 0, 
  _T("Marlett"))) != NULL) 
  hFont = (HFONT)SelectObject(hDC, hFont); 
  
 VERIFY(GetCharWidth(hDC, '6', '6', &_vDropDownWidth)); 
  
 if (hFont != NULL) 
 { 
  SelectObject(hDC, hFont); 
  DeleteObject(hFont); 
 } 
  
 ReleaseDC(NULL, hDC); 
  
 ASSERT(_vDropDownWidth != -1); 
  
 return _vDropDownWidth; 
 } 

  Это достаточно полезная функция - вы можете свободно использовать ее в своих проектах.


Далее - все просто : мы обрабатываем сообщения WM_NCCALCSIZE (для рассчета ширины неклиентской области в зависимости от значений переменных - ширины бордюра) и WM_NCPAINT для прорисовки неклиентской области.

  Обработчики этих сообщений приведены ниже :

 
 
 void CVToolBarCtrl::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS FAR* lpncsp)  
 { 
 // calculate border space (will add to top/bottom, subtract from right/bottom) 
 
 CRect rect; 
  
 rect.SetRectEmpty(); 
 
 CalcInsideRect(rect); 
 
 ASSERT(_VGetComCtlVersion() != -1); 
 ASSERT(_VGetComCtlVersion() >= VERSION_IE4 || rect.top >= 2); 
 
 // adjust non-client area for border space 
 
 lpncsp->rgrc[0].left += rect.left; 
 lpncsp->rgrc[0].top += rect.top; 
  
 // previous versions of COMCTL32.DLL had a built-in 2 pixel border 
 
 if (_VGetComCtlVersion() < VERSION_IE4) 
  lpncsp->rgrc[0].top -= 2; 
  
 lpncsp->rgrc[0].right += rect.right; 
 lpncsp->rgrc[0].bottom += rect.bottom; 
 } 
 
 void CVToolBarCtrl::OnNcPaint()  
 { 
 CWindowDC dc(this); 
 
 CRect rectClient; 
 GetClientRect(rectClient); 
  
 CRect rectWindow; 
 GetWindowRect(rectWindow); 
 
 ScreenToClient(rectWindow); 
  
 rectClient.OffsetRect(-rectWindow.left, -rectWindow.top); 
 rectWindow.OffsetRect(-rectWindow.left, -rectWindow.top); 
 
 dc.ExcludeClipRect(rectClient); 
 
 ::FillRect(dc.GetSafeHdc(), &rectWindow, ::GetSysColorBrush(COLOR_BTNFACE)); 
 
 dc.ExcludeClipRect(rectWindow); 
 dc.IntersectClipRect(rectClient); 
 
 SendMessage(WM_ERASEBKGND, (WPARAM) dc.m_hDC); 
 } 

  На этом закончим рассмотрение структуры класса CVToolBarCtrl и уделим еще немного времени для рассмотрения структуры класса окна-контейнера для объектов только что созданного нами класса.

Окно-контейнер панелей.

  Раскажу об этом вкратце.

  Понаследуем этот класс (CDrivesBar) от класса CControlBar.

Продекларируем в его описании объекты класса CVToolBarCtrl :

 
 
 m_wndToolBarLeft и 
 m_wndToolBarRight 

Создадим функцию CDrivesBar::Create, в которой зарегистрируем оконный класс с цветом фона трехмерных объектов. Проведем в этой функции инициализацию панелей :

 
 
 BOOL CDrivesBar::Create(CWnd *pParentWnd) 
 { 
 LPCTSTR lpszClass = AfxRegisterWndClass(0, 0, 
  (HBRUSH) ::GetSysColorBrush(COLOR_BTNFACE)); 
 
 m_dwstyle="CBRS_ALIGN_TOP" | CBRS_TOOLTIPS | CBRS_BORDER_3D | 
    CBRS_BORDER_TOP; 
 
 if(!CControlBar::Create(lpszClass, NULL, WS_CHILD | 
  WS_VISIBLE, rcNullRect, pParentWnd, 
  AFX_IDW_TOOLBAR + 256 - 3)) 
  return FALSE; 
 
 UpdateData(FALSE); 
 
 m_wndToolBarLeft.Create(0, WS_CHILD | WS_VISIBLE | 
    TBSTYLE_FLAT | TBSTYLE_LIST | 
    TBSTYLE_WRAPABLE | CCS_NOPARENTALIGN | 
    CCS_NORESIZE, rcNullRect, 
    this, IDC_DRIVES_TOOLBAR_LEFT); 
 
 m_wndToolBarRight.Create(0, WS_CHILD | WS_VISIBLE | 
    TBSTYLE_FLAT | TBSTYLE_LIST | 
    TBSTYLE_WRAPABLE | CCS_NOPARENTALIGN | 
    CCS_NORESIZE, rcNullRect, 
    this, IDC_DRIVES_TOOLBAR_RIGHT); 
 ... 

В этой же функции расположен код (не приводится) добавления на панели кнопок для тестирования работы.

Само переразмещение панелей происходит в обработчике сообщения WM_SIZE, а вычисление размеров и использование возможностей ранее написанного нами кода происходит в функции CDrivesBar::CalcDynamicLayout :

 
 
 CSize CDrivesBar::CalcDynamicLayout(int nLength, DWORD nMode) 
 { 
 if((nMode & (LM_HORZ | LM_HORZDOCK)) && (m_lpLayout != NULL)) 
 { 
  CRect rcInside; 
 
  rcInside.SetRectEmpty(); 
 
  CControlBar::CalcInsideRect(rcInside, TRUE); 
 
  if(m_lpLayoutBars != NULL) 
   delete [] m_lpLayoutBars; 
 
  m_lpLayoutBars = new CRect[2]; 
 
  m_lpLayoutBars[0].CopyRect(&m_lpLayout->rect); 
 
  nLength = m_lpLayoutBars[0].Width() - rcInside.left + rcInside.right; 
 
  int nLenLeft = int(double(nLength) / 2.0); 
 
  CSize size = m_wndToolBarLeft.SizeToolBarFromWidth(nLenLeft); 
 
  m_lpLayoutBars[0] = CRect(rcInside.TopLeft(), size); 
 
  m_lpLayoutBars[0].right = nLenLeft + rcInside.left; 
 
  m_lpLayoutBars[1].CopyRect(m_lpLayoutBars[0]); 
 
  m_lpLayoutBars[1].left = nLenLeft + rcInside.left; 
  m_lpLayoutBars[1].right = nLength + rcInside.left; 
 
  return CSize(32767, size.cy + rcInside.top - rcInside.bottom); 
 } 
 
 return CControlBar::CalcDynamicLayout(nLength, nMode); 
 } 

Здесь как раз и используется функция CVToolBarCtrl::SizeToolBarFromWidth, позволяющая правильно разместить все панели.

  Далее мы просто объявим и проинициализируем объект класса CDrivesBar в классе CMainFrame.

В результате всего вышеописнного получили панель следующего вида :


  Но... говорят - "лучше раз увидеть...". Одним словом - свежайшие исходники проекта находятся все там же.

  И еще одно - приведенные здесь методы небезупречны. Но описанные приемы дают направление для самостоятельных поисков и иногда ломают привычные стереотипы.

А то, что в итоге получилось вы можете посмотреть здесь :

http://SoftMaker.fatal.ru/projects/vcmd/shots.htm


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

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

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

Подписчикам

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

Вопросы

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

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

Ответы

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

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

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


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

В избранное