Why is user defined controls so slow?

G

Guest

Hello

Due to the limitations of the Compact Framework, I've made an "image button"
by making a modified PictureBox class and then defining a click event for it
as shown below. This code seems however to execute much slower than when
clicking a standard button. What could be the reason for this?
I've made an example in which I handle the click event of my "image button"
and a standard button exactly the same way (identical code in
imageButton1_Click() and button1_Click() of a standard button), and it seems
that the "image button" is considerably slower. Sometimes the click event for
the "image button" doesn't even fire despite clicking the button. What could
be the reason for this behaviour?


class Form1: : System.Windows.Forms.Form
{

public class Form1()
{
ImageButton imageButton1= new ImageButton;
imageButton1.Image= new Bitmap(...);
imageButton1.Size=...;
imageButton1.Location=...;
imageButton1.Click+=new EventHandler(imageButton1_Click);
this.Controls.Add(imageButton1);
}

private void imageButton1_Click(object sender, EventArgs e)
{
//Handle the click event here
}

}

class ImageButton: PictureBox
{

protected override OnPaint(System.Windows.Forms.PaintEventArgs e){}

protected override void
OnPaintBackground(System.Windows.Forms.PaintEventArgs e ){}

protected override void OnMouseDown ( System.Windows.Forms.MouseEventArgs e
){//Move button }

protected override void OnMouseUp ( System.Windows.Forms.MouseEventArgs e )
{//Move button back to original position}

}
 
G

Guest

A bug or inefficiency in the code that you omitted in your example. I've
done very complex user controls with no performance issues.
 
G

Guest

Well, that is of course always a possibility. Now please consider the MSDN
article "How to Create a Microsoft .NET Compact Framework-based Image Button"
(http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnnetcomp/html/ImageButton.asp), which is the basis of my approach.

I've done the following:
1) Created two 'ImageButtons' with the above mentioned code
2) Made one of the 'ImageButtons'decrease the selected index of a ListBox
listBox1
3) Made the other 'ImageButtons' increase the selected index of listBox1
4) Made two normal buttons do the same for another ListBox listBox2

This causes the described behaviour. The 'ImageButtons' are much slower and
the click events are quite frequently not even fired (or handeled) when
clicking the 'Imagebuttons'. What bug or inefficiency could be the reason for
this?

The source code for this is as follows with one Windows Form Form1 and
"ImageButton.cs" from the MSDN article:


//----------------------Form1.cs--------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Windows.Forms;

namespace ImageButtonExample
{
public partial class Form1 : Form
{

private ImageButton ImageButton_Up;
private ImageButton ImageButton_Down;
private Button NormalButton_Up;
private Button NormalButton_Down;
private ListBox listBox1;
private ListBox listBox2;
private int iNumberOfItems;

public Form1()
{

this.Size= new Size(500,300);

//ImageButton_Up
ImageButton_Up = new ImageButtonClass();
ImageButton_Up.Image = new

Bitmap(ImageButtonExample.Properties.Resources.Up);
ImageButton_Up.Location = new Point(30, 50);
ImageButton_Up.Size = new Size(44, 34);
ImageButton_Up.Click += new EventHandler(ImageButton_Up_Click);
this.Controls.Add(ImageButton_Up);

//ImageButton_Down
ImageButton_Down = new ImageButtonClass();
ImageButton_Down.Image = new

Bitmap(ImageButtonExample..Properties.Resources.Down);
ImageButton_Down.Location = new Point(30, 90);
ImageButton_Down.Size = new Size(44, 34);
ImageButton_Down.Click += new
EventHandler(ImageButton_Down_Click);
this.Controls.Add(ImageButton_Down);

//NormalButton_Up
NormalButton_Up = new Button();
NormalButton_Up.Location = new Point(220, 50);
NormalButton_Up.Text = "Up";
NormalButton_Up.Click += new EventHandler(NormalButton_Up_Click);
this.Controls.Add(NormalButton_Up);

//NormalButton_Down
NormalButton_Down = new Button();
NormalButton_Down.Location = new Point(220, 80);
NormalButton_Down.Text = "Down";
NormalButton_Down.Click += new
EventHandler(NormalButton_Down_Click);
this.Controls.Add(NormalButton_Down);

//listBox1
listBox1 = new ListBox();
listBox1.Size = new Size(100, 100);
listBox1.Location = new Point(100, 50);
listBox1.Items.Add("item1");
listBox1.Items.Add("item2");
listBox1.Items.Add("item3");
listBox1.Items.Add("item4");
listBox1.Items.Add("item5");
listBox1.SelectedIndex = 0;
this.Controls.Add(listBox1);

//listBox2
listBox2 = new ListBox();
listBox2.Size = new Size(100, 100);
listBox2.Location = new Point(300, 50);
listBox2.Items.Add("item1");
listBox2.Items.Add("item2");
listBox2.Items.Add("item3");
listBox2.Items.Add("item4");
listBox2.Items.Add("item5");
listBox2.SelectedIndex = 0;
this.Controls.Add(listBox2);

iNumberOfItems = 5;

}

void ImageButton_Up_Click(object sender, EventArgs e)
{
if (listBox1.SelectedIndex > 0)
{
listBox1.SelectedIndex -= 1;
if (listBox1.SelectedIndex == 0)
{
}
}
}

void ImageButton_Down_Click(object sender, EventArgs e)
{
if (listBox1.SelectedIndex < (iNumberOfItems - 1))
{
listBox1.SelectedIndex += 1;
if (listBox1.SelectedIndex == (iNumberOfItems - 1))
{
}
}
}

private void NormalButton_Up_Click(object sender, EventArgs e)
{
if (listBox2.SelectedIndex > 0)
{
listBox2.SelectedIndex -= 1;
if (listBox2.SelectedIndex == 0)
{
}
}
}

private void NormalButton_Down_Click(object sender, EventArgs e)
{
if (listBox2.SelectedIndex < (iNumberOfItems - 1))
{
listBox2.SelectedIndex += 1;
if (listBox2.SelectedIndex == (iNumberOfItems - 1))
{
}
}
}


}
}



