GDI+ on Windows Forms

R

R K

Hi,
I'm new to WinForms and GDI+ and decided to use .NET and c# to complete an
assignment which all of my other classmates are doing in Java using AWT.
Being relatively new to WinForms and GDI+ I need help. I hate to be stuck
and submit an incomplete assignment being the only person doing it in .NET.

I am creating a report which reads an XML file containing the periodic table
and displays each element in a row as well as draws the circle whose radius
based on the atomic radius of the element. The circle is filled with blue
if element is solid, white if element is liquid and transparent if element
is gas (given room temperature).

Now in the Form() constructor I read the XML file, set the size of the form
based on the number of elements in the periodic table and and using GDI+
draw on the form itself using this.CreateGraphics() object and DrawString
() The problem is the form is much greater than the screen and I do not
handle the OnPaint, Move, Size events which makes the program very
complicated. Is there any easy way for me to use GDI+ on the form and have
WinForms handle all the events and display my form properly? My XML file
processing is done in the form constructor itself and all I need to do is
display the results.

I tried using the ListView control but it does not allow me to use GDI+ to
draw in the SubItems.

I'd appreciate your help, recommendations, pointers, suggestions on any easy
ways of doing this.

Thanks
 
M

Morten Wennevik

Hi R.K.

If you only need to draw the items as a list, you can use a ListBox.
Set the ListBox's DrawMode to OwnerDrawFixed or OwnerDrawVariable (based on wether or not the height of the items change, usually fixed is what you would pick). Create an event handler for the DrawItem event.
In that event, determine which element is currently to be drawn (the event will fire for each element, so you only need to draw one at a time). The position to draw is stored in the Bounds property of the event. Use DrawString to write the element name, and any other graphics method as you need. Btw. you can use a ComboBox the exact same way, and the currently selected item will be drawn the same way in the edit box (looks very professional :p)

// This sample will fill 4 lines with various objects
private void listBox1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
// If you want to select items, you need to indicate the selected item by drawing the background
// SystemBrushes.HighLight is the selection color, and changes with window settings.
if(e.Index == listBox1.SelectedIndex)
e.Graphics.FillRectangle(SystemBrushes.Highlight, e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height);

// I used a switch to determine what to draw, but you can use anything you want.
// The listbox needs to be filled with items, even though they won't be shown,
// or you won't get an event to draw the items.
switch(e.Index)
{
case 0:
e.Graphics.FillRectangle(Brushes.BlueViolet, e.Bounds.X, e.Bounds.Y, 10, 10);
break;
case 1:
e.Graphics.DrawPie(Pens.HotPink, e.Bounds.X, e.Bounds.Y, 10, 10, 0, 90);
break;
case 2:
e.Graphics.DrawString("Hello World", this.Font, Brushes.Black, e.Bounds.X, e.Bounds.Y);
break;
case 3:
e.Graphics.DrawLine(Pens.OldLace, e.Bounds.X, e.Bounds.Y + 16, e.Bounds.X + 50, e.Bounds.Y);
break;
}
}

// Causes everything to be redrawn, needed to clear previously selected items.
// There are probably a more efficient ways, but this is the easiest.
private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
listBox1.Invalidate();
}
 
M

Morten Wennevik

Oh, and to be able to show drawn selection in the ComboBox's textbox you need to set DropDownStyle to DropDownList.
The selection code for combobox also doesn't work properly. There is an elegant way set background selection when the mouse moves over the items, but I forgot how I did it :(
 
R

R K

Hi Morten,
Thanks for the suggestions. So I'm essentially printing a report for
viewing based on processing an XML file. There is no interaction with the
elements in the report once it has been drawn, so I don't need to handle any
selected item. Also if I invalidate the listbox, wouldn't I need to
reprocess the XML file again to re-draw everything . Need some
clarifications here. Also since the elements in the listbox is around 120
which does not fit the screen, wouldn't I need to handle the scroll bar
events ..etc which I'm trying to avoid writing. Or am I stuck with
handling the resize, scrolbar, move events and redrawing each time? That's
really a pain for doing a static report.

Thanks,
R.K.
 
M

Morten Wennevik

Hi Morten,
Thanks for the suggestions. So I'm essentially printing a report for
viewing based on processing an XML file. There is no interaction with the
elements in the report once it has been drawn, so I don't need to handle any
selected item.

If you remove the selectioncolor and indexchanged code lines you don't need to disable it either. If you click on an item, nothing will appear to happen, even if selectionindex is changed.
Also if I invalidate the listbox, wouldn't I need to
reprocess the XML file again to re-draw everything. Need some
clarifications here.

No, you won't need to reprocess the xml file. But you will need to store information in a way that you can call a method or an array and get necessary information for the drawing code of a single element. You could create an element struct holding the element name, fullname, radius and solid state.

struct Element
{
public string Name;
public string FullName;
public int Radius;
public ElementType Type;
}

enum ElementType
{
Gas,
Liquid,
Solid
}

When processing the XML file, create an Element for each element. Set the appropriate data for the Element and put it in an Element array. At the same time, put an entry in the listbox so that the index will be the same for the listbox as in the array. Then, when DrawItem is called, you can use

