Меню сайта
Главная
Общие сведения
Архитектура Windows
Программируем на vs.net
Студенческие заметки
Статьи
Контакты
Интернет магазин
Visual Studio 2008 Standard
Переход на Visual Basic .NET
Тестирование и отладка приложений в VB.NET
Поддержка и оптимизация на VB.NET
Создание наборов данных ADO.NET
Сопровождение Web-приложений в VB.NET
Введение в ADO.NET
Editor.NET
MemPort.NET
Управление автономными данными

Весь каталог

Новости
Макроподстановка ASSERT_VALID в отладочной (Debug) версии проекта проверяет на осмысленность полученный указатель и дает сообщение об ошибке, в случае когда указатель равен нулю или в действительности не является адресом объекта класса, производного от класса CObject. Если вы просмотрите иерархию классов MFC, то увидите, что CObject является отцом-прародителем всех классов, потомки которых использованы в нашем приложении. Подробнее "Классы приложения"
При создании нового проекта Studio.Net автоматически создает рабочее пространство и помещает в него этот проект. Вот перечень шагов для создания нового проекта и нового рабочего пространства (solution), его содержащего. Подробнее "Создание нового проекта"
Откройте файл ChildView.cpp, который содержит коды реализации методов класса CChildView. Его имя содержит ложный намек на происхождение от CView. На самом деле он происходит от класса CWnd и инкапсулирует функциональность окна, оккупирующего клиентскую область окна рамки, которое управляется классом CMainFrame. Простое окно, как вы помните, для перерисовки своего содержимого, вместо метода OnDraw использует метод OnPaint. Найдите этот метод в классе CChildView и убедитесь, что в нем контекст устройства создается, а не приходит в качестве параметра от каркаса приложения, как это было в приложениях, поддерживающих архитектуру документ — представление. Подробнее "Класс окна для отображения графика"
Установка параметрпв освещения осуществляется подобно тому, как это делалось в предыдущем уроке. Но здесь мы храним все параметры для тога, чтобы можно было управлять освещенностью изображения. Немного позже разработаем диалог, с помощью которого пользователь программы сможет изменять настройки освещения, а сейчас введите коды функции SetLight. Подробнее "Параметры освещения"
Картинная галерея

Общие сведения

Программируем на vs.net

Архитектура программного комплекса

Результаты взаимодействия Visual Studio

Реализация

Архитектура Windows

Солирование

Использование материалов

Причины возникновения

Методы

Партнеры проекта

Работа с контейнером

Для работы с файлом мы пользовались буфером переменных типа BYTE. Для работы с данными в памяти значительно более удобной структурой данных является динамический контейнер. Мы, как вы помните, выбрали для этой цели контейнер, скроенный по шаблону vector. При заказе на его изготовление указали тип данных для хранения в контейнере. Это объекты класса CPointSD (точки трехмерного пространства). Мы пошли по простому пути и храним в файле только один компонент Y из трех координат точек поверхности в 3D. Остальные две координаты (узлов сетки на плоскости X-Z) будем генерировать на регулярной основе. Такой подход оправдан тем, что изображение OpenGL все равно претерпевает нормирующие преобразования, перед тем как попасть на двухмерный экран. Создание контейнера точек производится в теле функции SetGraphPoints, к разработке которой сейчас и приступим.

 

На вход функции подается временный буфер (и его размер), в который попали данные из файла. В настоящий момент в буфере находятся данные тестовой поверхности, а потом, при вызове из функции ReadData, в него действительно попадут данные из файла. Выбор данных из буфера происходит аналогично их записи. Здесь мы пользуемся адресной арифметикой, определяемой типом указателя. Так, операция ++ в применении к указателю типа UINT сдвигает его в памяти на sizeof (UINT) байт. Смена типа указателя (на float*) происходит в тот момент, когда выбраны данные о размерах сетки узлов.

 

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

 

void COGView::SetGraphPoints(BYTE* buff, DWORD nSize)

