WPF开发为按钮提供添加,删除和重新排列ListBox内容的功能
介绍
我有一种情况,我希望能够将项目添加到列表中,并在列表中移动项目,这似乎是使用a的最简单方法listbox
。我立刻想到了如何以通用的方式做到这一点,然后,也许,可以使用行为来做到这一点。这似乎是一个非常有用的想法。我决定以一种简单的方式为我正在开发的应用程序做这件事,但我想我会创建一个演示项目来探索这个想法。这是结果。
概观
该行为实际上有四个独立的部分,可以在一个类中执行不同的功能:
- 添加项目
- 将所选项目向上移动一个位置
- 将所选项目向下移动一个位置
- 删除所选项目。
每个函数的代码结构非常相似,只有一些细节不同。
将要检查的代码是move up函数的代码。
首先是以下定义dependencyproperty
:
public static readonly dependencyproperty moveitemupproperty = dependencyproperty.registerattached("moveitemup", typeof(selector), typeof(listhelperbehavior), new propertymetadata(null, onmoveitemupchanged)); public static selector getmoveitemup(uielement uielement) { return (selector)uielement.getvalue(moveitemupproperty); } public static void setmoveitemup(uielement uielement, selector value) { uielement.setvalue(moveitemupproperty, value); }
这用于为包含列表的selector
(或listbox
)控件提供绑定。它用于button
执行动作,在这种情况下是将所选项目向上移动一个位置。对于这个动作的代码需要有机会获得itemssource
和selectedindex
的selector
控制,首先要真正能够做到移动,第二知道要移动的项目。
对于所有操作,此代码几乎相同,只是add item不需要监视selectionchanged
事件selector
,并且button
永远不会禁用。
当此dependencyproperty
更改时,将onmoveupitemchanged
执行事件处理程序。此事件处理程序在dependencyproperty
registerattached方法的frameworkmetadata参数中指定。
private static void onmoveitemupchanged(dependencyobject d, dependencypropertychangedeventargs e) { if (e.oldvalue is selector selector1) { selector1.selectionchanged -= setmoveitemupbuttonisenabled; } if (e.newvalue is selector selector) { var button = checkforbuttonbase(d); button.click -= moveitemupevent; button.click += moveitemupevent; selector.setvalue(moveupbutton, button); selector.selectionchanged += setmoveitemupbuttonisenabled; setmoveitemupbuttonisenabled(selector, null); } }
此代码将事件处理程序附加到button
click事件和selector
selectionchanged
事件。为了确保button
在订阅事件之前没有双重订阅click事件,并且删除selectionchanged
旧事件的事件处理程序selector
(如果存在)。此外,button
它保存在附件dependencyproperty
中,selector
以便可以找到它以供selectionchanged
事件处理程序使用。最后,button
通过使用selectionchanged
事件处理程序调整isenabled值。
为的保存代码button
在selector
被下面的私人dependencyproperty
从而使button
被启用和禁用,可以发现:
private static readonly dependencyproperty moveupbutton = dependencyproperty.registerattached("moveupbutton", typeof(buttonbase), typeof(listhelperbehavior), new propertymetadata(null));
add item代码不需要监视selectionchanged事件,因为button
从不禁用它。
的click事件button
的下移功能如下:
private static void moveitemupevent(object sender, routedeventargs e) { debug.assert(sender is buttonbase); var button = (buttonbase)sender; var selector = getmoveitemup(button); var ilist = checkforilist(selector); var itemnumber = selector.selectedindex; var item = ilist[itemnumber]; ilist.removeat(itemnumber); var type = ilist.gettype().getgenericarguments().single(); var castinstance = convert.changetype(item, type); ilist.insert(itemnumber - 1, castinstance); if (itemnumber == 1) button.isenabled = false; selector.selectedindex = itemnumber - 1; }
sender参数必须强制转换为buttonbase类型,然后用于获取selector
作为buttonbase中附加属性保存的控件的值。然后使用它来获取ilist
绑定到selector
itemssource
dependencyproperty
的selecteditem
值和值selector
。ilist
然后复制所选项目,转换为正确的类型(使用type类的reflection getgenericargument方法获取类型,然后使用convert.changetype方法将其强制转换),然后从ilist
(removeat方法)中删除ilist
)。然后使用该selector
insert
方法插入删除的项目。
接下来检查是否现在是第一个项目,禁用button
它是否为,并且selector
selectedindex
设置为仍然指向同一个项目。
该移码几乎是相同的,则删除要简单得多,因为它没有保存已删除的项目,然后将其放回ilist
。
最后,有适当的代码启用或禁用button
取决于是否存在selecteditem
,selecteditem
是第一个(用于上移)或最后一个项目ilist
(用于下移)。这是selecteditem
在selector
触发事件时调用的事件处理程序:
private static void setmoveitemupbuttonisenabled(object sender, routedeventargs e) { <code> debug.assert(sender is selector); var selector = (selector)sender; var ilist = checkforilist(selector); var itemnumber = selector.selectedindex; var button = (buttonbase) selector.getvalue(moveupbutton); button.isenabled = (itemnumber >= 1 && itemnumber < ilist.count); }</code>
对于这种需要ilist
绑定到itemssource
的selectedindex
,并需要得到button
保存为一个附加属性在此功能selector
。对于remove函数,只需要知道if selectedindex
是否等于-1,这样简单得多。
使用行为
要使用此行为,只需要一个从selector
控件派生的列表控件,name
为此控件关联一个值,并button
为每个应该实现的函数定义一个。在每一个button
xaml只包括listhelperbahavior
与dependencyproperty
它有关联binding
的selector
:
<grid margin="10"> <grid.rowdefinitions> <rowdefinition height="*"/> <rowdefinition height="auto"/> </grid.rowdefinitions> <listbox name="thelist" itemssource="{binding list}" horizontalalignment="stretch" verticalalignment="stretch" > <listbox.itemtemplate> <datatemplate> <grid> <grid.columndefinitions> <columndefinition width="30"/> <columndefinition width="200"/> </grid.columndefinitions> <textblock text="{binding itemnumber}"/> <textblock grid.column="1" text="{binding timecreated}"/> </grid> </datatemplate> </listbox.itemtemplate> </listbox> <stackpanel grid.row="2" margin="-5 5" orientation="horizontal" horizontalalignment="right"> <button content="add" width="70" margin="5" local:listhelperbehavior.addtolist="{binding elementname=thelist}"/> <button content="remove" width="70" margin="5" local:listhelperbehavior.removefromlist="{binding elementname=thelist}"/> <button content="move up" width="70" margin="5" local:listhelperbehavior.moveitemup="{binding elementname=thelist}"/> <button content="move down" width="70" margin="5" local:listhelperbehavior.moveitemdown="{binding elementname=thelist}"/> </stackpanel>
问题
此行为存在一些限制,其中一些可以使用其他代码进行处理。
其中一个问题是行为预期绑定到该类型selector
的类型的ilist
,这意味着这两个list
和observablecollection
可使用,但array
type
不能。这可以编码,但需要array
每次重新创建。
另一个限制是add只有type
在它ilist
是一个类时才有效,并且有一个默认的构造函数。
当然另一个限制是它只处理从控件派生的selector
控件。
结论
这是一个非常好的小行为,因为它允许更改列表的顺序,并通过仅将行为添加button
到实现该功能的每个项目来添加或删除项目。在viewmodel中无需任何操作即可提供此功能。