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

时间:2022-08-10 00:02:29

I have a WinForms ListView, obviously containing ListViewItems. I'd like to be able to attach a click event to each item, instead of to the entire ListView (and then trying to figure out what item was clicked). The reason for this is that I need to perform a different action based on which item was selected. The ListViewItem class seems to be very limited in this regard. Is there any way to do what I want, or am I forced to use the ListView.Click event?

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

7 个解决方案

#1


I would still use the ListView Click event. A trick I've used in these situations is to use the Tag property of a ListViewItem. It's great for storing per item data and you can put anything in it.

我仍然会使用ListView Click事件。我在这些情况下使用的技巧是使用ListViewItem的Tag属性。它非常适合存储每个项目数据,您可以将任何内容放入其中。

#2


It may make sense to subclass ListViewItem and use virtual dispatch to select the appropriate behavior based on the selected ListViewItem in the appropriate ListView event.

继承ListViewItem并使用虚拟分派在适当的ListView事件中基于所选ListViewItem选择适当的行为可能是有意义的。

E.g. (uncompiled)

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();

As others have mentioned, it may also make sense to use the appropriate Tag property. Which technique you go for really depends on what your action is (and therefore where it belongs, architecturally). I assumed the subclass made more sense because you're looking for a click on a listview item, and that (to me) seems more likely to be presentation-layer b/c you're overriding some standard control behavior (which would normally just select an item) as opposed to doing something in response to behavior.

正如其他人所提到的,使用适当的Tag属性也是有意义的。你选择哪种技术实际上取决于你的行为是什么(因此它在哪里,在架构上)。我假设子类更有意义,因为你正在寻找listview项目的点击,而且(对我来说)似乎更可能是表示层b / c你覆盖了一些标准的控制行为(通常只是选择一个项目而不是做出回应行为的事情。

#3


In most use cases, a ListViewItem is a representation in the UI of some object, and what you're trying to do is execute a method of the object that the ListViewItem represents when the user clicks on it. For the sake of simplicity and maintainability, you want as few things to sit between the user's mouse-click and the actual method being executed.

在大多数用例中,ListViewItem是某个对象的UI中的表示,您要做的是执行ListViewItem在用户单击时表示的对象的方法。为了简单和可维护性,您希望在用户的鼠标单击和正在执行的实际方法之间放置很少的东西。

You can store the object in the ListViewItem's Tag property and then reference it in the Click event handler, but that results in code that's got some inherent weak points:

您可以将对象存储在ListViewItem的Tag属性中,然后在Click事件处理程序中引用它,但这会导致代码具有一些固有的弱点:

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();
      }
   }
}

That's a lot of casting and null-reference checking. And the really weak thing about this code is that if it turns out that Tag is null, or contains something other than a MyClass object, you don't really know where to look to find out where the problem is occurring.

这是很多的转换和空引用检查。关于这段代码的真正弱点在于,如果事实证明Tag为null,或者包含除MyClass对象之外的其他东西,那么您实际上并不知道在哪里查找问题发生的位置。

Contrast it with code like this:

将其与这样的代码进行对比:

private void MyListView_Click(object sender, EventArgs e)
{
   MyClass.ListViewClicked(sender as ListView);
}

When you're maintaining this code, you don't know how that ListViewClicked method is implemented, but at least you know where to look for it - in MyClass. And when you do, you'll see something like this:

当您维护此代码时,您不知道如何实现ListViewClicked方法,但至少您知道在哪里查找它 - 在MyClass中。当你这样做时,你会看到这样的事情:

public static void ListViewClicked(ListView listView)
{
   if (listView.SelectedItem == null)
   {
      return;
   }
   if (ListViewItemLookup.ContainsKey(listView.SelectedItem))
   {
      ListViewItemLookup[listView.SelectedItem].Execute();
   }
}

Well, that's interesting. Following the thread, how does that dictionary get populated? You find that in another method in MyClass:

嗯,这很有趣。在线程之后,该字典是如何填充的?你在MyClass的另一个方法中找到了:

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;
}

