Reputation: 15208
I'm building off this question and creating a simple ImageButton class that represents a button containing only an image. I implemented (at least I believe I did) the suggestions in this answer, but I'm still getting an exception with this code:
public class ImageButton : Button
{
// Overrides the property
public override Image BackgroundImage
{
get { return base.BackgroundImage; }
set
{
base.BackgroundImage = value;
if (value != null) this.Size = value.Size;
}
}
// Shadows the property (note the -new- keyword)
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new Size Size
{
get
{
return base.Size;
}
set
{
base.Size = value;
}
}
public ImageButton()
{
this.BackgroundImage = base.BackgroundImage;
this.BackgroundImageChanged += new EventHandler(ImageButton_BackgroundImageChanged);
}
void ImageButton_BackgroundImageChanged(object sender, EventArgs e)
{
this.Size = this.BackgroundImage.Size;
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawImage(BackgroundImage, 0, 0); // <-- Error occurs here
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// Do nothing
}
}
When I try to add this control to the designer, I get
The control ImageButton has thrown an unhandled exception in the designer and has been disabled.
Exception: Value cannot be null. Parameter name: image
Stack trace: ImageButton.OnPaint(PaintEventArgs e) in ImageButton.cs:line48
Line 48 is this line:
e.Graphics.DrawImage(BackgroundImage, 0, 0);
I realise that this error is thrown because BackgroundImage
is not set to a value, but I'm unsure how to do so in the code. In the actual application, this class will never be added to the designer, but rather added programmatically. How can I fix this exception?
Upvotes: 2
Views: 15478
Reputation: 47
to remove the background image just write:
this.BackgroundImage = null;
Upvotes: 3
Reputation: 942109
this.BackgroundImage = base.BackgroundImage;
Yes, sure, guaranteed exception. You hope that somebody has set the BackgroundImage property before the constructor runs. That's not possible, the constructor runs before any property on the control can be set.
The next thing that goes wrong is that the Paint event will be raised in the designer as well. Which happens immediately after you drop the control on the form. That's a Kaboom, neither the user nor your code has given the BackgroundImage property a value yet. So just fix the method:
protected override void OnPaint(PaintEventArgs e)
{
if (BackgroundImage != null) e.Graphics.DrawImage(BackgroundImage, 0, 0);
}
Upvotes: 6
Reputation: 42340
There is a lot of redundant code in your implementation: try this revised version...
public class ImageButton : Button
{
public ImageButton()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs pevent)
{
//base.OnPaint(pevent); <-- NO LONGER NEEDED (WE DO NOT WANT THE SYSTEM TO PAINT THE BUTTON);
if (this.BackgroundImage != null)
{
pevent.Graphics.DrawImage(this.BackgroundImage, 0, 0);
}
else
{
//Just fill in black (for example)
pevent.Graphics.FillRectangle(new SolidBrush(Color.Black), this.ClientRectangle);
}
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
base.OnPaintBackground(pevent);
pevent.Graphics.FillRectangle(new SolidBrush(this.BackColor), this.ClientRectangle);
}
public override Image BackgroundImage
{
get
{
return base.BackgroundImage;
}
set
{
base.BackgroundImage = value;
this.Size = base.BackgroundImage.Size;
this.Refresh();
}
}
}
Upvotes: 0
Reputation: 123
I don't know C#, but my guess for this would be to have a transparent 1x1px image stored somewhere and assign that on init. That way, there'd be an image but nothing will be seen.
Or you could try adding some check around line 48, like this:
if(BackgroundImage!=null){
e.Graphics.DrawImage(BackGroundImage,0,0);
}
Maybe add something like this:
else{ //No background image
//Draw some dummy/placeholder
}
Yet another method would be to encapsulate the DrawImage
call within a try{}
block and catch the resulting expression. You should be able to safely use an empty catch{}
block if it's that one.
Upvotes: 2
Reputation: 3485
What if you don't draw when there is no image set, also call base in the beginning of the function.
base.OnPaint(e);
Upvotes: 0