Adding an atribute to the Columns collection for Listview

N

news.microsoft.com

Hi,

I want to extend the functionality of the ListView control supplied by
system.windows.forms.

In order to do so I want to add a property to the listview's Columns
property. To do this I have done the following:

------------
public MyListView : System.Windows.Forms.ListView
{
public MyColumnHeader : System.Windows.Forms.ColumnHeader
{
private int myval = 0;

public int MyVal
{
set { myval = value; }
get { return myval; }
}

private MyColumnHeader[] mycolumnheaders;
public new MyColumnHeader[] Columns
{
set { mycolumnheader = value; }
get { return mycolumnheaders; }
}
}
}
-----------

When MyListView (from the above):
1. The "Columns" property no longer has "(Collection)" in the columns
property.
2. When I add columns by pressing the "..." button to invoke the "collection
editor", all the items seem to add correctly (including the associate code).
3. When I re-open the "collection editor" by pressing the "..." button, all
the column properties seem to have disappeared.

Can anyone answer (1) and (3) above?

Thanks,

Dave
 
C

Claes Bergefall

Columns property is not a collection, it's an array
Create a MyColumnHeaderCollection class (inherit CollectionBase) and you'll
probably solve both (1) and (3)
Also make the property read only and add the
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
attribute to it

And then keep your fingers crossed that you don't need a type converter :)

/claes
 
N

news.microsoft.com

Claes,

Thanks for your reply.

I noticed the code I had posted was incorrect in that the Column's
declaration belongs to ListView, not column header.

But that aside I followed your advice with deriving my own collection (I did
so from ListView.ColumnHeaderCollection) and using the
designerserializationvisibility... attribute and all went well.

I am left with one last problem. When the code is generated by the designer
the AddRange code is incorrect. ie. it says:

