没有翅膀得鸟 發表於 2025-8-11 20:46:00

一步一步学习使用LiveBindings(11) 绑定到自定义外观的ListBox

<p>虽然在Firemonkey中,TListView是与LiveBindings绑定最为友善的。但是ListBox在一些短平快的中小型的选项列表中也是非常不错的选择。</p>
<p>在本课中,将学习到:</p>
<ul>
<li>如何自定义ListBox的外观。</li>
<li>如何使用LiveBindings绑定到具有自定义外观的ListBox。</li>
</ul>
<p>请将你在《一步一步学习使用LiveBindings(10)》中的项目复制一份,然后开启这次列表的实际操作。</p>
<p>在这个练习中,将在TGrid的右侧放一个TListBox,以便进行数据的同步演示,真实的场景中,TListBox应该是类似于上一课的TCombobox,是一个可以多选的复选框。最终效果如下图:</p>

<p><img src="https://img2024.cnblogs.com/blog/22554/202508/22554-20250811203604178-1141999081.gif"></p>
<p>如果您是从《一步一步学习使用LiveBindings(8)》一路学过来的话,就会明白为什么要在那一课详细的介绍向导,因为在那之后的几课中,都反复的在使用向导先构建绑定的控件,然后再对控件进行微调。</p>
<p>步骤如下:</p>
<p><strong>1. 现在再次右击主窗口,单击“LiveBindings Wizard”打开向导。在向导中选择第一项“Link a Control With a Field”,在第二个任务页选择“New Control”,选择TListBox,第三个任务页选择“ProtoTypeBindSource1”,也就是Employee列表,第四个任务页选择Field为“ContactName”字段。第5个任务页什么也不选,单击Finish完成。</strong></p>

<p><img src="https://img2024.cnblogs.com/blog/22554/202508/22554-20250811054251488-756782158.gif"></p>
<p>然后,一个与Grid保持同步的,非常简陋的TListBox就出现了。稍稍调整一下布局,效果如下图所示:</p>

<p><img src="https://img2024.cnblogs.com/blog/22554/202508/22554-20250811054735557-1410728166.gif"></p>
<p><strong>2. 从工具面板拖一个TStyleBook到主窗体,确保主窗体的StyleBook指向了这个TStyleBook控件。双击这个控件,将进入到样式设计窗口。</strong></p>
<p>选中根节点StyleContainer,然后从工具栏拖一个TLayout到设计窗口上,指定高度为64,宽度为300,在属性窗口设置StyleName为“customitem”。现在有了一个空白的项。</p>

<p><img src="https://img2024.cnblogs.com/blog/22554/202508/22554-20250811055736774-2045048291.png"></p>
<p><strong>3. 从工具面板拖一个TLayout到第2步的TLayout控件内,这个时候应该严重依赖于Structure窗口,因为很容易布局出现混乱,需要重新刷新设计面板,甚至可能需要关闭样式窗口。</strong></p>
<p>先将这个TLayout指定Align为MostLeft,在这个TLayout内部,再拖一个TCheckBox控件,指定其Align为Center,StyleName为avalilnow。</p>
<p>再拖一个TImage控件在customitem这个TLayout内,指定其Align也为MostLeft,styleName为icon。<br>
布局如下所示:</p>

<p><img src="https://img2024.cnblogs.com/blog/22554/202508/22554-20250811064447419-1736785626.png"></p>
<p><strong>4. 接下来,在customitem布局内部加一个TLayout控件,指定其Align为MostRight,在里边加一个TSpeedButton,指定其stylename为“info”。</strong></p>
<p>最后,加了4个Align为Top的TText控件,分别命名为“text",“hiredate”,“salary”和“detail”。</p>
<h3 id="特别注意这里的大小写将来在代码中引用时要区分大小写">特别注意这里的大小写,将来在代码中引用时,要区分大小写。</h3>


<p><img src="https://img2024.cnblogs.com/blog/22554/202508/22554-20250811183009309-1592599252.png"></p>
<h5 id="注意请将控件的hittest均设置为false">注意:请将控件的HitTest均设置为False。</h5>
<h5 id="这4个ttext控件的textsettingshorzalign均设为leading表示将从水平开头位置进行显示">这4个TText控件的TextSettings.HorzAlign均设为Leading,表示将从水平开头位置进行显示。</h5>
<p>由图中可以看到,这里还放了3个accessorymore、accessorycheckmark和accessorydetail,其visible都为false。</p>
<p>这些都是复制自标准的ListBoxItemStyle样式。可以从工具栏拖一个新的TListBox在主界面上,手动增加一个Item,指定StyleLookUp为“listboxitembottomdetail”,然后右击鼠标,选择“Edit Custom Style...”,在StyleBook1中就会新加一个ListBoxItem1Style1样式,复制里边的3个accessory。</p>
<p>做完这一切后,将刚刚添加的TListBox删除。</p>
<p><strong>4. 回到LiveBindings Desinger视图,由于现在绑定对象越来越多,为了更快的找到需要的对象,这里选中了ListBoxContactName和PrototypeBindSource1绑定框,右击鼠标,选择 Layers &gt; Add to New Layer菜单项,将其添加到一个新的Layer。</strong></p>

<p><img src="https://img2024.cnblogs.com/blog/22554/202508/22554-20250811073035792-1674946801.png"></p>
<p>在右上角的Layer下拉列表框中,会增加一个新的Layer,将它重命名为友好的名称,以后只要单击Layer名就可以快速定位到绑定框。</p>