//----------------ImageButton.cs from MSDN article--------------
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;


namespace ImageButtonExample
{

public class ImageButton : Control
{
private Image image;
private bool bPushed;
private Bitmap m_bmpOffscreen;

public Image Image
{
get
{
return image;
}
set
{
image = value;
}
}

public ImageButton()
{
bPushed = false;
//default minimal size
this.Size = new Size(21, 21);
}

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e )
{
Graphics gxOff; //Offscreen graphics
Rectangle imgRect; //image rectangle
Brush backBrush; //brush for filling a backcolor

if (m_bmpOffscreen == null) //Bitmap for doublebuffering
{
m_bmpOffscreen = new Bitmap(ClientSize.Width,

ClientSize.Height);
}

gxOff = Graphics.FromImage(m_bmpOffscreen);

gxOff.Clear(this.BackColor);

if (!bPushed)
backBrush = new SolidBrush(Parent.BackColor);
else //change the background when it's pressed
backBrush = new SolidBrush(Color.LightGray);

gxOff.FillRectangle(backBrush, this.ClientRectangle);

if (image != null)
{
//Center the image relativelly to the control
int imageLeft = (this.Width - image.Width) / 2;
int imageTop = (this.Height - image.Height) / 2;

if (!bPushed)
{
imgRect = new Rectangle(imageLeft, imageTop,

image.Width, image.Height);
}
else //The button was pressed
{
//Shift the image by one pixel
imgRect = new Rectangle(imageLeft + 1 , imageTop

+1, image.Width, image.Height);
}
//Set transparent key
ImageAttributes imageAttr = new ImageAttributes();
imageAttr.SetColorKey(BackgroundImageColor(image),

BackgroundImageColor(image));
//Draw image
gxOff.DrawImage(image, imgRect, 0, 0, image.Width,

image.Height, GraphicsUnit.Pixel, imageAttr);
}

if (bPushed) //The button was pressed
{
//Prepare rectangle
Rectangle rc = this.ClientRectangle;
rc.Width--;
rc.Height--;
//Draw rectangle
gxOff.DrawRectangle(new Pen(Color.Black), rc);
}

//Draw from the memory bitmap
e.Graphics.DrawImage(m_bmpOffscreen, 0, 0);

base.OnPaint(e);
}

protected override void

OnPaintBackground(System.Windows.Forms.PaintEventArgs e )
{
//Do nothing
}

protected override void OnMouseDown (

System.Windows.Forms.MouseEventArgs e )
{
bPushed = true;
this.Invalidate();
}

protected override void OnMouseUp ( System.Windows.Forms.MouseEventArgs

e )
{
bPushed = false;
this.Invalidate();
}

private Color BackgroundImageColor(Image image)
{
Bitmap bmp = new Bitmap(image);
return bmp.GetPixel(0, 0);
}

}
}
 
