Groups | Blog | Home
all groups > c# > march 2005 >

c# : ItemCollections and custom UserControl...


Tinus
3/1/2005 11:44:22 PM
Hello all,
[I've been programming in C# for several months now, so bare with me... I'm
still a newbie]

I've create a custom control (UserControl) and have a custom Item
Collection. The control is a custom calendar which is draw using the
Graphics Rectangle etc. functions. It is drawn when the control is painted
or resized. When the control is drawn it draws also the items found in the
collection.

So far so good.... I have 3 questions which I'm unable to find a solution
for yet (googled for several days now :-( ). Hopefull someone of you can
help me?

1. With a ListView control you can remove items using the
listView1.Items[1].Remove() or listView1.Remove(item). How does one
implement the first option (the second one I know how)?

2. The UserControl is drawn when the control is painted or resized (in the
event I call the function that draws the control). Every time I add a item
to the collection I have to call the control.Refresh() function to update my
control with the added item. How can I make it so that whenever I add an
item to my collection the control is (re)drawn automatically?

3. The drawn control flickers when I do a Refresh() or when I redraw the
control. I've already tried to remove the Graphics.Clear(Color) function but
that did not help :-( How can I reduce the flickering?

Below I placed the item collection code (simplified). Hopefully someone in
this newsgroup can tell me what to do with the first 2 questions.... they
are really bugging me.

Thanks in advance!
Regards,
Tinus
(The Netherlands)
----
public class Items : System.Collections.CollectionBase
{
public virtual Items Add(Item item)
{
if (this.List.Contains(item))
throw new InvalidOperationException();

this.List.Add(item);

return this;
}

public virtual Items Add(string name, int number)
{
return this.Add(new Data(name, number));
}

public virtual Item this[int index]
{
get
{
if (index < 0 || index > this.List.Count)
throw new ArgumentOutOfRangeException();
return (Item)this.List[index];
}
}
}

public class Item
{
private string name;
private int number;

public string Name
{
get
{
return this.name;
}
}

public int Number
{
get
{
return this.number;
}
set
{
if (value < 0)
throw new ArgumentOutOfRangeException();
this.number = value;
}
}

public Item(string name, int number)
{
this.name = name;
this.number = number;
}
}

Ricky Lee
3/2/2005 2:42:34 AM
Hi Tinus,

First of all, if you're creating an owner-drawn custom calender (drawing
using the graphics rectangle etc), it's better to derive your control from
the Control class instead of UserControl. UserControl is best suited if you
want to combine several basic/constituent controls into a single logical
unit.

As to your questions:
1. The 'Items[1].Remove()' expression is basically calling the Remove()
method of the Item object returned by the indexer. So you have to implement
a Remove() method in your Item class. You can just call the Remove(item)
method of the Items collection from the Remove() method of the Item class.
It's not hard to implement this. I'll leave the detail for your exercise.
Hint: you may need to define a private field in the Items collection and
Item class to point to the their owner (i.e. an instance of your control).

2. After adding the item to the collection, just call the Invalidate()
method of your control.

3. There are several techniques in reducing flicker. One of them is a
technique called 'Double Buffering'. You can just do some googling to learn
more about double buffering. One site that you may find useful is
http://www.codeproject.com/cs/media/flickerFreeDrawing.asp.

Hope it helps. Good luck.

-- Ricky Lee
============================================
^o^ "When all doors are closed, God will open a Windows" ^o^
============================================

[quoted text, click to view]

Tinus
3/2/2005 6:31:17 AM
Thanks Ricky!

Question 1 is now solved.... and it was so easy! But I really needed the
hint ;-)

As for question 2; How can I Invalidate() my control from either the Item
class of Items class? If I just make an instance to my control and then
Invalidate this instance then the results are: nothing happens. This is
because the instance does not have any items in it I think.

So can you once again give me one of your helpfull hints?

Also thanks for the flickerfree link... I'm going to look at it today.

Tinus

[quoted text, click to view]

Tinus
3/2/2005 9:05:04 AM
Ricky,

This 'flickerfree' link was awesome! No more flickering. Now the custom
control looks and feels really professional.

Thanks a lot!
Tinus

[quoted text, click to view]

Ricky Lee
3/2/2005 2:22:23 PM
Tinus,
Since you've solved question 1, I assume you now have a field in your Items
collection that points to the instance of your control owning this
collection. In the Items.Add method of your collection, call the
Invalidate() method of the owning control. For example:

public class Items : System.Collections.CollectionBase
{
// define field to point to owner control
// you can initialize it in the constructor
// or provide property accessor
private MyControl owner;

public virtual Items Add(Item item)
{
if (this.List.Contains(item))
throw new InvalidOperationException();
this.List.Add(item);
// invalidate the owning control
// to redraw with the new collection
if (owner != null)
owner.Invalidate();

return this;
}

Note: you have to provide a proper Paint logic for your control, to draw all
members of the Items collection.

Hope it helps.

-- Ricky Lee
==================================================
^o^ "When all doors are closed, God will open a Windows" ^o^
==================================================
[quoted text, click to view]
Tinus
3/2/2005 8:49:32 PM
Ehh, no....

I changed the public Item(name, number) function in the Item class to:

private Items _item;
public Item(Items items, string name, int number)
{
this._items = items;
...
}

and the public Items Add(string name, int number) to:

public Items Add(string name, int number)
{
this.Add(new Item(this, name, number));
return this;
}

The above code gives me the Items collection in the Item class. So now I
could add a Remove() function in the Item class:

public void Remove()
{
_items.Remove(this);
}

I think you had a different solution in mind? Can you give me an example of
how to do it your way?
How can I get an instance of my control in the Item class?

Many thanks!
Tinus

[quoted text, click to view]
Ricky Lee
3/2/2005 10:05:01 PM
Again I'll leave this one for your exercise. Hint: instead of using a
private field pointing to the Items collection, try using one pointing to
the owning control. Study the members of ListView, ListViewItemCollection,
and ListViewItem for ideas.

-- Ricky Lee
==================================================
^o^ "When all doors are closed, God will open a Windows" ^o^
==================================================
[quoted text, click to view]
Tinus
3/2/2005 10:29:22 PM
Got it!

In my Items class I had to add:

private MyControl owner;
internal Datas(MyControl control)
{
this.owner = control;
}

And had to change Items items = new Items() in my custom control to:
Items items = new Items(this)

:-) Now it works.

Ricky, thanks for your time and hints!

Tinus

[quoted text, click to view]
Ricky Lee
3/2/2005 11:12:15 PM
Glad to help ;)


-- Ricky Lee
==================================================
^o^ "When all doors are closed, God will open a Windows" ^o^
==================================================
[quoted text, click to view]
AddThis Social Bookmark Button