<p><img src="https://img2024.cnblogs.com/blog/22554/202508/22554-20250811073215783-267502282.gif"></p>
<ul>
<li>确保将Item.text关联到ContactName。</li>
<li>Item.Detail关联到Title。</li>
<li>Item.Bitmap关联到ContactBitmap。</li>
</ul>
<p><strong>5. 选中一条链接,可以看到选中了属性编辑器选中了LinkListControlToFieldContactName,然后切换到Events选项卡,为OnFillingListItem添加事件处理代码。</strong></p>
<pre><code class="language-Pascal">procedure TfrmMain.LinkListControlToFieldContactNameFillingListItem(
Sender: TObject; const AEditor: IBindListEditorItem);
var
Item:TListBoxItem;
begin
//得到当前的TListBoxItem
Item:= (AEditor.CurrentObject as TListBoxItem);
//指定项的高度是样式设计器的高度
Item.Height:=68;
//指定样式为自定义的样式名。
Item.StyleLookup :='customitem';
//暂时指定Accessory为aMore.
Item.ItemData.Accessory:=TListBoxItemData.TAccessory.aMore;
end;
</code></pre>
<p>按F9键运行程序,自定义的ListBox果然显示了不俗的效果。</p>

<p><img src="https://img2024.cnblogs.com/blog/22554/202508/22554-20250811185305147-2008610447.gif"></p>
<p>虽然有一部分文本框已经显示了数据,但是Salary、HireDate和AvailNow仍然没有显示,继续添加代码,LinkListControlToFieldContactNameFillingListItem完整的处理代码如下:</p>
<pre><code class="language-Pascal">procedure TfrmMain.LinkListControlToFieldContactNameFillingListItem(
Sender: TObject; const AEditor: IBindListEditorItem);
var
Item:TListBoxItem;
emp:TEmployee;
begin
//得到当前的TListBoxItem
Item:= (AEditor.CurrentObject as TListBoxItem);
//指定项的高度是样式设计器的高度
Item.Height:=70;
//指定样式为自定义的样式名。
Item.StyleLookup :='customitem';
//暂时指定Accessory为aMore.
Item.ItemData.Accessory:=TListBoxItemData.TAccessory.aMore;
//得到当前记录对象
emp:=bsEmployee.List;
if emp&lt;&gt;nil then
begin
    Item.StylesData['availnow'] := emp.AvailNow;
    Item.StylesData['hiredate'] := formatdatetime('MM/DD/YYYY',emp.HireDate);
    Item.StylesData['salary'] := format('%.2m',);
    //设置按钮的单击事件
    Item.StylesData['info.OnClick'] := TValue.From&lt;TNotifyEvent&gt;(DoInfoClick); // set OnClick value
end;
end;
</code></pre>
<p>这里使用Item.StylesData键值对给样式中的其他对象进行了赋值,最后<br>
注意这个语法TValue.From<tnotifyevent>(DoInfoClick),这是为info.OnClick事件关联了事件处理代码。</tnotifyevent></p>
<p>代码如下:</p>
<pre><code class="language-Pascal">//递归查找样式对象的函数
function FindItemParent(Obj: TFmxObject; ParentClass: TClass): TFmxObject;
begin
Result := nil;
if Assigned(Obj.Parent) then
    if Obj.Parent.ClassType = ParentClass then
      Result := Obj.Parent
    else
      Result := FindItemParent(Obj.Parent, ParentClass);
end;


procedure TfrmMain.DoInfoClick(Sender: TObject);
var
Item : TListBoxItem;
begin
//得到当前样式对象的顶层TListBoxItem容器对象实例
Item := TListBoxItem(FindItemParent(Sender as TFmxObject,TListBoxItem));
//显示信息
if Assigned(Item) then
    ShowMessage('你单击了 ' + IntToStr(Item.Index) + ' 号项目');
end;

</code></pre>
<p>FindItemParent这个函数将会查找当前Info样式对象的父项,递归直到找到TListBoxItem对象,获得当前项的信息,再用ShowMessage显示信息。</p>
<p>最终效果如下:</p>

<p><img src="https://img2024.cnblogs.com/blog/22554/202508/22554-20250811203459413-508966391.png"></p>
<p>当单击“详细”按钮时,可以看到果然成功的响应了事件处理代码。</p>

<p><img src="https://img2024.cnblogs.com/blog/22554/202508/22554-20250811203604178-1141999081.gif"></p>
<p>Item.ItemData实际上和Item.StylesData是完成了类似的功能,Item.ItemData会访问样式中预定义的功能,比如一个StyleName为“text”的TText控件表示文本,一个StyleName为icon的TImage用来显示图像。Accessory则用来显示右侧的按钮栏。</p>
<p>它们也会出现在可绑定的列表,如果无法直接绑定,只能通过Item.StylesData['salary']这样的语法进行操作。</p>
<h2 id="总结">总结</h2>
<p>TListBox虽然提供了无比强大的自定义功能,但它仍然不是万能的,它不适合显示特别复杂的项,适合于简短的,只读的或者是外观不是特别复杂的对象。</p>
<p>TListView是与LiveBindings绑定友好的,更加强大的列表控件,将在下一节介绍。</p><br><br>
来源:https://www.cnblogs.com/lincats/p/19032685
頁: [1]
查看完整版本: 一步一步学习使用LiveBindings(11) 绑定到自定义外观的ListBox