Студия web-дизайна Хостмэйк
Наши работыКонтактыО компанииОтзывыГлоссарийСтатьи

Unbound DBGrid на VisualC++

Статьи Программирование на С/С++

Или о том, как не мешает иногда поразмыслить
:) Часть 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();



}



 



 



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

:)



 



Так…
статейка большая, а написано по сути мало, грид будет просто заполняться
введёнными данными, осталось еще организовать удобный опрос значений строк,
заполнение массива данными (к примеру из базы данных:)),
опрос и изменение значений ячеек, выпадающие списки, картинки, раскраска… много
чего еще…


Автор: MIB

16.02.2006

Телефон

+7 8636 237-836

Поиск

VSESMI.ru — новости в СМИ.
Один из больших по объему информации проектов, работающих под управлением HostCMS.

Tur-Hotel.ru — отзывы об отелях
На сайте представлено описание отелей, рейтинг отелей с отзывами туристов.