this.mylistview.Columns.AddRange(new System.Window.Forms.ColumnHeader[] {
.....

The problem is the ColumnHeader[]. I was hoping this would be
MyColumnHeader[] {...

Does anyone know why this might be?

Thanks,

Dave

Claes Bergefall said:
Columns property is not a collection, it's an array
Create a MyColumnHeaderCollection class (inherit CollectionBase) and
you'll probably solve both (1) and (3)
Also make the property read only and add the
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
attribute to it

And then keep your fingers crossed that you don't need a type converter
:)

/claes

news.microsoft.com said:
Hi,

I want to extend the functionality of the ListView control supplied by
system.windows.forms.

In order to do so I want to add a property to the listview's Columns
property. To do this I have done the following:

------------
public MyListView : System.Windows.Forms.ListView
{
public MyColumnHeader : System.Windows.Forms.ColumnHeader
{
private int myval = 0;

public int MyVal
{
set { myval = value; }
get { return myval; }
}

private MyColumnHeader[] mycolumnheaders;
public new MyColumnHeader[] Columns
{
set { mycolumnheader = value; }
get { return mycolumnheaders; }
}
}
}
-----------

When MyListView (from the above):
1. The "Columns" property no longer has "(Collection)" in the columns
property.
2. When I add columns by pressing the "..." button to invoke the
"collection editor", all the items seem to add correctly (including the
associate code).
3. When I re-open the "collection editor" by pressing the "..." button,
all the column properties seem to have disappeared.

Can anyone answer (1) and (3) above?

Thanks,

Dave
 
C

Claes Bergefall

I don't think you will be able to actually inherit ColumnHeaderCollection
and get it to work. What object are you in that case returning from your
Columns property? If you return the Columns property of the base listview
you will get the wrong type back (might explain your AddRange problem). An
if you return some internal variable of your derived class you'll need to
map things between that and the real columns in the listview.

Could you show some code of what you're doing?

I have done similar things when creating a listview with columns and a tree
structure in the first column (i.e. a TreeListView). I ended up creating a
new ListViewItem and ListViewItemCollection class and forwarded everything
to an internal (non-inherited)listview. It was a lot of work to get
everything to play nicely with the designer. Fortunately for you the
ColumnHeader and ColumnHeaderCollection are a lot smaller than ListViewItem
and ListViewItemCollection, so it should be easier

/claes

news.microsoft.com said:
Claes,

Thanks for your reply.

I noticed the code I had posted was incorrect in that the Column's
declaration belongs to ListView, not column header.

But that aside I followed your advice with deriving my own collection (I
did so from ListView.ColumnHeaderCollection) and using the
designerserializationvisibility... attribute and all went well.

I am left with one last problem. When the code is generated by the
designer the AddRange code is incorrect. ie. it says:

this.mylistview.Columns.AddRange(new System.Window.Forms.ColumnHeader[]
{ ....

The problem is the ColumnHeader[]. I was hoping this would be
MyColumnHeader[] {...

Does anyone know why this might be?

Thanks,

Dave

Claes Bergefall said:
Columns property is not a collection, it's an array
Create a MyColumnHeaderCollection class (inherit CollectionBase) and
you'll probably solve both (1) and (3)
Also make the property read only and add the
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
attribute to it

And then keep your fingers crossed that you don't need a type converter
:)

/claes

news.microsoft.com said:
Hi,

I want to extend the functionality of the ListView control supplied by
system.windows.forms.

In order to do so I want to add a property to the listview's Columns
property. To do this I have done the following:

------------
public MyListView : System.Windows.Forms.ListView
{
public MyColumnHeader : System.Windows.Forms.ColumnHeader
{
private int myval = 0;

public int MyVal
{
set { myval = value; }
get { return myval; }
}

private MyColumnHeader[] mycolumnheaders;
public new MyColumnHeader[] Columns
{
set { mycolumnheader = value; }
get { return mycolumnheaders; }
}
}
}
-----------

When MyListView (from the above):
1. The "Columns" property no longer has "(Collection)" in the columns
property.
2. When I add columns by pressing the "..." button to invoke the
"collection editor", all the items seem to add correctly (including the
associate code).
3. When I re-open the "collection editor" by pressing the "..." button,
all the column properties seem to have disappeared.

Can anyone answer (1) and (3) above?

Thanks,

Dave
 
N

news.microsoft.com

Claes,

I have to thankyou for all your help. Without it I'd still be puzzled.

Anyway I found that AddRange to use an array of my derived columnheader did
not make a difference to the end result.

All in all, all I wanted to do was add an extra member to the standard
columnheader class for listview.

I did so by:

1. deriving a class from System.Windows.Forms.ColumnHeader. Inside here I
added my new member.
2. derived my own collection class from
System.Windows.Forms.ListView.ColumnHeaderCollection. Inside here, my
constructor called the base class. In addition to this I overrode the []
accessor to derived ColumnHeader.
3. I also overrode (by way of new) my derived ListView's Column member (to
replace it with my derived one).
4. I also had to specify the DesignerSerializationVisibility.Content
attribute for the overriden ListView's Column member.

This saved me from defining the whole class again.

If I am not clear just let me know and I'll post some code for you.

Thanks again,

Dave

Claes Bergefall said:
I don't think you will be able to actually inherit ColumnHeaderCollection
and get it to work. What object are you in that case returning from your
Columns property? If you return the Columns property of the base listview
you will get the wrong type back (might explain your AddRange problem). An
if you return some internal variable of your derived class you'll need to
map things between that and the real columns in the listview.

Could you show some code of what you're doing?

I have done similar things when creating a listview with columns and a
tree structure in the first column (i.e. a TreeListView). I ended up
creating a new ListViewItem and ListViewItemCollection class and forwarded
everything to an internal (non-inherited)listview. It was a lot of work to
get everything to play nicely with the designer. Fortunately for you the
ColumnHeader and ColumnHeaderCollection are a lot smaller than
ListViewItem and ListViewItemCollection, so it should be easier

/claes

news.microsoft.com said:
Claes,

Thanks for your reply.

I noticed the code I had posted was incorrect in that the Column's
declaration belongs to ListView, not column header.

But that aside I followed your advice with deriving my own collection (I
did so from ListView.ColumnHeaderCollection) and using the
designerserializationvisibility... attribute and all went well.

I am left with one last problem. When the code is generated by the
designer the AddRange code is incorrect. ie. it says:

this.mylistview.Columns.AddRange(new System.Window.Forms.ColumnHeader[]
{ ....

The problem is the ColumnHeader[]. I was hoping this would be
MyColumnHeader[] {...

Does anyone know why this might be?

Thanks,

Dave

Claes Bergefall said:
Columns property is not a collection, it's an array
Create a MyColumnHeaderCollection class (inherit CollectionBase) and
you'll probably solve both (1) and (3)
Also make the property read only and add the
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
attribute to it

And then keep your fingers crossed that you don't need a type converter
:)

/claes

Hi,

I want to extend the functionality of the ListView control supplied by
system.windows.forms.

In order to do so I want to add a property to the listview's Columns
property. To do this I have done the following:

------------
public MyListView : System.Windows.Forms.ListView
{
public MyColumnHeader : System.Windows.Forms.ColumnHeader
{
private int myval = 0;

public int MyVal
{
set { myval = value; }
get { return myval; }
}

private MyColumnHeader[] mycolumnheaders;
public new MyColumnHeader[] Columns
{
set { mycolumnheader = value; }
get { return mycolumnheaders; }
}
}
}
-----------

When MyListView (from the above):
1. The "Columns" property no longer has "(Collection)" in the columns
property.
2. When I add columns by pressing the "..." button to invoke the
"collection editor", all the items seem to add correctly (including the
associate code).
3. When I re-open the "collection editor" by pressing the "..." button,
all the column properties seem to have disappeared.

Can anyone answer (1) and (3) above?

Thanks,

Dave
 
C

Claes Bergefall

The problem is that ColumnHeaderCollection is so tightly coupled to
ColumnHeader that you'll have a hard time convincing it to use your derived
class instead. So, unfortunately, I don't see an easy way to accomplish what
you want.

I would create two new classes, MyColumnHeader and MyColumnHeaderCollection,
that internally holds a reference to ColumnHeader and ColumnHeaderCollection
resp., and then forward calls to the internal members.

Something like this (not complete)

class MyColumnHeader : ICloneable
{
private ColumnHeader m_header;

public MyColumnHeader
{
m_header = new ColumnHeader;
}

internal MyColumnHeader(ColumnHeader header)
{
m_header = header
}

internal ColumnHeader InternalHeader
{
get
{
return m_header;
}
}

public virtual object Clone()
{
ColumnHeader header = (ColumnHeader) m_header.Clone();
return new MyColumnHeader(header);
}

public override string ToString()
{
return m_header.ToString();
}

// TODO: Add wrappers for every public property
// in ColumnHeader and forward the calls to m_header.
// Also add any new properties/methods you want
}

class MyColumnHeaderCollection : CollectionBase
{
private ListView m_owner;

public MyColumnHeaderCollection(ListView owner)
{
m_owner = owner;
}

public MyColumnHeader this[int index]
{
return (MyColumnHeader) List[index];
}

public int Add(MyColumnHeader header)
{
return List.Add(header);
}

public MyColumnHeader(string text, int width, HorizontalAlignment align)
{
MyColumnHeader header = new MyColumnHeader();
header.Text = text;
header.Width = width;
header.TextAlign = align;
this.Add(header);
return header;
}

protected override void OnInsertComplete(int index, object value)
{
base.OnInsertComplete(index, value);
ColumnHeader header = ((MyColumnHeader) value).InternalHeader;
m_owner.Columns.Insert(index, header);
}

protected override void OnRemoveComplete(int index, object value)
{
base.OnRemoveComplete(index, value);
ColumnHeader header = ((MyColumnHeader) value).InternalHeader;
m_owner.Columns.Remove(index, header);
}

protected override void OnSetComplete(int index, object oldValue, object
newValue)
{
base.OnSetComplete(index, oldValue, newValue);
ColumnHeader header = ((MyColumnHeader) newValue).InternalHeader;
m_owner.Columns[index] = header;
}

protected override void OnClearComplete()
{
base.OnClearComplete();
m_owner.Columns.Clear();
}

// TODO: Implement the rest of the collection (AddRange,
// Contains, IndexOf, Insert, Remove and CopyTo). You don't
// need to manipulate m_owner from inside them. That is
// handled by the overrides above. Simply implement them
// as you would do in a normal collection
}

I don't think you'll need a type converter for this since MyColumnHeader
only has a default constructor, so that will spare you some trouble.

/claes


news.microsoft.com said:
Claes,

I have to thankyou for all your help. Without it I'd still be puzzled.

Anyway I found that AddRange to use an array of my derived columnheader
did not make a difference to the end result.

All in all, all I wanted to do was add an extra member to the standard
columnheader class for listview.

I did so by:

1. deriving a class from System.Windows.Forms.ColumnHeader. Inside here I
added my new member.
2. derived my own collection class from
System.Windows.Forms.ListView.ColumnHeaderCollection. Inside here, my
constructor called the base class. In addition to this I overrode the []
accessor to derived ColumnHeader.
3. I also overrode (by way of new) my derived ListView's Column member (to
replace it with my derived one).
4. I also had to specify the DesignerSerializationVisibility.Content
attribute for the overriden ListView's Column member.

This saved me from defining the whole class again.

If I am not clear just let me know and I'll post some code for you.

Thanks again,

Dave

Claes Bergefall said:
I don't think you will be able to actually inherit ColumnHeaderCollection
and get it to work. What object are you in that case returning from your
Columns property? If you return the Columns property of the base listview
you will get the wrong type back (might explain your AddRange problem). An
if you return some internal variable of your derived class you'll need to
map things between that and the real columns in the listview.

Could you show some code of what you're doing?

I have done similar things when creating a listview with columns and a
tree structure in the first column (i.e. a TreeListView). I ended up
creating a new ListViewItem and ListViewItemCollection class and
forwarded everything to an internal (non-inherited)listview. It was a lot
of work to get everything to play nicely with the designer. Fortunately
for you the ColumnHeader and ColumnHeaderCollection are a lot smaller
than ListViewItem and ListViewItemCollection, so it should be easier

/claes

news.microsoft.com said:
Claes,

Thanks for your reply.

I noticed the code I had posted was incorrect in that the Column's
declaration belongs to ListView, not column header.

But that aside I followed your advice with deriving my own collection (I
did so from ListView.ColumnHeaderCollection) and using the
designerserializationvisibility... attribute and all went well.

I am left with one last problem. When the code is generated by the
designer the AddRange code is incorrect. ie. it says:

this.mylistview.Columns.AddRange(new System.Window.Forms.ColumnHeader[]
{ ....

The problem is the ColumnHeader[]. I was hoping this would be
MyColumnHeader[] {...

Does anyone know why this might be?

Thanks,

Dave

Columns property is not a collection, it's an array
Create a MyColumnHeaderCollection class (inherit CollectionBase) and
you'll probably solve both (1) and (3)
Also make the property read only and add the
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
attribute to it

And then keep your fingers crossed that you don't need a type converter
:)

/claes

Hi,

I want to extend the functionality of the ListView control supplied by
system.windows.forms.

In order to do so I want to add a property to the listview's Columns
property. To do this I have done the following:

------------
public MyListView : System.Windows.Forms.ListView
{
public MyColumnHeader : System.Windows.Forms.ColumnHeader
{
private int myval = 0;

public int MyVal
{
set { myval = value; }
get { return myval; }
}

private MyColumnHeader[] mycolumnheaders;
public new MyColumnHeader[] Columns
{
set { mycolumnheader = value; }
get { return mycolumnheaders; }
}
}
}
-----------

When MyListView (from the above):
1. The "Columns" property no longer has "(Collection)" in the columns
property.
2. When I add columns by pressing the "..." button to invoke the
"collection editor", all the items seem to add correctly (including
the associate code).
3. When I re-open the "collection editor" by pressing the "..."
button, all the column properties seem to have disappeared.

Can anyone answer (1) and (3) above?

Thanks,

Dave
 
D

Dave Parry

Claes,

I assure you it works. In my derived listview class I had to override the
Columns attribute.

eg.

public MyNewColumnCollection :
System.Windows.Forms.ListView.ColumnHeaderCollection
{
public MyNewColumnCollection(ListView owner) : base(owner) {}

public new MyNewColumn this[int index]
{
get { return this[index]; }
}
}

private MyNewColumnCollection m_mynewcolumncollection;

public new MyNewColumncollection Columns
{
get { return m_mynewcolumncollection; }
set { m_mynewcolumncollection = value; }
}

Let me know if I havnt explained enough.

Thanks,

Dave

All I did was derived my class from
Claes Bergefall said:
The problem is that ColumnHeaderCollection is so tightly coupled to
ColumnHeader that you'll have a hard time convincing it to use your
derived class instead. So, unfortunately, I don't see an easy way to
accomplish what you want.

I would create two new classes, MyColumnHeader and
MyColumnHeaderCollection, that internally holds a reference to
ColumnHeader and ColumnHeaderCollection resp., and then forward calls to
the internal members.

Something like this (not complete)

class MyColumnHeader : ICloneable
{
private ColumnHeader m_header;

public MyColumnHeader
{
m_header = new ColumnHeader;
}

internal MyColumnHeader(ColumnHeader header)
{
m_header = header
}

internal ColumnHeader InternalHeader
{
get
{
return m_header;
}
}

public virtual object Clone()
{
ColumnHeader header = (ColumnHeader) m_header.Clone();
return new MyColumnHeader(header);
}

public override string ToString()
{
return m_header.ToString();
}

// TODO: Add wrappers for every public property
// in ColumnHeader and forward the calls to m_header.
// Also add any new properties/methods you want
}

class MyColumnHeaderCollection : CollectionBase
{
private ListView m_owner;

public MyColumnHeaderCollection(ListView owner)
{
m_owner = owner;
}

public MyColumnHeader this[int index]
{
return (MyColumnHeader) List[index];
}

public int Add(MyColumnHeader header)
{
return List.Add(header);
}

public MyColumnHeader(string text, int width, HorizontalAlignment
align)
{
MyColumnHeader header = new MyColumnHeader();
header.Text = text;
header.Width = width;
header.TextAlign = align;
this.Add(header);
return header;
}

protected override void OnInsertComplete(int index, object value)
{
base.OnInsertComplete(index, value);
ColumnHeader header = ((MyColumnHeader) value).InternalHeader;
m_owner.Columns.Insert(index, header);
}

protected override void OnRemoveComplete(int index, object value)
{
base.OnRemoveComplete(index, value);
ColumnHeader header = ((MyColumnHeader) value).InternalHeader;
m_owner.Columns.Remove(index, header);
}

protected override void OnSetComplete(int index, object oldValue,
object newValue)
{
base.OnSetComplete(index, oldValue, newValue);
ColumnHeader header = ((MyColumnHeader) newValue).InternalHeader;
m_owner.Columns[index] = header;
}

protected override void OnClearComplete()
{
base.OnClearComplete();
m_owner.Columns.Clear();
}

// TODO: Implement the rest of the collection (AddRange,
// Contains, IndexOf, Insert, Remove and CopyTo). You don't
// need to manipulate m_owner from inside them. That is
// handled by the overrides above. Simply implement them
// as you would do in a normal collection
}

I don't think you'll need a type converter for this since MyColumnHeader
only has a default constructor, so that will spare you some trouble.

/claes


news.microsoft.com said:
Claes,

I have to thankyou for all your help. Without it I'd still be puzzled.

Anyway I found that AddRange to use an array of my derived columnheader
did not make a difference to the end result.

All in all, all I wanted to do was add an extra member to the standard
columnheader class for listview.

I did so by:

1. deriving a class from System.Windows.Forms.ColumnHeader. Inside here I
added my new member.
2. derived my own collection class from
System.Windows.Forms.ListView.ColumnHeaderCollection. Inside here, my
constructor called the base class. In addition to this I overrode the []
accessor to derived ColumnHeader.
3. I also overrode (by way of new) my derived ListView's Column member
(to replace it with my derived one).
4. I also had to specify the DesignerSerializationVisibility.Content
attribute for the overriden ListView's Column member.

This saved me from defining the whole class again.

If I am not clear just let me know and I'll post some code for you.

Thanks again,

Dave

Claes Bergefall said:
I don't think you will be able to actually inherit ColumnHeaderCollection
and get it to work. What object are you in that case returning from your
Columns property? If you return the Columns property of the base listview
you will get the wrong type back (might explain your AddRange problem).
An if you return some internal variable of your derived class you'll need
to map things between that and the real columns in the listview.

Could you show some code of what you're doing?

I have done similar things when creating a listview with columns and a
tree structure in the first column (i.e. a TreeListView). I ended up
creating a new ListViewItem and ListViewItemCollection class and
forwarded everything to an internal (non-inherited)listview. It was a
lot of work to get everything to play nicely with the designer.
Fortunately for you the ColumnHeader and ColumnHeaderCollection are a
lot smaller than ListViewItem and ListViewItemCollection, so it should
be easier

/claes

Claes,

Thanks for your reply.

I noticed the code I had posted was incorrect in that the Column's
declaration belongs to ListView, not column header.

But that aside I followed your advice with deriving my own collection
(I did so from ListView.ColumnHeaderCollection) and using the
designerserializationvisibility... attribute and all went well.

I am left with one last problem. When the code is generated by the
designer the AddRange code is incorrect. ie. it says:

this.mylistview.Columns.AddRange(new System.Window.Forms.ColumnHeader[]
{ ....

The problem is the ColumnHeader[]. I was hoping this would be
MyColumnHeader[] {...

Does anyone know why this might be?

Thanks,

Dave

Columns property is not a collection, it's an array
Create a MyColumnHeaderCollection class (inherit CollectionBase) and
you'll probably solve both (1) and (3)
Also make the property read only and add the
DesignerSerializationVisibility(DesignerSerializationVisibility.Content)
attribute to it

And then keep your fingers crossed that you don't need a type
converter :)

/claes

Hi,

I want to extend the functionality of the ListView control supplied
by system.windows.forms.

In order to do so I want to add a property to the listview's Columns
property. To do this I have done the following:

------------
public MyListView : System.Windows.Forms.ListView
{
public MyColumnHeader : System.Windows.Forms.ColumnHeader
{
private int myval = 0;

public int MyVal
{
set { myval = value; }
get { return myval; }
}

private MyColumnHeader[] mycolumnheaders;
public new MyColumnHeader[] Columns
{
set { mycolumnheader = value; }
get { return mycolumnheaders; }
}
}
}
-----------

When MyListView (from the above):
1. The "Columns" property no longer has "(Collection)" in the columns
property.
2. When I add columns by pressing the "..." button to invoke the
"collection editor", all the items seem to add correctly (including
the associate code).
3. When I re-open the "collection editor" by pressing the "..."
button, all the column properties seem to have disappeared.

Can anyone answer (1) and (3) above?

Thanks,

Dave
 
R

rwessels

It's cheeting and a throwback BUT you could just create an object that
has all the properties/members that you would like to send around in
each columnheader, and just put that in the tag property of the
columnheader. It sure beats overriding every single property/method for
a column header, and a columnheadercollection.

EXAMPLE:
class MyOBJ
{
public int myValue;
public MyOBJ(){}
}
.... // inside main code
MyOBJ obj = new MyOBJ();
obj.myValue = 2;
listView1.Columns[0].Tag = obj;
....

Then when referencing it just remember to cast it and that object (obj)
just "tages" along...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top