Или о том, как не мешает иногда поразмыслить
:)Часть 1.
Автор MIB
О, сколько нам открытий чудных,
не очень чудных и не нам…
(автор не известен)
Предупреждение: данная статья предназначена для прочтения лицам, достигшим
совершеннолетия, без психических отклонений. Вы читаете эту статью на свой страх
и риск. За возможные отрицательные последствия автор ответственности не несёт.
:)
И так. Думаю, многие из вас сталкивались с этим элементом управления и ломали
себе голову, пытаясь заставить его работать. Я в своё время обыскал весь
интернет на эту тему, но безрезультатно, везде упоминается только делфи, вижуал
бейсик и тому подобное. Я против этих языков ничего не имею, но решил всё-таки
как-то перенести этот элемент в VisualC++ и кое-чего достиг.
Разговор пойдёт пока только о
режиме
Unbound.
Создаём
пустой диалог, привязанный к нему класс.
В
редакторе ресурсов вставляем в диалог элемент управления
ActiveX
DBGrid,
нажимаем правой кнопкой, выбираем
“DBGrid
Control Object -> Edit”,
добавляем нужное количество колонок (в принципе, эти и другие настройки можно
сделать и программно, но пока воспользуемся редактором ресурсов, так меньше
мороки).
:)
Теперь, в “обычных” свойствах DBGrid,
переименовываем колонки, где необходимо – добавляем режим
“button” (в
этих колонках будет появляться выпадающий список). Обязательно ставим галочки в
строках
“Allow add new”, “Allow delete”,
выбираем
режим «Unbound». Всё, первичные настройки закончены.
Еще раз
нажимаем правой кнопкой на
DBGrid,
выбираем
”Class Wizard”,
переходим в закладку «Member
Variables»,
нажимаем двойным щелчком на строке, содержащей
ID
этого
элемента (я его назвал к примеру
IDD_Grid1).
Появляется сообщение, что для этого элемента
Wizard
должен сгенерировать соответствующий класс, нажимаем «да». Создаётся класс
CmsDgridCtrl. Теперь, необходимо дать название переменной, которая будет
привязана к «DBGrid
Control»
и обмениваться с ним информацией.
Возможно,
вы уже доходили до этого момента, но оказывалось, что этот класс не слишком
приспособлен для работы с гридом, тем более в режиме «Unbound»
:(
Я решил
ненмого отвлечься от VisualC
и обратиться за помощью к самому гриду. Вместе с этим компонентом прилагается
справочный файл:
C:WindowsHelpDbgrid96.hlp.
Открываю,
веду поиск на тему
“DBGrid unbound”.
Нахожу,
помимо прочего, примеры обработки четырех сообщений от самого грида:
UnboundAddData – добавление записи
UnboundDeleteRow – удаление записи
UnboundWriteData – обновление отредактированной записи
UnboundReadData – чтение данных и отображение при прокрутке или открытии грида.
Примеры,
естественно, для
VBA.
После
небольших косметических изменений я получил скрипт
VBS
и сделал
простенькую вэб-страничку:
ВНИМАНИЕ!
Если не появится грид – возможно, он прописан у вас в реестре под другим
Clsid`ом,
нужно проверить.
<HTML>
<BODY>
<BR>
<OBJECT
CLASSID="Clsid:00028C00-0000-0000-0000-000000000046" ID=DBGrid1 height=200
width=700 >
</OBJECT>
<SCRIPT
LANGUAGE=VBSCRIPT>
Dim
UserData()
‘гибкий
массив для хранения данных
Dim
mTotalRows
‘счетчик
строк
Dim
MAXCOLS
‘количество
столбцов
Sub
window_OnLoad()
DBGrid1.AllowAddNew=True
DBGrid1.AllowDelete=True
DBGrid1.AllowUpdate=True
DBGrid1.DataMode=1
‘bound=0, unbound=1
mTotalRows=0
MAXCOLS=2
End Sub
Sub
DBGrid1_UnboundAddData(ByVal RowBuf, NewRowBookmark)
Dim Col
mTotalRows = mTotalRows + 1
ReDim Preserve UserData(MAXCOLS - 1, mTotalRows - 1)
‘
изменяем размеры массива
‘
с сохранением данных (счет от нуля)
NewRowBookmark = mTotalRows - 1
For Col = 0 To UBound(UserData, 1)
If Not IsNull(RowBuf.Value(0, Col)) Then
UserData(Col, mTotalRows - 1) =
RowBuf.Value(0, Col)
Else
UserData(Col, mTotalRows - 1) =
DBGrid1.Columns(Col).DefaultValue
End If
Next
End Sub
Sub
DBGrid1_UnboundReadData(ByVal RowBuf, StartLocation, ByVal ReadPriorRows)
Dim CurRow, Row, Col, RowsFetched, Incr
If ReadPriorRows Then
Incr = -1
Else
Incr = 1
End If
If IsNull(StartLocation) Then
If ReadPriorRows Then
CurRow = RowBuf.RowCount - 1
Else
CurRow = 0
End If
Else
CurRow = CLng(StartLocation) + Incr
End If
For Row = 0 To RowBuf.RowCount - 1
If CurRow < 0 Or CurRow >= mTotalRows Then Exit
For
For Col = 0 To UBound(UserData, 1)
RowBuf.Value(Row, Col) =
UserData(Col, CurRow)
Next
RowBuf.Bookmark(Row) = CStr(CurRow)
CurRow = CurRow + Incr
RowsFetched = RowsFetched + 1
Next
RowBuf.RowCount = RowsFetched
End Sub
Sub
DBGrid1_UnboundWriteData(ByVal RowBuf, WriteLocation)
Dim Col
For Col = 0 To MAXCOLS - 1
If Not IsNull(RowBuf.Value(0, Col)) Then
UserData(Col, WriteLocation) =
RowBuf.Value(0, Col)
End If
Next
End Sub
Sub
DBGrid1_UnboundDeleteRow(Bookmark)
Dim Col, Row
For Row = Bookmark + 1 To mTotalRows - 1
For Col = 0 To MAXCOLS - 1
UserData(Col, Row - 1) =
UserData(Col, Row)
Next
Next
mTotalRows = mTotalRows - 1
End Sub
</SCRIPT>
</BODY>
</HTML>
Комментарии к коду почти не пишу - и так более-менее понятно, что к чему
:)
Получилась вполне работоспособная страничка, правда, без выпадающих списков, но
об этом – позже. После пятиминутного созерцания кода меня озарило: «В
Unbound
режиме
грид генерирует
Unbound-сообщения,
и если их правильно обработать, сделать двумерный массив для хранения данных –
всё заработает!»
С этой
гениальной мыслью я вернулся к
VisualC.
В
класс-визарде я нашел такие же сообщения для элемента
IDD_Grid1,
заготовил функции-обработчики для всех четырёх событий. Вот они:
void
CAboutDlg::OnUnboundReadDataDbgrid1(LPDISPATCH RowBuf, VARIANT FAR*
StartLocation, BOOL ReadPriorRows)
{
}
void
CAboutDlg::OnUnboundWriteDataDbgrid1(LPDISPATCH RowBuf, VARIANT FAR*
WriteLocation)
{
}
void
CAboutDlg::OnUnboundAddDataDbgrid1(LPDISPATCH RowBuf, VARIANT FAR*
NewRowBookmark)
{
}
void
CAboutDlg::OnUnboundDeleteRowDbgrid1(VARIANT FAR* Bookmark)
{
}
…Так-тааак
:(
Что это
за LPDISPATCH такой? В справочнике
MSDN
читаю: при помощи этой гадости можно управлять
ActiveX
элементами. Но как? Искал-искал, много чего нашел, но ничего не понял. Бред
:(
И еще.
RowBuf- это явно указатель на RowBuffer
(это,
как я понял, суб-объект грида, хранящий данные о текущей строке) В
VBS
всё
просто: Если объект составной – пишем что-то типа
Object.SubObject.Value=5.
Но в классе, сгенерированном для грида - ни одного упоминания об RowBuffer,
Columns, Column
и т д, а
ведь эти объекты входят в грид…
:(
И как выдернуть данные из этого ровбуффера?.. Перепробовал я много вариантов –
без толку.
В общем я забросил эту идею на некоторое время, но не забыл о ней.
И вот однажды, роясь в MSDN
по каким-то своим делам, я краем глаза заметил что-то об импортировании классов
из исполняемых файлов. Оказывается, в класс-визарде есть такая штука: Нажимаем «Add
Class»
- появляется выпадающий список:
“New”
и
“From a type library”,
выбираем второй. Предлагается выбрать файл
*.tlb, *.olb, *.dll …
Мдя…:(
Но вдруг,
почувствовав очередное озарение, выбираю опцию «*.*»
и ищу этот грид (C:Windowssystem
Dbgrid32.ocx)
Йесс!!! Я
- лучший!!! Класс-визард, как ни в чем не бывало, проглотил этот файл и выдал
список классов, которые я могу спокойно подключить к своей суппер-программе.
Внушительный, кстати списочек-с
:) И ровбуффер присутствует. Естественно, выделяю все классы (про запас типа),
жму ОК.
И так, что мы имеем: автоматичесски сформированный родной класс грида в файлах
msdgridctrl.h & msdgridctrl.cpp и кучу
классов,
умеющих работать с гридом, но ни чем к нему не подключенных (файлы dbgrid32.h
& dbgrid32.cpp)
Не
мудрствуя лукаво, пишу в файле
msdgridctrl.h #include “dbgrid32.h”,
а в разделе
public:
добавляю:
RowBuffer rb;
Всё
спокойно компилируется - очень хорошо.
:)
Дальше – проще. Мне в голову пришла еще одна идея: В программе может быть
несколько диалогов с гридами, и делать обработку каждого грида в классе
“родного” диалога – не красиво, лучше все обработки событий полностью написать в
классе CmsDgridCtrl, и вызывать их из любого диалога. Иными словами:
………………..
………………..
void MyDialog::OnUnboundWriteDataDbgrid1(LPDISPATCH RowBuf, VARIANT FAR*
WriteLocation) //Это
перехват сообщения грида именно в этом диалоге
{
c_Grid.UnboundWrite(RowBuf,WriteLocation);
//
А это – вызов созданной вручную функции, которая находится в классе
CmsDgridCtrl
// (объект
c_Grid
объявлен
в классе
MyDialog “CmsDgridCtrl c_Grid”)
}
……………
……………
Так.
Теперь приступим к массиву. Я решил использовать ColeSafeArray. Он достаточно
удобный, многомерный и позволяет работать с данными
VARIANT.
В
msdgridctrl.h
пишем:
public:
COleSafeArray UserData;
Теперь, в файле msdgridctrl.cpp,
сразу после #includ’ов
DWORD
numElements[] = {1, 65535}; //20 столбцов, много-много строк,
//
в общем с запасом подготавливаем параметры для
UserData
Теперь,
нужно вручную объявить и добавить функцию инициализации массива, да и всего
грида.
void
CMsDgridCtrl::init(int NumColumns)
{
numElements[0]=NumColumns;
UserData.Create(VT_VARIANT, 2, numElements);
mTotalRows=0;
}
Правильно. Эту функцию мы и будем вызывать из диалогов, перехватывая, например,
WM_INITDIALOG
А теперь
– сам код:
void
CMsDgridCtrl::UnboundAdd(LPDISPATCH RowBuf, VARIANT *NewRowBookmark)
{
int a,Col;
long index[2];
VARIANT value;
rb.AttachDispatch(RowBuf,FALSE);
for(Col=0;Col<numElements[0];Col++)
{
index[0]=Col;
index[1]=mTotalRows;
value=rb.GetValue(0,Col);
UserData.PutElement(index, &value);
}
mTotalRows++;//***
rb.DetachDispatch();
NewRowBookmark->vt=VT_I2;
NewRowBookmark->iVal=mTotalRows-1;
}
void
CMsDgridCtrl::UnboundRead(LPDISPATCH RowBuf, VARIANT *StartLocation, BOOL
ReadPriorRows)
{
int a,Col,RowsFetched;
short Incr;
long index[2],Row;//,ubound;
VARIANT value;
VARIANT CurRow;
CurRow.vt=VT_I2;
rb.AttachDispatch(RowBuf,TRUE);
RowsFetched=0;
if(ReadPriorRows)
{
Incr=-1;
}else
{
Incr=1;
}
a=0;
if ((StartLocation->vt)==1)
{
if(ReadPriorRows)
{
CurRow.iVal=(short)rb.GetRowCount()-1;
a=0;
}else
{
CurRow.iVal=0;
a=0;
}
}
else
{
CurRow.iVal=(StartLocation->iVal)+Incr;
a=0;
}
a=(rb.GetRowCount())-1;
a=a;
for( Row = 0;(Row<=a);Row++)
{
if( (CurRow.iVal<0)||(CurRow.iVal>=mTotalRows) )
{
//break;
goto err;
}
for (Col=0;Col<numElements[0];Col++)
{
index[0]=Col;
index[1]=CurRow.iVal;
UserData.GetElement(index,&value);
rb.SetValue(Row,Col,value);
}
rb.SetBookmark(Row,CurRow);
CurRow.iVal+=Incr;
RowsFetched++;
}
err:
rb.SetRowCount(RowsFetched);
rb.DetachDispatch();
}
void
CMsDgridCtrl::UnboundWrite(LPDISPATCH RowBuf, VARIANT *WriteLocation)
{
int Col;
long index[2];
VARIANT value;
// AfxMessageBox("UnboundWrite",MB_OK);
WriteLocation->vt=VT_I2;
rb.AttachDispatch(RowBuf,TRUE);
for(Col=0;Col<numElements[0];Col++)
{
index[0]=Col;
index[1]=WriteLocation->iVal;
value=rb.GetValue(0,Col);
if(value.vt!=VT_NULL)
{
UserData.PutElement(index,
&value);
}
}
rb.DetachDispatch();
}
void
CMsDgridCtrl::UnboundDeleteRow(VARIANT *Bookmark)
{
int Col, Row;
long index[2];
VARIANT value;
for(Row=(Bookmark->iVal)+1;Row<=mTotalRows-1;Row++)
{
for(Col=0;Col<numElements[0];Col++)
{
index[0]=Col;
index[1]=Row;
UserData.GetElement(index, &value);
index[0]=Col;
index[1]=Row-1;
UserData.PutElement(index, &value);
}
}
mTotalRows = mTotalRows - 1;
}
В общем
один-в-один, как наVBS
:)
Чуть не
забыл: каждому
init’у
по личному
destroy() :)
void
CMsDgridCtrl::destroy()
{
mTotalRows=0;
UserData.Detach();
}
при
компилировании будут предупреждения о преобразовании типов переменных, да и сам
код не ахти, я не волшебник, я учусь
:)
Так…
статейка большая, а написано по сути мало, грид будет просто заполняться
введёнными данными, осталось еще организовать удобный опрос значений строк,
заполнение массива данными (к примеру из базы данных:)),
опрос и изменение значений ячеек, выпадающие списки, картинки, раскраска… много
чего еще…
(подумал,
и добавил в самом начале «Часть 1»
:))
Продолжение следует.
HTML 5 — это грядущее обновление гипертекстового языка разметки, основного
способа создания контента для размещения его во всемирной паутине. Разработка
HTML остановилась в 1999 году, на версии HTML 4.01 и с тех пор web-содержимое
изменилось так, что текущие спецификации HTML перестали соответствовать
сегодняшним требованиям.
HTML 5 нацелен на то, чтобы увеличить функциональную совместимость HTML и
соответствовать растущим требованиям разнообразного и смешанного web-контента.
HTML 5 так же нацелен на устранение недостатков четвертой версии. В этой статье
мы взглянем на 5 новых интересных вещей в HTML 5.
Элемент управления ListView был представлен в .Net Framework 3.5 как замена
устаревшему GridView. Новый элемент имеет более расширенный функционал, чем его
предшественник, но в тоже время лишен некоторых внутренних механизмов, что
впрочем целиком следствие из расширенной универсальности ListView. Среди отличий
ListView и GridView можно назвать и гибкую настройку разметки, что позволяет
выводить данные не только в табличном виде, но и вообще в любом каком пожелает
программист. Благодаря шаблонам ItemTemplate, EditItemTemplate,
InsertItemTeplate можно настроить внешний вид при любом из состояний ListView:
редактировании или выборе элемента.
Компания Стимулсофт предоставляет для разработчиков мощный набор инструментов
для создания отчетов для Microsoft Visual Studio .Net 2005 и 2008; эти инструменты доступны
как для Windows Forms, так и для Web Forms. Это генератор отчетов Stimulsoft Reports.Net.
Генератор отчетов Stimulsoft Reports.Net имеет ряд особенностей: простая работа с дизайнером
отчетов, полная поддержка экспорта в PDF, Word, Excel и многие другие форматы. Crystal Report
и Microsoft Reporting Service – очень хорошие программные продукты для повседневной работы,
но, если Вам необходимо создать отчеты с поддержкой кросс-табов, drill down, Ajax, штрих-кодов
и возможностью подключения одновременно более одного источника данных, то Stimulsoft Reports.Net поможет Вам
сэкономить массу времени. Также, данный генератор отчетов позволяет пользователям создавать свои собственные
отчеты любой сложности. И все эти особенности делают Stimulsoft Reports.Net хорошим выбором в
сфере программных продуктов для Business Intelligence.