(Reasonable people can disagree about whether or not it's appropriate for a class to be so closely tied to a specific form of its representation in the UI - there are those who would isolate these methods and this dictionary in a helper class instead of in MyClass itself, and depending on how hairy the rest of the problem is I might do it too.)

(合理的人可能会不同意一个类是否适合在UI中与其表示的特定形式如此紧密地联系在一起 - 有些人会将这些方法和这个字典隔离在一个辅助类中,而不是在MyClass本身中,根据问题的其余部分多毛,我也可以这样做。)

This approach solves a number of problems: it gives you a simple way of handling the ListView's Click event properly, which is what you asked for. But it also isolates the not-always-trivial process of creating the ListViewItem in the first place. It reduces the amount of code you'll have to move around if you refactor your form and move the ListView to another form. And it reduces the number of things that your form class needs to know about, which is generally a good thing.

这种方法解决了许多问题:它为您提供了一种简单的方法来正确处理ListView的Click事件,这就是您所要求的。但它也首先隔离了创建ListViewItem的非常简单的过程。如果您重构表单并将ListView移动到另一个表单,它会减少您必须移动的代码量。它减少了表单类需要了解的事物的数量,这通常是一件好事。

Also, it's testable. Generally, the only way to test code in a UI event handler is through the UI. This approach lets you isolate all of the logic surrounding this part of the UI in something that you can unit test; the only thing you can't write a unit test for is a single line of code in the form.

此外,它是可测试的。通常,在UI事件处理程序中测试代码的唯一方法是通过UI。这种方法可以让您在可以单元测试的东西中隔离UI部分的所有逻辑;唯一不能编写单元测试的是表单中的单行代码。

I should point out that the other approach people have been suggesting - subclassing ListViewItem - is perfectly fine too. You put the logic I put in the GetListViewItem method in the class's constructor, make the MyClass instance a private property of the class, and expose a Click method that calls the method of MyClass. Pretty much the only reason I don't like it is that it still leaves you with a fair amount of code in your form that you can't really unit test:

我应该指出,人们一直在建议的另一种方法 - 继承ListViewItem - 也非常好。你把我放在GetListViewItem方法中的逻辑放在类的构造函数中,使MyClass实例成为该类的私有属性,并公开一个调用MyClass方法的Click方法。我不喜欢它的唯一原因是它仍然在你的表单中留下了相当多的代码,你无法进行单元测试:

ListView l = (ListView)sender;
if (l.SelectedItem != null)
{
    MyClassListViewItem item = l.SelectedItem as MyClassListViewItem;
    if (item != null)
    {
       item.MyClass.Method();
    }
}

#4


You might however have luck sticking a reference to a delegate or other handler in the tag field (assuming there is a tag property of a ListViewItem). You would still have to determine which ListViewItem is clicked, but you could then go straight to the tag instead of another decision structure.

但是,您可能会幸运地在标记字段中粘贴对委托或其他处理程序的引用(假设存在ListViewItem的标记属性)。您仍然需要确定单击哪个ListViewItem,但您可以直接转到标记而不是另一个决策结构。

#5


You want to create a new class (or classes if there are various types), which inherits from ListViewItem, then populate your ListView with these objects (as long as they inherit from listview (even several levels of inheritence) The ListView control will take them).

你想创建一个新类(或类,如果有各种类型),继承自ListViewItem,然后使用这些对象填充ListView(只要它们从listview继承(甚至几个级别的继承)ListView控件将采用它们)。

Then add a click method to your custom class(es) and on the ItemClick event of your listView, just call the click method of the clicked item. (some casting may be needed)

然后将click方法添加到自定义类和listView的ItemClick事件中,只需调用单击项的click方法即可。 (可能需要一些铸造)

#6


Actually there is no way to use a ListViewItem. You have to use the ListView itself. By using the 'SelectedItems' property of the ListView you can access the selected ListViewItems.

实际上没有办法使用ListViewItem。您必须使用ListView本身。通过使用ListView的'SelectedItems'属性,您可以访问选定的ListViewItems。

One option is to override the ListViewItem class an implement the specific stuff in there. Then you can cast the selected item to the overridden one and perform the action.

一种选择是覆盖ListViewItem类,在那里实现特定的东西。然后,您可以将所选项目强制转换为已覆盖的项目并执行操作。

#7


I really don't understand the reason to do so instead of just using the regular ListView Click event, but if I were to do like you suggest I would assign an EventHandler delegate to the Tag property of each ListViewItem, then in the ListView Click event handler I would check if the ListViewItem.Tag <> null, and if so call the delegate.

我真的不明白这样做的原因,而不仅仅是使用常规的ListView Click事件,但如果我这样做,你建议我将一个EventHandler委托分配给每个ListViewItem的Tag属性,然后在ListView Click事件中handler我会检查ListViewItem.Tag <>是否为null,如果是,则调用委托。

#1


I would still use the ListView Click event. A trick I've used in these situations is to use the Tag property of a ListViewItem. It's great for storing per item data and you can put anything in it.

我仍然会使用ListView Click事件。我在这些情况下使用的技巧是使用ListViewItem的Tag属性。它非常适合存储每个项目数据,您可以将任何内容放入其中。

#2


It may make sense to subclass ListViewItem and use virtual dispatch to select the appropriate behavior based on the selected ListViewItem in the appropriate ListView event.

继承ListViewItem并使用虚拟分派在适当的ListView事件中基于所选ListViewItem选择适当的行为可能是有意义的。

E.g. (uncompiled)

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();

As others have mentioned, it may also make sense to use the appropriate Tag property. Which technique you go for really depends on what your action is (and therefore where it belongs, architecturally). I assumed the subclass made more sense because you're looking for a click on a listview item, and that (to me) seems more likely to be presentation-layer b/c you're overriding some standard control behavior (which would normally just select an item) as opposed to doing something in response to behavior.

正如其他人所提到的,使用适当的Tag属性也是有意义的。你选择哪种技术实际上取决于你的行为是什么(因此它在哪里,在架构上)。我假设子类更有意义,因为你正在寻找listview项目的点击,而且(对我来说)似乎更可能是表示层b / c你覆盖了一些标准的控制行为(通常只是选择一个项目而不是做出回应行为的事情。

#3


In most use cases, a ListViewItem is a representation in the UI of some object, and what you're trying to do is execute a method of the object that the ListViewItem represents when the user clicks on it. For the sake of simplicity and maintainability, you want as few things to sit between the user's mouse-click and the actual method being executed.

在大多数用例中,ListViewItem是某个对象的UI中的表示,您要做的是执行ListViewItem在用户单击时表示的对象的方法。为了简单和可维护性,您希望在用户的鼠标单击和正在执行的实际方法之间放置很少的东西。

You can store the object in the ListViewItem's Tag property and then reference it in the Click event handler, but that results in code that's got some inherent weak points:

您可以将对象存储在ListViewItem的Tag属性中,然后在Click事件处理程序中引用它,但这会导致代码具有一些固有的弱点:

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();
      }
   }
}

