by jingzhongrong 在默认状态下,ListView控件的编辑功能是通过提供一个类似Edit控件的栏来实现的。但是如果你想让界面更加友好或者希望使用其他类型的控件来编辑数据,以提高操作的便利性。如下图所示,我们利用一个ComboBox控件来让用户以选择的方式来编辑数据。
在上述图片中,使用ComboBox控件进行选择,选中的项目可以直接对ListView中的“插件状态”栏进行修改。其中还涉及对ComboBox控件和ListView控件的自绘,不属于此文所要描述的范围,略过这部分的代码以及实现。如果需要了解有关控件自绘的信息,可以自行到网上搜索。此文的编译环境是C++Builder 2007。 下面具体说明实现的方法。首先添加ListView控件以及ComboBox控件到程序中,并设置ComboBox控件的Visible属性默认为false。 1、每当用户单击ListView控件时,我们应该获取鼠标此时单击的位置,并根据位置计算出所在行以及所在的列(如上图,ComboBox出现在第二列中,只有当鼠标单击的位置在ListView第二列中才会出现ComboBox让用户进行选择)。 判断用户单击的行及列使用如下代码:
// 在ListView 控件的OnMouseUp 事件中添加如下代码 TListView *listview = (TListView*)Sender; TListItem *lv = listview->GetItemAt(X,Y); if(lv) // 如果选中一项 { TPoint p(X,Y); TRect ClickedItem = lv->DisplayRect(drBounds); // 获取选择的项目的边框 TRect rct(ClickedItem); int CellLeft = rct.Left, CellIndex = 0; // 下面计算鼠标单击的位置处在哪一列中 for(int x = 0; x < listview->Columns->Count; x++) { rct.Left = CellLeft; rct.Right = CellLeft + listview->Column[x]->Width; if(PtInRect(rct,p)) // 鼠标单击位置在x+1 列中 { CellIndex = x + 1; break; } CellLeft += listview->Column[x]->Width; } // 检查鼠标点击位置是否位于" 插件状态" 一列中(对应上图实现) if(CellIndex != 2) return; else // 处于“插件状态”列中 { … // 暂时省略此处代码 } } |
2、此时我们已经判断得出鼠标单击的位置是否应该显示出ComboBox控件。下面我们继续进行判断,此时我们应该考虑到这个列可能处于完全不可见的状态或者部分可见的状态,我们应该调整ComboBox的位置以及大小之后才能显示出来。 上述步骤中变量ClickedItem便是用于保存ComboBox应该的大小以及位置。我们根据下面的代码对ClickedItem进行修正。
// 检查数据行是否已经完全滚动至左侧之外 if((ClickedItem.Left + listview->Column[0]->Width + listview->Column[1]->Width) < 0) return; // 单元格在左侧之外无法看到不做处理 // // 检查数据行是否部分被滚动至左侧之外 //By jingzhongrong else if(ClickedItem.Left + listview->Column[0]->Width < 0) { // 检查此数据行是否已延伸至ListView 的右侧之外 if((ClickedItem.Left + listview->Column[1]->Width + listview->Column[0]->Width) > listview->Width) { // 将数据行(ComboBox )的宽度设置成ListView 的宽度 ClickedItem.Left = 0; ClickedItem.Right = ClickedItem.Left + listview->Width; } else { // 单元格的右侧位于视图之中 ClickedItem.Right = ClickedItem.Left + listview->Column[0]->Width + listview->Column[1]->Width; ClickedItem.Left = 0; } } // 检查数据行是否已经完全滚动至右侧之外 else if(ClickedItem.Left + listview->Column[0]->Width > listview->Width) return; // 单元格在右侧之外无法看到不做处理 // 数据行被部分滚动至右侧之外 else if(listview->Column[1]->Width + listview->Column[0]->Width > listview->Width) { ClickedItem.Left += listview->Column[0]->Width; ClickedItem.Right = listview->Width - ClickedItem.Left; } else { ClickedItem.Left = listview->Column[0]->Width; ClickedItem.Right = listview->Column[1]->Width; } // 进行调整 ClickedItem.Left += listview->Left; ClickedItem.Top += 25; //Columns Height ClickedItem.Right += ClickedItem.Left; |
3、此时的ClickedItem已经保存ComboBox应该出现的位置,我们下面显示出ComboBox来。
this->ComboBox1->Top = ClickedItem.Top; this->ComboBox1->Left = ClickedItem.Left; this->ComboBox1->Width = ClickedItem.Width(); // 对ComboBox 显示的文本进行设置 //ComboBox 应在初始化时添加选择项并将其Style 属性设置为csDropDownList 用来限制 // 用户的选择 if(lv->SubItems->Strings[0] == " 启用") this->ComboBox1->ItemIndex = 0; else this->ComboBox1->ItemIndex = 1; //By jingzhongrong this->ComboBox1->Visible = true; this->ComboBox1->BringToFront(); this->ComboBox1->SetFocus(); |
4、至此,我们已经将ComboBox控件显示出来。下面我们处理ComboBox进行选择。在ComboBox控件的OnChange事件中添加如下代码。
AnsiString str = this->ListView1->Selected->SubItems->Strings[0]; this->ListView1->Selected->SubItems->Strings[0] = ComboBox1->Text; ComboBox1->Visible = false; // 判断ComboBox 选择前后是否有更改过 if(str != ComboBox1->Text && ComboBox1->Text == " 启用") { this->LoadPlugin(…); // 加载插件 } else if(str != ComboBox1->Text && ComboBox1->Text == " 禁用") { this->LoadPlugin(…); // 卸载插件 } |
5、继续处理ComboBox控件,在OnExit事件中添加代码,隐藏ComboBox控件。
AnsiString str = this->ListView1->Selected->SubItems->Strings[0]; if(str == "") { ComboBox1->Visible = false; } else { this->ListView1->Selected->SubItems->Strings[0] = ComboBox1->Text; ComboBox1->Visible = false; if(str != ComboBox1->Text && ComboBox1->Text == " 启用") { this->LoadPlugin(); //load } else if(str != ComboBox1->Text && ComboBox1->Text == " 禁用") { this->LoadPlugin(); //unload } } this->ListView1->SetFocus(); |
6、OnKeyPress事件对Esc键以及Enter键进行处理
switch(Key) { case VK_ESCAPE: // 其他处理,此处省略 //… this->ComboBox1->Visible = false; this->ListView1->SetFocus(); break; case VK_RETURN: this->ComboBox1->Visible = false; this->ListView1->SetFocus(); break; } |
7、在最后,我们应该对WM_VSCROLL和WM_HSCROLL两个消息进行处理。这两个消息会在用户垂直或者水平滚动ListView控件时引发。因为我们的ComboBox实际上并非ListView控件的一部分,因此,ComboBox控件并不会随着ListView控件的滚动而滚动。所以我们在这两个消息引发时将ComboBox控件隐藏起来。我们可以有几种方法来实现。可以继承TListView控件,然后对WndProc进行重载,然后在WndProc函数中过滤掉这两个消息。 下面我采用的是另外一种方法,需要在窗体OnCreate事件中加入以下代码:
OldListViewWindowProc = ListView1->WindowProc; ListView1->WindowProc = MyListViewWindowProc; |
相应的在头文件中增加以下代码:
TWndMethod OldListViewWindowProc; void __fastcall MyListViewWindowProc(TMessage &Message); |
下面是MyListViewWindowProc函数的实现
if(Message.Msg == WM_VSCROLL || Message.Msg == WM_HSCROLL) { ListView1->SetFocus(); } OldListViewWindowProc(Message); // 调用默认的处理函数处理其他消息 |
通过上述步骤,可以基本上可以实现使用ComboBox控件对ListView控件的项进行编辑。 By jingzhongrong 2007-9-19