A

ackesa

Hello,

Here is the code for my "pictureButton". I'm quite satisfied with the
reaction of it and it has nice "pushed-look" if you click on it

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;

namespace MyControls
{
public class PBoxButton:pictureBox
{

private bool bPushed;
private Bitmap m_bmpOffscreen;


public PBoxButton()
{
bPushed = false;
//default minimal size
this.Size = new Size(21, 21);
}

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e
)
{
Graphics gxOff; //Offscreen graphics
Rectangle imgRect; //image rectangle
Brush backBrush; //brush for filling a backcolor

if (m_bmpOffscreen == null) //Bitmap for doublebuffering
{
m_bmpOffscreen = new Bitmap(ClientSize.Width, ClientSize.Height);
}

gxOff = Graphics.FromImage(m_bmpOffscreen);

gxOff.Clear(this.BackColor);

if (!bPushed)
backBrush = new SolidBrush(Parent.BackColor);
else //change the background when it's pressed
backBrush = new SolidBrush(Color.LightGray);

gxOff.FillRectangle(backBrush, this.ClientRectangle);

if ((Image != null) )
{
//Center the image relativelly to the control
int imageLeft = (this.Width - Image.Width) / 2;
int imageTop = (this.Height - Image.Height) / 2;

if (!bPushed)
{
imgRect = new Rectangle(imageLeft, imageTop,
Image.Width, Image.Height);
//Set transparent key
ImageAttributes imageAttr = new ImageAttributes();
gxOff.DrawImage(Image, imgRect, 0, 0, Image.Width,
Image.Height, GraphicsUnit.Pixel, imageAttr);
}
else //The button was pressed
{
//Shift the image by one pixel
imgRect = new Rectangle(imageLeft + 1, imageTop +
1, Image.Width, Image.Height);
ImageAttributes imageAttr = new ImageAttributes();

//imageAttr.SetColorKey(BackgroundImageColor(Image),
BackgroundImageColor(Image));
imageAttr.SetColorKey(Color.White, Color.White);
gxOff.DrawImage(Image, imgRect, 0, 0, Image.Width,
Image.Height, GraphicsUnit.Pixel, imageAttr);

}
//Draw image

}
//else
//{
// base.OnPaint(e
//}

if (bPushed) //The button was pressed
{
//Prepare rectangle
Rectangle rc = this.ClientRectangle;
rc.Width--;
rc.Height--;
//Draw rectangle
gxOff.DrawRectangle(new Pen(Color.Black), rc);
}

//Draw from the memory bitmap
e.Graphics.DrawImage(m_bmpOffscreen, 0, 0);

//base.OnPaint(e);
}

protected override void
OnPaintBackground(System.Windows.Forms.PaintEventArgs e )
{
//Do nothing
}

protected override void OnMouseDown (
System.Windows.Forms.MouseEventArgs e )
{
bPushed = true;
this.Invalidate();
}

protected override void OnMouseUp (
System.Windows.Forms.MouseEventArgs e )
{
bPushed = false;
this.Invalidate();
}

private Color BackgroundImageColor(Image image)
{
Bitmap bmp = new Bitmap(image);
return bmp.GetPixel(0, 0);
}
}
}
 
G

Guest

Hello ackesa

Thanks for replying. Your code is pritty much exactly the same as mine. I've
tested your code with the example I described in my last post, and not
surprisingly it suffers from the same symptoms: It responds much slower than
a normal button to the click events, and quite often it does not respond at
all.
Have you tested my example with your button? Would appreciate if you gave it
a try by clicking the up and down buttons rapidly.

I get the same problems when running the code on Windows XP as well.
 
G

Guest

A colleague of mine has solved the problem, so here it is in case anyone else
struggels with the same problem:
The reason that the user defined control is so slow was that I used the
'click' event to hook on to the control. This approach is also used in the
MSDN article "How to Create a Microsoft .NET Compact Framework-based Image
Button".
Rather use the 'OnMouseDown' or 'OnMouseUp' events to hook on to the
control, and the user defined control will respond the way it should.
 

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