That's a lot of casting and null-reference checking. And the really weak thing about this code is that if it turns out that Tag is null, or contains something other than a MyClass object, you don't really know where to look to find out where the problem is occurring.

这是很多的转换和空引用检查。关于这段代码的真正弱点在于,如果事实证明Tag为null,或者包含除MyClass对象之外的其他东西,那么您实际上并不知道在哪里查找问题发生的位置。

Contrast it with code like this:

将其与这样的代码进行对比:

private void MyListView_Click(object sender, EventArgs e)
{
   MyClass.ListViewClicked(sender as ListView);
}

When you're maintaining this code, you don't know how that ListViewClicked method is implemented, but at least you know where to look for it - in MyClass. And when you do, you'll see something like this:

当您维护此代码时,您不知道如何实现ListViewClicked方法,但至少您知道在哪里查找它 - 在MyClass中。当你这样做时,你会看到这样的事情:

public static void ListViewClicked(ListView listView)
{
   if (listView.SelectedItem == null)
   {
      return;
   }
   if (ListViewItemLookup.ContainsKey(listView.SelectedItem))
   {
      ListViewItemLookup[listView.SelectedItem].Execute();
   }
}

Well, that's interesting. Following the thread, how does that dictionary get populated? You find that in another method in MyClass:

嗯,这很有趣。在线程之后,该字典是如何填充的?你在MyClass的另一个方法中找到了:

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;
}

