Instead of setting image using 'background' property, I would like to draw the image using Graphics class on a panel. How can I do it in C#.Net?
you can try following piece of code.
public class ImagePanel:Panel
{
private Image image;
public Image Image
{
get { return image; }
set
{
image = value;
Refresh();
}
}
protected override void OnPaint(PaintEventArgs e)
{
if(Image!=null)
{
e.Graphics.DrawImage(this.Image,Point.Empty);
}
base.OnPaint(e);
}
}
Make use of System.Drawing.Graphics class to draw the things.
Details : http://msdn.microsoft.com/en-us/library/system.drawing.graphics.aspx related to drawing
example : http://www.techotopia.com/index.php/Drawing_Graphics_in_C_Sharp
Related
So I'm coding a project that is Plants vs. Zombies made of pure c# using no game engines and here I have a graphic problem.
I need to render a transparent picturebox over another transparent picturebox and I had to define a new control that is really transparent and everything goes fine with the transparency aspect but here is a problem:
Flicker :|
I have so much of them because of the RecreateHandle(); method I use when I change the image of my control to make animations and when it moves to have the real transparency.
Here is my code I wonder if any one could help !
public class TransparentControl : Control
{
private Image _image;
public TransparentControl()
{
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.SupportsTransparentBackColor, true);
base.BackColor = Color.FromArgb(0, 0, 0, 0);
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnMove(EventArgs e)
{
//RecreateHandle();
}
protected override void OnPaint(PaintEventArgs e)
{
if (_image != null)
{
e.Graphics.DrawImage(_image, (Width / 2) - (_image.Width / 2), (Height / 2) - (_image.Height / 2));
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
//Do not paint background
}
//Hack
public void Redraw()
{
//RecreateHandle();
}
public Image Image
{
get
{
return _image;
}
set
{
_image = value;
//RecreateHandle();
}
}
}
One way to reduce flickering is to create two different bitmaps. One to draw to, and one to display.
Image BackBuffer;
Image BrontBuffer;
private void RotateImages()
{
lock (this.BackBuffer)
{
var temp = this.BackBuffer;
this.BackBuffer = this.FrontBuffer;
this.FrontBuffer = temp;
}
}
Draw everything into your BackBuffer, then show the FrontBuffer.
Note that you should declare the FrontBuffer with the same width/height values as the BackBuffer, in pretty much exactly the same spot that you declare the BackBuffer.
Use the method RotateImages() immediately after (before should work, too) you display the front buffer.
I have a ToolStripSplitButton with various elements in dropdown list.
One of them is a Trackbar enclosed in a ToolStripControlHost, called ToolStripTrackbarItem. It's code (I've got it from stackoverflow):
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace Application
{
[System.ComponentModel.DesignerCategory("code")]
[System.Windows.Forms.Design.ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ContextMenuStrip | ToolStripItemDesignerAvailability.MenuStrip)]
public class ToolStripTrackbarItem : ToolStripControlHost
{
public ToolStripTrackbarItem()
: base(CreateControlInstance())
{
this.Size = Control.Size;
}
public TrackBar TrackBar
{
get { return Control as TrackBar; }
}
private static Control CreateControlInstance()
{
TrackBar t = new TrackBar();
t.AutoSize = false;
return t;
}
[DefaultValue(0)]
public int Value
{
get { return TrackBar.Value; }
set { TrackBar.Value = value; }
}
protected override void OnSubscribeControlEvents(Control control)
{
base.OnSubscribeControlEvents(control);
TrackBar trackBar = control as TrackBar;
trackBar.ValueChanged += new EventHandler(trackBar_ValueChanged);
}
protected override void OnUnsubscribeControlEvents(Control control)
{
base.OnUnsubscribeControlEvents(control);
TrackBar trackBar = control as TrackBar;
trackBar.ValueChanged -= new EventHandler(trackBar_ValueChanged);
}
void trackBar_ValueChanged(object sender, EventArgs e)
{
if (this.ValueChanged != null)
ValueChanged(sender, e);
}
public event EventHandler ValueChanged;
protected override Size DefaultSize
{
get { return new Size(300, 16); }
}
}
It works, but I need to show images to the left of the dropdown items:
I'm successful with a simple ToolStripMenuItem by setting the Image property. However, it is ineffective to set Image property of my ToolStripTrackbarItem (that is inherited from ToolStripControlHost, see code above). According to MSDN, Image property is irrelevant to ToolStripControlHost.
What does it mean? Is it not even possible to include an image left to ToolStripControlHost?
If it is possible anyway, how to do that?
You should solve 2 problems here:
ToolStripControlHost doesn't show Image property and also doesn't serialize the image when you save the form.
ToolStripProfessionalRendered doesn't draw image for ToolStripControlHost.
You need to override Image property of ToolStripControlHost and make it browsable and serializable. Also you need to create a custom renderer to draw the image in the correct location and size. Then if you simply set the renderer for ToolStrip using below code, you will get expected result:
this.toolStrip1.Renderer = new MyCustomRenderer();
ToolStripTrackBar
The item enables Image property to show in property grid and let it serialize when saving form.
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.Design;
[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.All)]
public class ToolStripTrackBar : ToolStripControlHost
{
public TrackBar TrackBar { get { return (TrackBar)Control; } }
public ToolStripTrackBar() : base(CreateControl()) { }
private static TrackBar CreateControl()
{
var t = new TrackBar()
{ TickStyle = TickStyle.None, AutoSize = false, Height = 28 };
return t;
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override Image Image
{
get { return base.Image; }
set { base.Image = value; }
}
/*Expose properties and events which you need.*/
public int Value
{
get { return TrackBar.Value; }
set { TrackBar.Value = value; }
}
}
MyCustomRenderer
This renderer draws images for ToolStripTrackBar.
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
public class MyCustomRenderer : ToolStripProfessionalRenderer
{
protected override void OnRenderImageMargin(ToolStripRenderEventArgs e)
{
base.OnRenderImageMargin(e);
e.ToolStrip.Items.OfType<ToolStripTrackBar>()
.ToList().ForEach(item =>
{
if (item.Image != null)
{
var size = item.GetCurrentParent().ImageScalingSize;
var location = item.Bounds.Location;
location = new Point(5, location.Y + 1);
var imageRectangle = new Rectangle(location, size);
e.Graphics.DrawImage(item.Image, imageRectangle,
new Rectangle(Point.Empty, item.Image.Size),
GraphicsUnit.Pixel);
}
});
}
}
I set a PropertyGrid's SelectedObject to an object with a Bitmap property:
public class Obj
{
private Bitmap myimage;
[Category("Main Category")]
[Description("Your favorite image")]
public Bitmap MyImage
{
get { return myimage; }
set
{
myimage = value;
}
}
}
This automatically shows the property in the PropertyGrid with a little button that the user can click on to select the image. How can I get the file path of this image?
(Also, is there any way I can override the dialog box that pops up and put my own one in? I know this is a separate question so I'll probably post it separately.)
How can I get the file path of this image?
Also, is there any way I can override the dialog box that pops up and put my own one in?
To achieve the both one way would be to create a new UITypeEditor:
public class BitmapLocationEditor : UITypeEditor
{
}
I overrided the GetEditStyle, EditValue, GetPaintvalueSupported and PaintValue methods.
// Displays an ellipsis (...) button to start a modal dialog box
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
// Edits the value of the specified object using the editor style indicated by the GetEditStyle method.
public override object EditValue(ITypeDescriptorContext context,
IServiceProvider provider,
object value)
{
// Show the dialog we use to open the file.
// You could use a custom one at this point to provide the file path and the image.
using (OpenFileDialog openFileDialog = new OpenFileDialog())
{
openFileDialog.Filter = "Image files (*.bmp | *.bmp;";
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
Bitmap bitmap = new Bitmap(openFileDialog.FileName);
bitmap.SetPath(openFileDialog.FileName);
return bitmap;
}
}
return value;
}
// Indicates whether the specified context supports painting
// a representation of an object's value within the specified context.
public override bool GetPaintValueSupported(ITypeDescriptorContext context)
{
return true;
}
// Paints a representation of the value of an object using the specified PaintValueEventArgs.
public override void PaintValue(PaintValueEventArgs e)
{
if (e.Value != null)
{
// Get image
Bitmap bmp = (Bitmap)e.Value;
// This rectangle indicates the area of the Properties window to draw a representation of the value within.
Rectangle destRect = e.Bounds;
// Optional to set the default transparent color transparent for this image.
bmp.MakeTransparent();
// Draw image
e.Graphics.DrawImage(bmp, destRect);
}
}
Your Obj class should remain the same, except add an attribute to take advantage of our new BitmapLocationEditor class:
public class Obj
{
private Bitmap myImage;
[Category("Main Category")]
[Description("Your favorite image")]
[Editor(typeof(BitmapLocationEditor), typeof(UITypeEditor))]
public Bitmap MyImage
{
get { return myImage; }
set
{
myImage = value;
}
}
}
The SetPath method used in EditValue method is just an extension method to set the Bitmap.Tag property. You can use later the GetPath method to retrieve the actual path of your image file.
public static class BitmapExtension
{
public static void SetPath(this Bitmap bitmap, string path)
{
bitmap.Tag = path;
}
public static string GetPath(this Bitmap bitmap)
{
return bitmap.Tag as string;
}
}
Article about PropertyGrid
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?
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);
}
to remove the background image just write:
this.BackgroundImage = null;
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.
What if you don't draw when there is no image set, also call base in the beginning of the function.
base.OnPaint(e);
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();
}
}
}
Morning all,
I've created a custom control with an image property. That image property is a get/set to a private Image variable.
Can anyone tell me how I enable that get/set to clear the property from the designer?
I.e. if I add an image to a standard PictureBox, I can hit Del to clear the image from the PictureBox. How can I replicate this behaviour on my own custom control?
At the simplest level, DefaultValueAttribute should do the job:
private Bitmap bmp;
[DefaultValue(null)]
public Bitmap Bar {
get { return bmp; }
set { bmp = value; }
}
For more complex scenarios, you might want to try adding a Reset method; for example:
using System;
using System.Drawing;
using System.Windows.Forms;
class Foo {
private Bitmap bmp;
public Bitmap Bar {
get { return bmp; }
set { bmp = value; }
}
private void ResetBar() { bmp = null; }
private bool ShouldSerializeBar() { return bmp != null; }
}
static class Program {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Form form = new Form();
PropertyGrid grid = new PropertyGrid();
grid.Dock = DockStyle.Fill;
grid.SelectedObject = new Foo();
form.Controls.Add(grid);
Application.Run(form);
}
}