我有一个WinForms ListView,显然包含ListViewItems。我希望能够将click事件附加到每个项目,而不是整个ListView(然后尝试找出点击了哪个项目)。原因是我需要根据选择的项目执行不同的操作。在这方面,ListViewItem类似乎非常有限。有没有办法做我想要的,或者我被迫使用ListView.Click事件?
答案 0 :(得分:3)
我仍然会使用ListView Click事件。 我在这些情况下使用的技巧是使用ListViewItem的Tag属性。它非常适合存储每个项目数据,您可以将任何内容放入其中。
答案 1 :(得分:2)
继承ListViewItem
并使用虚拟调度根据相应ListViewItem
事件中的选定ListView
选择适当的行为可能有意义。
E.g。 (未编译)
public abstract class MyItems : ListViewItem
{
public abstract DoOperation();
}
public class MyItemA : MyItems
{
public override DoOperation()
{ /* whatever a */ }
}
public class MyItemB : MyItems
{
public override DoOperation()
{ /* whatever b */ }
}
// in ListView event
MyItems item = (MyItems)this.SelectedItem;
item.DoOperation();
正如其他人所提到的,使用适当的Tag属性也是有意义的。你选择哪种技术实际上取决于你的行为是什么(因此它在哪里,在架构上)。我假设子类更有意义,因为你正在寻找listview项目的点击,而且(对我来说)似乎更可能是表示层b / c你覆盖了一些标准的控制行为(通常只是选择一个项目而不是做出回应行为的事情。
答案 2 :(得分:1)
在大多数用例中,ListViewItem
是某个对象的UI中的表示,您要做的是执行用户ListViewItem
表示的对象的方法点击它。为了简单和可维护性,您希望在用户的鼠标单击和正在执行的实际方法之间放置一些东西。
您可以将对象存储在ListViewItem
的{{1}}属性中,然后在Click事件处理程序中引用它,但这会导致代码中存在一些固有的弱点:
Tag
这是很多转换和空引用检查。关于这段代码的真正弱点在于,如果事实证明private void MyListView_Click(object sender, EventArgs e)
{
ListView l = (ListView)sender;
if (l.SelectedItem != null)
{
MyClass obj = l.SelectedItem.Tag as MyClass;
if (obj != null)
{
obj.Method();
}
}
}
为空,或者包含除Tag
对象之外的其他内容,那么您实际上并不知道在哪里可以找到问题正在发生。
将其与这样的代码进行对比:
MyClass
当您维护此代码时,您不知道private void MyListView_Click(object sender, EventArgs e)
{
MyClass.ListViewClicked(sender as ListView);
}
方法是如何实现的,但至少知道在哪里查找它 - 在ListViewClicked
中。当你这样做时,你会看到类似的东西:
MyClass
嗯,这很有意思。在线程之后,该字典是如何填充的?你可以在MyClass的另一个方法中找到它:
public static void ListViewClicked(ListView listView)
{
if (listView.SelectedItem == null)
{
return;
}
if (ListViewItemLookup.ContainsKey(listView.SelectedItem))
{
ListViewItemLookup[listView.SelectedItem].Execute();
}
}
(合理的人可能会不同意一个类是否适合在UI中与其特定形式的表示紧密相关 - 有些人会将这些方法和这个字典隔离在一个帮助类而不是在private static Dictionary<ListViewItem, MyClass> ListViewItemLookup =
new Dictionary<ListViewItem, MyClass>();
public ListViewItem GetListViewItem()
{
ListViewItem item = new ListViewItem();
item.Text = SomeProperty;
// population of other ListViewItem columns goes here
ListViewItemLookup.Add(item, this);
return item;
}
本身,并且根据问题的其余部分是多么毛茸茸,我也可以这样做。)
这种方法解决了许多问题:它为您提供了一种简单的方法来正确处理MyClass
的Click事件,这就是您所要求的。但它也首先隔离了创建ListView
的非常简单的过程。如果您重构表单并将ListViewItem
移动到另一个表单,它会减少您必须移动的代码量。并且它减少了表单类需要知道的事情的数量,这通常是一件好事。
此外,它是可测试的。通常,在UI事件处理程序中测试代码的唯一方法是通过UI。这种方法可以让您在可以单元测试的东西中隔离UI部分的所有逻辑;唯一不能编写单元测试的是表单中的单行代码。
我应该指出,人们一直建议的另一种方法 - 继承ListView
- 也完全没问题。您将我在ListViewItem
方法中放入的逻辑放在类的构造函数中,使GetListViewItem
实例成为该类的私有属性,并公开调用MyClass
方法的Click方法。我不喜欢它的唯一原因是它仍然在你的表单中留下了相当多的代码,你无法真正进行单元测试:
MyClass
答案 3 :(得分:0)
但是,您可能会在标记字段中粘贴对委托或其他处理程序的引用(假设有ListViewItem的标记属性)。您仍然需要确定单击哪个ListViewItem,但您可以直接转到标记而不是另一个决策结构。
答案 4 :(得分:0)
你想创建一个新类(或类,如果有各种类型),它继承自ListViewItem,然后使用这些对象填充ListView(只要它们从listview继承(甚至几个级别的继承)ListView控件将带他们)。
然后将click方法添加到自定义类和listView的ItemClick事件中,只需调用单击项目的click方法即可。 (可能需要一些铸造)
答案 5 :(得分:0)
实际上没有办法使用ListViewItem。您必须使用ListView本身。通过使用ListView的'SelectedItems'属性,您可以访问选定的ListViewItems。
一个选项是覆盖ListViewItem类,在那里实现特定的东西。然后,您可以将所选项目强制转换为已覆盖的项目并执行操作。
答案 6 :(得分:0)
我真的不明白这样做的原因,而不仅仅是使用常规的ListView Click事件,但如果我这样做,你建议我将一个EventHandler委托分配给每个ListViewItem的Tag属性,然后在ListView Click事件处理程序我会检查ListViewItem.Tag&lt;&gt; null,如果是,则调用委托。