(Reasonable people can disagree about whether or not it's appropriate for a class to be so closely tied to a specific form of its representation in the UI - there are those who would isolate these methods and this dictionary in a helper class instead of in MyClass itself, and depending on how hairy the rest of the problem is I might do it too.)

(合理的人可能会不同意一个类是否适合在UI中与其表示的特定形式如此紧密地联系在一起 - 有些人会将这些方法和这个字典隔离在一个辅助类中,而不是在MyClass本身中,根据问题的其余部分多毛,我也可以这样做。)

This approach solves a number of problems: it gives you a simple way of handling the ListView's Click event properly, which is what you asked for. But it also isolates the not-always-trivial process of creating the ListViewItem in the first place. It reduces the amount of code you'll have to move around if you refactor your form and move the ListView to another form. And it reduces the number of things that your form class needs to know about, which is generally a good thing.

这种方法解决了许多问题:它为您提供了一种简单的方法来正确处理ListView的Click事件,这就是您所要求的。但它也首先隔离了创建ListViewItem的非常简单的过程。如果您重构表单并将ListView移动到另一个表单,它会减少您必须移动的代码量。它减少了表单类需要了解的事物的数量,这通常是一件好事。

Also, it's testable. Generally, the only way to test code in a UI event handler is through the UI. This approach lets you isolate all of the logic surrounding this part of the UI in something that you can unit test; the only thing you can't write a unit test for is a single line of code in the form.

此外,它是可测试的。通常,在UI事件处理程序中测试代码的唯一方法是通过UI。这种方法可以让您在可以单元测试的东西中隔离UI部分的所有逻辑;唯一不能编写单元测试的是表单中的单行代码。

I should point out that the other approach people have been suggesting - subclassing ListViewItem - is perfectly fine too. You put the logic I put in the GetListViewItem method in the class's constructor, make the MyClass instance a private property of the class, and expose a Click method that calls the method of MyClass. Pretty much the only reason I don't like it is that it still leaves you with a fair amount of code in your form that you can't really unit test:

我应该指出,人们一直在建议的另一种方法 - 继承ListViewItem - 也非常好。你把我放在GetListViewItem方法中的逻辑放在类的构造函数中,使MyClass实例成为该类的私有属性,并公开一个调用MyClass方法的Click方法。我不喜欢它的唯一原因是它仍然在你的表单中留下了相当多的代码,你无法进行单元测试:

ListView l = (ListView)sender;
if (l.SelectedItem != null)
{
    MyClassListViewItem item = l.SelectedItem as MyClassListViewItem;
    if (item != null)
    {
       item.MyClass.Method();
    }
}

#4


You might however have luck sticking a reference to a delegate or other handler in the tag field (assuming there is a tag property of a ListViewItem). You would still have to determine which ListViewItem is clicked, but you could then go straight to the tag instead of another decision structure.

但是,您可能会幸运地在标记字段中粘贴对委托或其他处理程序的引用(假设存在ListViewItem的标记属性)。您仍然需要确定单击哪个ListViewItem,但您可以直接转到标记而不是另一个决策结构。

#5


You want to create a new class (or classes if there are various types), which inherits from ListViewItem, then populate your ListView with these objects (as long as they inherit from listview (even several levels of inheritence) The ListView control will take them).

你想创建一个新类(或类,如果有各种类型),继承自ListViewItem,然后使用这些对象填充ListView(只要它们从listview继承(甚至几个级别的继承)ListView控件将采用它们)。

Then add a click method to your custom class(es) and on the ItemClick event of your listView, just call the click method of the clicked item. (some casting may be needed)

然后将click方法添加到自定义类和listView的ItemClick事件中,只需调用单击项的click方法即可。 (可能需要一些铸造)

#6


Actually there is no way to use a ListViewItem. You have to use the ListView itself. By using the 'SelectedItems' property of the ListView you can access the selected ListViewItems.

实际上没有办法使用ListViewItem。您必须使用ListView本身。通过使用ListView的'SelectedItems'属性,您可以访问选定的ListViewItems。

One option is to override the ListViewItem class an implement the specific stuff in there. Then you can cast the selected item to the overridden one and perform the action.

一种选择是覆盖ListViewItem类,在那里实现特定的东西。然后,您可以将所选项目强制转换为已覆盖的项目并执行操作。

#7


I really don't understand the reason to do so instead of just using the regular ListView Click event, but if I were to do like you suggest I would assign an EventHandler delegate to the Tag property of each ListViewItem, then in the ListView Click event handler I would check if the ListViewItem.Tag <> null, and if so call the delegate.

我真的不明白这样做的原因,而不仅仅是使用常规的ListView Click事件,但如果我这样做,你建议我将一个EventHandler委托分配给每个ListViewItem的Tag属性,然后在ListView Click事件中handler我会检查ListViewItem.Tag <>是否为null,如果是,则调用委托。