ObjectListView cast exception (for hit testing)

2019-06-21 04:12发布

问题:

I am using Grammarian's ObjectListView. I changed my old listviews to that, but all I do is to fill in items. But when the app starts up and my mouse is over the listview, it immediately throws an exception for:

System.InvalidCastException was unhandled
  Message="Unable to cast object of type 'System.Windows.Forms.ListViewItem' to type 'BrightIdeasSoftware.OLVListItem'."
  Source="ObjectListView"

How to fix this?

Also I use Win7 if that's important.

EDIT:

I use a dictionary. It looks like I need to use the SetObjects method instead of adding items.

Ok this is great, but I am just using the dict.Value collection. I don't want to modify the data through listview, only show. So I have only 1 column and list everything there which are strings. Is this possible?

I would appreciate a small sample.

回答1:

You are right -- you should be using the SetObjects() method rather than adding ListViewItems. In an ObjectListView, there should never be ListViewItems. The control keeps track of more information and so needs more than ListViewItems provides.

You might want to read the Getting Started page of the website, especially the Unlearn you must section.

ObjectListView does have its own forum, if you want to ask questions there.



回答2:

In regards to Grammarian's answer, what they do not tell you is this.

They tell you that you need just one line to activate it, but you will find it's a headache and a lot more work then it's worth. I highly suggest you to stick to the normal ListView, otherwise you will find yourself writing a 1000 lines model class... Just to add 1 string to a ListView.

Just the truth about this ObjectListView business before you look too much into it...

Go download the example and take a look, I find it easier to just use a normal ListView frankly.



回答3:

Answer for OP:

As far as I understand, you are using a dictionary where the value type is a string.

This shows me a list of the values in the dictionary, in Details mode.

        // Create dictionary.. Can be done somewhere else..
        var dictionary = new Dictionary<int, string>();
        dictionary.Add(1, "Item 1");
        dictionary.Add(2, "Item 2");

        // You can set up the column in the designer as well.
        objectListView1.Columns.Add(new OLVColumn(title: "Items", aspect: "Value"));
        // Initially tells OLV to use the dictionary as a datasource.
        objectListView1.SetObjects(dictionary);

        // .....


        // Later on, you can add another item to the dictionary.
        dictionary.Add(3, "Item 3");
        // All you have to do now, is call .BuildList(), and your listview is updated.
        // shouldPreserveState can be false if you want. I want it to be true. :)
        objectListView1.BuildList(shouldPreserveState:true);

This is not exactly "one line", but if you do set up the column in the designer, then SetObjects() is really that one line to activate it. You just have to remember to call BuildList whenever your dictionary changes.


Answer for @ElektroStudios

Okay, so for some reason you want to use ListViewItem as your "data container". This is not the intended usage for OLV, as pointed out by @Grammarian, but as ListViewItem is a class with properties as any other class with properties, this is easily done.

This is not a "one-liner", but it most definitely isn't a write a 1000 lines model class... Just to add 1 string to a ListView. Note I specified 2 ways of setting up the getters for the columns.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        // Items collection.
        // Add your list view items to this.
        // Note the fact that we have a different amount of subitems!!!
        var items = new List<ListViewItem>
        {
            new ListViewItem(new []{"Hello", "Stack","Overflow"}), 
            new ListViewItem(new []{"ObjectListView is pretty damn neat!"}), 
            new ListViewItem(new []{"Pretty", "Cool"})
        };

        // These are set up by the WinForms Designer when I create OLV columns in the designer.

        // Here, I am telling each column to use a custom getter, created by the SubItemGetter method.
        // ensures the sub-items actually exist on each LVI.
        olvColumn1.AspectGetter = SubItemGetter(0); // ListViewItem's first sub-item is the same as ListViewItem.Text. :)
        olvColumn2.AspectGetter = SubItemGetter(1);
        olvColumn3.AspectGetter = SubItemGetter(2);

        // NOTE: I assume you know at design-time how many columns there are in your list view.
        // Set them up as I've done above, or, if you want to be fancy..
        for (int index = 0; index < objectListView1.Columns.Count; index++)
        {
            OLVColumn column = objectListView1.AllColumns[index];
            column.AspectGetter = SubItemGetter(index);
        }

        // Tells OLV to use the items collection.
        objectListView1.SetObjects(items);

        // Sometime later, probably somewhere else in the code...
        items.Add(new ListViewItem(new []{"I","Dont","Care","How","Many","SubItems","There","Is!"}));
        // Tell OLV to rebuild!
        objectListView1.BuildList(shouldPreserveState:true); // I'd like to preserve state, please :)
    }

    private AspectGetterDelegate SubItemGetter(int subItemIndex)
    {
        // This returns a method that gives OLV the string it needs to render each cell,
        // while also making sure the sub item exists.
        return rowObject =>
        {
            // Cast the row object to a ListViewItem. This should be safe.
            var lvi = (ListViewItem) rowObject;
            // Make sure the index is not out of range.
            if (lvi.SubItems.Count <= subItemIndex)
                return null;
            // Return what needs to be displayed!
            return lvi.SubItems[subItemIndex].Text;
        };
    }
}

This gives me a default looking OLV like this (note the grouping is configurable!)..



回答4:

I got a similar error when trying to pass a normal ListView in virtual mode to ListViewPrinter in OLV 2.7

The error occurred here:
#if !WITHOUT_OBJECTLISTVIEW
        /// <summary>
        /// Get the nth item from the given listview, which is in virtual mode.
        /// </summary>
        /// <param name="lv">The ListView in virtual mode</param>
        /// <param name="n">index of item to get</param>
        /// <returns>the item</returns>
        override protected ListViewItem GetVirtualItem(ListView lv, int n)
        {
// Invalid Cast happens here
            return ((VirtualObjectListView)lv).MakeListViewItem(n);
        }

It works as expected with an objectlistview. No solution except to use an objectlistview.