{

//====== Готовимся к расшифровке данных буфера

//====== Указываем на него указателем целого типа

UINT *p = (UINT*)buff;

//=== Выбираем данные целого типа, сдвигая указатель

m_xSize = *р; m_zSize = *++p;

//====== Проверка на непротиворечивость

if (m_xSize<2 || m_zSize<2 ||

m_xSize*m_zSize*sizeof(float)

+ 2 * sizeof(UINT) != nSize)

{

MessageBox (_T ("Данные противоречивы") ) ;

return;

}

//====== Изменяем размер контейнера

//====== При этом его данные разрушаются

m_cPoints . resize (m_xSize*m_zSize) ;

if (m_cPoints .empty () )

{

MessageBox (_T ("He возможно разместить данные")

return;

}

//====== Подготовка к циклу пробега по буферу

//====== и процессу масштабирования

float x, z,

//====== Считываем первую ординату

*pf = (float*) ++р,

fMinY = *pf,

fMaxY = *pf,

right = (m_xSize-l) /2 . f ,

left = -right,

read = (m_zSize-l) /2 . f ,

front = -rear,

range = (right + rear) /2. f;

UINTi, j, n;

//====== Вычисление размаха изображаемого объекта

m_fRangeY = range;

m_fRangeX = float (m_xSize) ;

m_fRangeZ = float (m_zSize) ;

//====== Величина сдвига вдоль оси Z

m_zTrans = -1.5f * m_fRangeZ;

//====== Генерируем координаты сетки (X-Z)

//====== и совмещаем с ординатами Y из буфера

for (z=front, i=0, n=0; i<m_zSize; i++, z+=l.f)

{

for (x=left, j=0; j<m_xSize; j++, x+=l.f, n++)

{

MinMax (*pf, fMinY, fMaxY) ;

m_cPoints[n] = CPoint3D(x, z, *pf++) ;

}

}

//====== Масштабирование ординат

float zoom = fMaxY > fMinY ? range/ (fMaxY-fMinY)

: l.f;

for (n=0; n<m_xSize*m_zSize;n++)

{

m_cPoints [n] . у = zoom * (m_cPoints [n] . у - fMinY) - range/2. f;

}

}

 

При изменении размеров контейнера методом (resize) все его данные разрушаются. В двойном цикле пробега по узлам сетки мы восстанавливаем (генерируем заново) координаты X и Z всех вершин четырехугольников. В отдельном цикле пробега по всему контейнеру происходит масштабирование ординат (умножение на предварительно вычисленный коэффициент zoom). В используемом алгоритме необходимо искать экстремумы функции у = f (x, z). С этой целью удобно иметь глобальную функцию MinMax, которая корректирует значение минимума или максимума, если входной параметр превышает существующие на сей момент экстремумы. Введите тело этой функции в начало файла реализации оконного класса (ChildView.cpp):

 

inline void MinMax (float d, floats Min, float& Max)

{

//====== Корректируем переданные по ссылке параметры

if (d > Max)

Max = d; // Претендент на максимум

else if (d < Min)

Min = d; // Претендент на минимум

}

 

Диалог по управлению светом

В окне редактора диалогов (Resource View > Dialog > Контекстное меню > Insert Dialog) создайте окно диалога по управлению светом, которое должно иметь такой вид:

 

Обратите внимание на то, что справа от каждого движка расположен элемент типа static Text, в окне которого будет отражено текущее положение движка в числовой форме. Три регулятора (элемента типа Slider Control) в левом верхнем углу окна диалога предназначены для управления свойствами света. Группа регуляторов справа от них поможет пользователю изменить координаты источника света. Группа регуляторов, объединенная рамкой (типа Group Box) с заголовком Material, служит для изменения отражающих свойств материала. Кнопка с надписью Data File позволит пользователю открыть файловый диалог и выбрать файл с данными для нового изображения. Для диалогов, предназначенных для работы в немодальном режиме, необходимо установить стиль Visible. Сделайте это в окне Properties > Behavior. Идентификаторы элементов управления мы сведем в табл.

 

Таблица - Идентификаторы элементов управления

 

Элемент

Идентификатор

Диалог

IDD_PROP

Ползунок Ambient в группе Light

IDC_AMBIENT

Ползунок Diffuse в группе Light

IDC_DIFFUSE

Ползунок Specular в группе Light

IDC_SPECULAR

; Static Text справа от Ambient в группе Light

IDC_AMB_TEXT

, Static Text справа от Diffuse в группе Light

IDC_DIFFUSE_TEXT

Static Text справа от Specular в группе Light

IDC_SPECULAR_TEXT

Ползунок Ambient в группе Material

IDC_AMBMAT

Ползунок Diffuse в группе Material

IDC_DIFFMAT

' Ползунок Specular в группе Material

IDC_SPECMAT

f Static Text справа от Ambient в группе Material

 

IDC_AMBMAT_TEXT

 

:! Static Text справа от Diffuse. в группе Material

IDC_DIFFMATJFEXT

; Static Text справа от Specular в группе Material

IDC_SPECMAT_TEXT

Ползунок Shim'ness

IDC_SHINE

Ползунок Emission

IDC_EMISSION

« Static Text справа от Shininess

IDC_SHINE_TEXT

Static Text справа от Emission

IDC_EMISSION_TEXT

Ползунок X

IDC_XPOS

| Ползунок Y

IDC_YPOS

1 Ползунок Z

IDC_ZPOS

Static Text справа от X

IDC_XPOS_TEXT

Static Text справа от Y

IDC_YPOS_TEXT

Static Text справа от Z

IDC_ZPOS_TEXT

Кнопка Data File

IDC_FILENAME

 

Диалоговый класс

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

 

  • Выберите в контекстном меню команду Add Class.

  • В левом окне диалога Add Class раскройте дерево Visual C++, сделайте выбор MFC > MFC Class и нажмите кнопку Open.

  • В окне мастера MFC Class Wizard задайте имя класса CPropDlg, в качестве базового класса выберите CDialog. При этом станет доступным ноле Dialog ID.

  • В это поле введите или выберите из выпадающего списка идентификатор шаблона диалога IDD_PROP и нажмите кнопку Finish.

 

Просмотрите объявление класса CPropDlg, которое должно появиться в новом окне PropDlg.h. Как видите, мастер сделал заготовку функции DoDataExchange для обмена данными с элементами управления на форме диалога. Однако она нам не понадобится, так как обмен данными будет производиться в другом стиле, характерном для приложений не MFC-происхождения. Такое решение выбрано в связи с тем, что мы собираемся перенести рассматриваемый код в приложение, созданное на основе библиотеки шаблонов ATL. Это будет сделано в уроке 9 при разработке элемента ActiveX, а сейчас введите в диалоговый класс новые данные. Они необходимы для эффективной работы с диалогом в немодальном режиме. Важным моментом в таких случаях является использование указателя на оконный класс. С его помощью легко управлять окном прямо из диалога. Мы слегка изменили конструктор и ввели вспомогательный метод GetsiiderNum. Изменения косметического характера вы обнаружите сами:

 

#pragma once

class COGView; // Упреждающее объявление

class CPropDlg : public CDialog

{

DECLARE_DYNAMIC(CPropDlg)

public:

COGView *m_pView; // Адрес представления

int m_Pos[ll]; // Массив позиций ползунков

CPropDlg(COGView* p) ;

virtual ~CPropDlg();

// Метод для выяснения ID активного ползунка int GetsiiderNum(HWND hwnd, UINT& nID) ;

enum { IDD = IDD_PROP };

protected: virtual void DoDataExchange(CDataExchange* pDX);

DECLARE_MESSAGE_MAP()

};

 

Откройте файл реализации диалогового класса и с учетом сказанного про адрес окна введите изменение в тело конструктора, который должен приобрести такой вид:

 

CPropDlg::CPropDlg(COGView* p)

: CDialog(CPropDlg::IDD, p)

{

//====== Запоминаем адрес объекта

m_pView = p;

}

 

Инициализация диалога

При каждом открытии диалога все его элементы управления должны отражать текущие состояния регулировок (положения движков), которые хранятся в классе представления. Обычно эти установки производят в коде функции OninitDialog. Введите в класс CPropDlg стартовую заготовку этой функции (CPropDlg > Properties > Overrides > OninitDialog > Add) и наполните ее кодами, как показано ниже:

 

BOOL CPropDlg: rOnlnitDialog (void)

{ CDialog: :OnInitDialog () ;

//====== Заполняем массив текущих параметров света

m_pView->GetLightParams (m _Pos) ;

//====== Массив идентификаторов ползунков

UINT IDs[] =

{

IDC_XPOS, IDC_YPOS, IDC_ZPOS,

IDC_AMBIENT,

IDC_DIFFUSE,

IDC_SPECULAR,

IDC_AMBMAT,

IDC_DIFFMAT,

IDC_SPECMAT,

IDC_SHINE,

IDCEMISSION

//====== Цикл прохода по всем регуляторам

for (int i=0; Ksizeof (IDs) /sizeof (IDs [ 0] ) ; i++)

{

//=== Добываем Windows-описатель окна ползунка H

WND hwnd = GetDlgItem(IDs[i] } ->GetSafeHwnd () ;

UINT nID;

//====== Определяем его идентификатор

int num = GetSliderNum(hwnd, nID) ;

// Требуем установить ползунок в положение m_Pos[i]

: :SendMessage(hwnd, TBM_SETPOS, TRUE, (LPARAM) m_Pos [i] )

char s [ 8 ] ;

//====== Готовим текстовый аналог текущей позиции

sprintf (s, "%d" ,m_Pos [ i] ) ;

//====== Помещаем текст в окно справа от ползунка

SetDlgltemText (nID, (LPCTSTR) s) ;

}

return TRUE;

}

 

Вспомогательная функция GetsliderNum по переданному ей описателю окна (hwnd ползунка) определяет идентификатор связанного с ним информационного окна (типа Static text) и возвращает индекс соответствующей ползунку пози ции в массиве регуляторов:

 

int CPropDlg: :GetSliderNum (HWND hwnd, UINT& nID)

{

//==== GetDlgCtrllD по известному hwnd определяет

//==== и возвращает идентификатор элемента управления

switch ( : : GetDlgCtrllD (hwnd) )

{

// ====== Выясняем идентификатор окна справа

case IDC_XPOS:

nID = IDC_XPOS_TEXT;

return 0;

case IDC_YPOS:

nID = IDC_YPOS_TEXT;

return 1;

case IDC_ZPOS:

nID = IDC_ZPOS_TEXT;

return 2;

case IDC_AMBIENT:

nID = IDC_AMB_TEXT;

return 3;

case IDC_DIFFUSE:

nID = IDC_DIFFUSE_TEXT;

return 4 ;

case IDC_SPECULAR:

nID = IDC_SPECULAR_TEXT;

return 5; case IDC_AMBMAT:

nID = IDC_AMBMAT_TEXT;

return 6 ;

case IDC_DIFFMAT:

nID = IDC_DIFFMAT_TEXT;

return 7 ;

case IDC_SPECMAT:

nID = IDC_SPECMAT_TEXT;

return 8 ; case IDC_SHINE:

nID = IDC_SHINE_TEXT;

return 9;

case IDC_EMISSION:

nID = IDC_EMISSION_TEXT;

return 10;

}

return 0;

}

 

Работа с группой регуляторов

В диалоговый класс введите обработчики сообщений WM_HSCROLL и WM_CLOSE, a также реакцию на нажатие кнопки IDC_FILENAME. Воспользуйтесь для этого окном Properties и его кнопками Messages и Events. В обработчик OnHScroll введите логику определения ползунка и управления им с помощью мыши и клавиш. Подобный код мы подробно рассматривали в уроке 4. Прочтите объяснения вновь, если это необходимо, Вместе с сообщением WM_HSCROLL система прислала нам адрес объекта класса GScrollBar, связанного с активным ползунком. Мы добываем Windows-описатель его окна (hwnd) и передаем его в функцию GetsliderNum, которая возвращает целочисленный индекс. Последний используется для доступа к массиву позиций ползунков. Кроме этого, система передает nSBCode, который соответствует сообщению об одном из множества событий, которые могут произойти с ползунком (например, управление клавишей левой стрелки — SB_LINELEFT). В зависимости от события мы выбираем для ползунка новую позицию:

 

void CPropDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)

{

//====== Windows-описатель окна активного ползунка

HWND hwnd = pScrollBar->GetSafeHwnd();

UINT nID;

//=== Определяем индекс в массиве позиций ползунков

int num = GetSliderNum(hwnd, nID) ;

int delta, newPos;

//====== Анализируем код события

switch (nSBCode)

{

case SBJTHUMBTRACK:

case SB_THUMBPOSITION: // Управление мышью

m_Pos[num] = nPos;

break; case SB_LEFT: // Клавиша Home

delta = -100;

goto New_Pos; case SB_RIGHT: // Клавиша End

delta = + 100;

goto New__Pos; case SB_LINELEFT: // Клавиша <-

delta = -1;

goto New_Pos; case SB_LINERIGHT: // Клавиша ->

delta = +1;

goto New_Pos; case SB_PAGELEFT: // Клавиша PgUp

delta = -20;

goto New_Pos; case SB_PAGERIGHT: // Клавиша PgDn

delta = +20-;

goto New_Pos;

New_Pos: // Общая ветвь

//====== Устанавливаем новое значение регулятора

newPos = m_Pos[num] + delta;

//====== Ограничения

m_Pos[num] = newPos<0 ? 0 :

newPos>100 ? 100 : newPos;

break; case SB ENDSCROLL:

default:

return;

}

//====== Синхронизируем текстовый аналог позиции

char s [ 8 ] ;

sprintf (s, "%d",m__Pos [num] ) ;

SetDlgltemText (nID, (LPCTSTR)s);

//---- Передаем изменение в класс COGView

m_pView->SetLightParam (num, m_Pos [num] ) ;

}

 

Особенности немодального режима

Рассматриваемый диалог используется в качестве панели управления освещением сцены, поэтому он должен работать в немодальном режиме. Особенностью такого режима, как вы знаете, является то, что при закрытии диалога он сам должен позаботиться об освобождении памяти, выделенной под объект собственного класса. Эту задачу можно решить разными способами. Здесь мы покажем, как это делается в функции обработки сообщения WM_CLOSE. До того как уничтожено Windows-окно диалога, мы обнуляем указатель m_pDlg, который должен храниться в классе COGView и содержать адрес объекта диалогового класса. Затем вызываем родительскую версию функции OnClose, которая уничтожает Windows-окно. Только после этого мы можем освободить память, занимаемую объектом своего класса:

 

void CPropDlg: :OnClose (void)

{

//=== Обнуляем указатель на объект своего класса

m_pView->m_pDlg = 0;

//====== Уничтожаем окно

CDialog: :OnClose () ;

//====== Освобождаем память

delete this;

}

 

Реакция на нажатие кнопки IDC_FILENAME совсем проста, так как основную работу выполняет класс COGView. Мы лишь вызываем функцию, которая реализована в этом классе:

 

void CPropDlg:: OnClickedFilename (void)

{

//=== Открываем файловый диалог и читаем данные

m_pView->ReadData ( ) ;

}

 

Создание немодального диалога должно происходить в ответ на выбор команды меню Edit > Properties. Обычно объект диалогового класса, используемого в немодальном режиме, создается динамически. При этом предполагается, что класс родительского окна хранит указатель m_pDlg на объект класса диалога. Значение указателя обычно используется не только для управления им, но и как признак его наличия в данный момент. Это позволяет правильно обработать ситуацию, когда диалог уже существует и вновь приходит команда о его открытии. Введите в класс COGView новую public-переменную:

 

CPropDlg *m_pDlg; // Указатель на объект диалога

 

В начало файла заголовков OGView.h вставьте упреждающее объявление класса

 

CPropDlg:

class CPropDlg; // Упреждающее объявление

В конструктор COGView вставьте обнуление указателя:

m_pDlg =0; // Диалог отсутствует

 

Для обеспечения видимости класса CPropDlg дополните список директив препроцессора файла OGView.cpp директивой:

 

linclude "PropDlg.h"

 

Теперь можно ввести коды функции, которая создает диалог и запускает его вызовом функции Create (в отличие от DoModal для модального режима). Если происходит попытка повторного открытия диалога, то возможны два варианта развития событий:

 

  • новый диалог не создается, но окно существующего диалога делается активным;

  • команда открытия диалога недоступна, так как ее состояние зависит от значения указателя m_pDlg.

 

Реализуем первый вариант:

 

void COGView::OnEditProperties (void)

{

//====== Если диалог еще не открыт

if (!m_pDlg)

{

//=== Создаем его и запускаем в немодальном режиме

m_pDlg = new CPropDlg(this);

m_pDlg->Create(IDD_PROP);

}

else

// Иначе, переводим фокус в окно диалога

m_pDlg->SetActiveWindow();

}

 

Реакция на команду обновления пользовательского интерфейса при этом может быть такой:

 

void COGView::OnUpdateEditProperties(CCmdUI *pCmdUI)

{

pCmdUI->SetCheck (m_pDlg != 0);

}

 

Второй вариант потребует меньше усилий:

 

void COGView::OnEditProperties (void)

{

m_pDlg = new CPropDlg(this);

m_pDlg->Create(IDD_PROP); }

 

Но при этом необходима другая реакция на команду обновления интерфейса:

 

void COGView::OnUpdateEditProperties(CCmdUI *pCmdUI)

{

pCmdUI->Enable(m_pDlg == 0);

}

 

Выберите и реализуйте один из вариантов.

 

Панель управления

Завершая разработку приложения, вставьте в панель управления четыре кнопки

 

Для команд ID_EDIT_BACKGROUND, ID_EDIT_PROPERTIES, ID_VIEW_FILL И ID_VIEW_

 

QUAD. Заодно уберите из нее неиспользуемые нами кнопки с идентификаторами

 

ID_FILE_NEW, ID_FILE_OPEN, ID_FILE_SAVE, ID_FILE_PRINT, ID__EDIT_CUT,

 

ID_EDIT_COPY, ID_EDIT_PASTE. Запустите приложение, включите диалог Edit > Properties и попробуйте управлять регуляторами параметров света. Отметьте, что далеко не все из них отчетливым образом изменяют облик поверхности. Нажмите кнопку Data File, при этом должен открыться файловый диалог, но мы не сможем открыть никакого другого файла, кроме того, что был создан по умолчанию. Он имеет имя «Sin.dat» и должен находиться (и быть виден) в папке проекта. В качестве упражнения создайте какой-либо другой файл с данными, отражающими какую-либо поверхность в трехмерном пространстве. Вы можете воспользоваться для этой цели функцией DefaultGraphic, немного модифицировав ее код. На рис. 49 и 50 приведены поверхности, полученные таким способом. Вы можете видеть эффект, вносимый различными настройками параметров освещения.

 

Если вы тщательно протестируете поведение приложения, то обнаружите недостатки. Отметим один из них. Закрытые части изображения при некотором ракурсе просвечивают сквозь те части поверхности, которые находятся ближе к наблюдателю. Причину этого дефекта было достаточно трудно выявить. И здесь опять пришли на помощь молодые, талантливые слушатели Microsoft Authorized Educational Center (www.Avalon.ru) Кондрашов С. С. (scondor@rambler.ru) и Фролов Д. С. (dmfrolov@rambler.ru). Оказалось, что при задании типа проекции с помощью команды gluPerspective значения ближней границы фрустума не должны быть слишком маленькими:

 

gluPerspective (45., dAspect, 0.01, 10000.);

 

В нашем случае этот параметр равен 0.01. Замените его на 10. и сравните качество генерируемой поверхности.

 

Подведем итог. В этой главе мы:

 

  • научились превращать окно, поддерживаемое классом cview, в окно OpenGL;

  • вновь использовали стандартный контейнер объектов класса GPoint3D, который удобен для хранения вершин изображаемой поверхности;

  • убедились, что использование списка команд OpenGL повышает эффективность передачи сложного изображения;

  • применили формулу вычисления нормали к поверхности и убедились в необходимости уделять этой проблеме достаточное внимание;

  • научились управлять освещенностью сцены OpenGL с помощью группы регуляторов;

  • оценили удобство управления группой регуляторов типа slider Control в функции обработки сообщения о прокрутке WM_HSCROLL.

Интернет магазин

 

1510 руб.

Переход на Visual Basic .NET

Слушатели курса познакомятся с различиями между Visual Basic 6.0 и Visual Basic .NET, а также с новыми возможностями Visual Studio .NET. Целевая аудитория: разработчики, имеющие опыт работы с предыдущими версиями Microsoft Visual Basic и желающие перейти на Microsoft Visual Basic .NET. 

Список версий:

Переход на Visual Basic .NET


1510 руб.

Тестирование и отладка приложений в VB.NET

На курсе обсуждаются методы тестирования и отладки программ, использование классов Debug и Trace из библиотеки классов .NET Framework, и работа с отладчиками из Visual Studio .NET и .NET Framework SDK. Целевая аудитория: Web-разработчики, желающие обновить свои знания и навыки в области тестирования и отладки приложений в среде Visual Studio .NET или подготовиться к сертификации MCAD или MCSD .NET. 

Список версий:

Тестирование и отладка приложений в VB.NET


 

  Все права защищены.
  Копирование запрещено.

Rambler's Top100
  Студия профессионального дизайна
  Дизайн: Студия Onta.ru