amonroejj
amonroejj

Reputation: 633

Winforms ObjectListView: inner OLVColumn instances Name property is empty string so I cannot show/hide columns by name

This question is an offshoot of: Localizing ObjectListView OLVColumn, impossible due to Empty Name property

For simplicity's sake, let's say my ObjectListView contains car information. User A wants to display only Make and Model columns. User B only wants to display Model and Year columns. These preferences would be saved to/loaded from an .ini file on the users' local machines.

I cannot loop through the columns of the ObjectListView and do if (col.Name == colNameFromIni) { col.Visible == true; } because the .Name property of every column is an empty string ("") and does not get serialized to the designer codebehind file. This never happens with any other Winforms control (Label, Button, etc.) They always get their .Name written to the designer codebehind.

In some sense, this is a flaw in Winforms itself, because OLVColumn inherits from System.Windows.Forms.ColumnHeader, and a traditional ListView has exactly the same problem. .Name is always an empty string for all columns.

I would like to patch our local build of ObjectListView.dll to force populate the .Name property, but I can't figure out how Winforms automagically knows the name of every control on the form. It somehow(?) knows the names of the OLVColumn objects since it can display them in the Edit Columns... dialog on the ObjectListView's context menu. I'm also a little fuzzy on where the best spot is to plug this in.

(Yes, per linked question at top I know that as a last resort, I can hardcode colXX.Name = "colXX"; for all columns in my source code, but future column additions are likely to get overlooked and a programmatic solution is much preferred.)

(See also: https://sourceforge.net/p/objectlistview/bugs/160/ : the ObjectListView author declared this a wont-fix so it is up to me (or us), I guess.)

Upvotes: 0

Views: 180

Answers (2)

amonroejj
amonroejj

Reputation: 633

The workaround for this is to get the OLVColumns via reflection and set their column's Name property at runtime. Every OLVColumn is a form-level field, so just pick them out of the list returned by GetFields().

        Dim allFieldInfos As FieldInfo() = GetType(FrmMain).GetFields(BindingFlags.NonPublic or BindingFlags.Instance)      
        For Each fi As FieldInfo In allFieldInfos
            If fi.FieldType Is GetType(OLVColumn) Then
                Dim instance As OLVColumn = fi.GetValue(Me)
                For Each col As OLVColumn In fdlvMain.AllColumns
                    If ReferenceEquals(col, instance) Then
                        col.Name = fi.Name
                    End If
                Next
            End If
        Next

Upvotes: 0

jason.kaisersmith
jason.kaisersmith

Reputation: 9610

As you point out, this is a bug which is not with the ObjectListView, but the underlying component. And a bug which is around since at least 2008! Therefore, I doubt it will ever be fixed by MS.

Actually, it is a problem with the Autogenerated code in the designer.

If you look at other components such as a button, then the autogenerated code adds a name such as this;

 // 
 // button2
 // 
 this.button2.Location = new System.Drawing.Point(458, 199);
 this.button2.Name = "button2";
 ...

But for ColumnHeader (Listview) and OLVColumn (ObjectListView), then this is not done, so then you end up with this.

    // 
    // olvColumn1
    // 
    this.olvColumn1.AspectName = "Name";
    this.olvColumn1.Text = "Name";

If you manually add the line

this.olvColumn1.Text = "olvColumn1";

Then the "problem" is solved.

Of course, you can't do this, because the designer will override the autogenerated code when you make any changes, and then you will lose these manually added lines. It is also not sustainable.

So I'm afraid you need to code around this with some kind of ugly solution. Some options are:

  1. Use the Tag to store the name and compare against this.
  2. Use the text instead of the name (not possible if you have multi language support!)
  3. Code the names column manually in the Constructor
  4. Set the Text to be something like "ColName;ColText" and then in your code separate these out.

I have done option 3 in the past, but only I was maintaining the code, so this was easy.

What you could do to ensure you don't have discrepancies is to add a check in your constructor to compare the actual number of columns with the number you expect (hard coded for), and throw an exception if they don't match. Also, not the best, but another way to highlight and reduce errors.

Upvotes: 0

Related Questions