Element currentElement = Elements[e.Index];

assuming Elements is the Element array.
Also since the elements in the listbox is around 120
which does not fit the screen, wouldn't I need to handle the scroll bar
events ..etc which I'm trying to avoid writing. Or am I stuck with
handling the resize, scrolbar, move events and redrawing each time? That's
really a pain for doing a static report.

No, that is the great thing about using DrawItem. All you need to worry about is how to draw a single Element. The DrawItem event will tell which item to draw and where to draw it. Scrolling is handled by the ListBox.
Thanks,
R.K.

The DrawItem code would be something like

private void listBox1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e)
{
Element el = Elements[e.Index];
int x = e.Bounds.X;
int y = e.Bounds.Y;
int h = e.Bounds.Height;
e.Graphics.DrawString(el.Name + " " + el.FullName, listBox1.Font, Brushes.Black, x, y);

switch(el.Type)
{
case ElementType.Solid:
e.Graphics.FillEllipse(Brushes.Blue, x + 60, y + h/2, el.Radius*2, el.Radius*2);
break;
case ElementType.Liquid:
e.Graphics.FillEllipse(Brushes.White, x + 60, y + h/2, el.Radius*2, el.Radius*2);
break;
}

e.Graphics.DrawEllipse(Pens.Black, x + 60, y + h/2, el.Radius*2, el.Radius*2);

}

You probably would want to change the ListBox's background color to something other than white or liquid and gas would look the same (gas is just a circle).
 
M

Morten Wennevik

Probably better to set SelectionMode to None, I got some drawing artifacts when you could click on an item.
 
M

Morten Wennevik

Oh yeah, and DrawEllipse uses top and left position of a bounding box as the two first integers, not center of radius like I have a tendency to believe. So you would want to adjust the y position accordingly.
 
R

R K

Hi Morten,
Thanks for the clarifications and for puting me on the right track. I
should be able to do the assignment now since paved my path.

Thanks again,
Raphael

Morten Wennevik said:
Hi Morten,
Thanks for the suggestions. So I'm essentially printing a report for
viewing based on processing an XML file. There is no interaction with
the
elements in the report once it has been drawn, so I don't need to handle
any
selected item.

If you remove the selectioncolor and indexchanged code lines you don't
need to disable it either. If you click on an item, nothing will appear
to happen, even if selectionindex is changed.
Also if I invalidate the listbox, wouldn't I need to
reprocess the XML file again to re-draw everything. Need some
clarifications here.

No, you won't need to reprocess the xml file. But you will need to store
information in a way that you can call a method or an array and get
necessary information for the drawing code of a single element. You could
create an element struct holding the element name, fullname, radius and
solid state.

struct Element
{
public string Name;
public string FullName;
public int Radius;
public ElementType Type;
}

enum ElementType
{
Gas,
Liquid,
Solid
}

When processing the XML file, create an Element for each element. Set the
appropriate data for the Element and put it in an Element array. At the
same time, put an entry in the listbox so that the index will be the same
for the listbox as in the array. Then, when DrawItem is called, you can
use

Element currentElement = Elements[e.Index];

assuming Elements is the Element array.
Also since the elements in the listbox is around 120
which does not fit the screen, wouldn't I need to handle the scroll bar
events ..etc which I'm trying to avoid writing. Or am I stuck with
handling the resize, scrolbar, move events and redrawing each time?
That's
really a pain for doing a static report.

No, that is the great thing about using DrawItem. All you need to worry
about is how to draw a single Element. The DrawItem event will tell which
item to draw and where to draw it. Scrolling is handled by the ListBox.
Thanks,
R.K.

The DrawItem code would be something like

private void listBox1_DrawItem(object sender,
System.Windows.Forms.DrawItemEventArgs e)
{
Element el = Elements[e.Index];
int x = e.Bounds.X;
int y = e.Bounds.Y;
int h = e.Bounds.Height;
e.Graphics.DrawString(el.Name + " " + el.FullName, listBox1.Font,
Brushes.Black, x, y);

switch(el.Type)
{
case ElementType.Solid:
e.Graphics.FillEllipse(Brushes.Blue, x + 60, y + h/2, el.Radius*2,
el.Radius*2);
break;
case ElementType.Liquid:
e.Graphics.FillEllipse(Brushes.White, x + 60, y + h/2, el.Radius*2,
el.Radius*2); break;
}

e.Graphics.DrawEllipse(Pens.Black, x + 60, y + h/2, el.Radius*2,
el.Radius*2);
}

You probably would want to change the ListBox's background color to
something other than white or liquid and gas would look the same (gas is
just a circle).
 
R

R K

Hi Morton,
I finished my assignment yesterday and all I can say is "sweet ginger
brown". Just the prior day I has so many questions but once I got started
on the assignment it took me around 3 hours to complete. What a difference
a day makes. Your tips were invaluable. Moreover my classmates were
still struggling with Java AWT and Swing while I was able to embillish my
app with more graphics and make it jazzy.

All I need to do is handle the DrawItem event of the Listbox and viola .NET
takes care of the rest.

thanks again Morton, you are a true Samaritan.

R. K.
 

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