有没有一种简单的方法将事件与ListViewItem相关联?

时间:2009-07-14 16:04:29

标签: c# .net winforms listview listviewitem

我有一个WinForms ListView,显然包含ListViewItems。我希望能够将click事件附加到每个项目,而不是整个ListView(然后尝试找出点击了哪个项目)。原因是我需要根据选择的项目执行不同的操作。在这方面,ListViewItem类似乎非常有限。有没有办法做我想要的,或者我被迫使用ListView.Click事件?

7 个答案:

答案 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,如果是,则调用委托。