custom control Painting Issue in Win7 - c#

We have a painting issue that appears only in Win7. It works perfectly in Win Xp. When the user clicks on an item or any portion of the custom control (the user control is marked in red rectangle in the picture below), the tree view and all controls in the screen starts flickering. The flickering stops only when the mouse pointer is moved out of the user control.
How can the issue be solved?
The user control code is given below
namespace NeST.ICE.Screens.ICEJobHistory
{
public partial class CustomControlPropertGrid : System.Windows.Forms.PropertyGrid
{
public CustomControlPropertGrid()
{
InitializeComponent();
this.DoubleBuffered = true;
}
protected override void OnPaint(PaintEventArgs pe)
{
// TODO: Add custom paint code here
// Calling the base class OnPaint
base.OnPaintBackground(pe);
base.OnPaint(pe);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
}
}
}
The data filling code is given below. this._waferGrid is an object of CustomControlPropertGrid
if (dataReader != null && dataReader.Read())
{
//FIX_OCT_08: BUG2891
WaferProperty wafer = new WaferProperty();
//FIX_JUN_09:Changed Name to SubstrateID
wafer.SubstrateID = dataReader[Constants.NAME_PARAMETER_INDEX].ToString();
wafer.InCarrier = dataReader[Constants.SOURCE_INCARRIER_PARAMETER_INDEX].ToString();
wafer.OutCarrier = dataReader[Constants.DESTINATION_OUTCARRIER_PARAMETER_INDEX].ToString();
wafer.InLoadPort = dataReader[Constants.DESTINATIONSLOT_INLP_PARAMETER_INDEX].ToString();
wafer.OutLoadPort = dataReader[Constants.STARTTIME_OUTLP_PARAMETER_INDEX].ToString();
wafer.StartTime = dataReader[Constants.WAFER_TIME_PARAMETER_INDEX].ToString();
wafer.EndTime = dataReader[Constants.ENDTIME_OPERATION_PARAMETER_INDEX].ToString();
//<<EHN_AUG_11_WaferLotId
//Added the waferLotId parameter to the grid
wafer.LotID = dataReader[Constants.LOT_ID_PARAMETER_INDEX].ToString();
//EHN_AUG_11_WaferLotId >>
//FIX_JUN_09:Changed WaferID to ActualWaferID
wafer.ActualWaferID = dataReader[Constants.WAFER_STATUS_PARAMETER_INDEX].ToString();
wafer.RouteRecipe = dataReader[Constants.WAFER_ROUTE_RECIPE_PARAMETER_INDEX].ToString();
this._waferGrid.SelectedObject =wafer;
}

Related

Propagate event to all controls

I'm using the following piece of code, for documentation, error handling and/or logging. It's saves an image of the UserControl or Form when I click it pressing Control+Alt+Shift:
public Image GetImage()
{
Bitmap oBmp = new Bitmap(this.Width, this.Height);
this.DrawToBitmap(oBmp, new Rectangle(0, 0, oBmp.Width, oBmp.Height));
return (Image)oBmp;
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
bool bControl = false;
bool bShift = false;
bool bAlt = false;
bControl = (Control.ModifierKeys & Keys.Control) == Keys.Control;
bShift = (Control.ModifierKeys & Keys.Shift) == Keys.Shift;
bAlt = (Control.ModifierKeys & Keys.Alt) == Keys.Alt;
if (bControl && bShift && bAlt)
{
GetImage().Save(this.Name.TimedLocalFileName("png"), ImageFormat.Png);
}
}
Right now, I'm coding it in every UserControl, in the base form and so. It's easy to do because I'm using a code Snippet. But it has obvious setbacks.
The same piece of code in a lot of places (maintainability); and
Works only when I click on the base control and not it's childs (if an UserControl has a Label, this doesn't works.
I've been for a few days analyzing GlobalHooks (mostly here: CodeProject, but my head is not helping me.
Any suggestion will be very much appreciated.
Note: TimedLocalFileName is an extension method that returns a String in format <ControlName>_<Culture>_<YYYYMMDD>_<HHMMSS>.<FileExtension>
Create a base UserControl and name it BaseUserControl and derive all your user control from BaseUserControl then you can put all the logic inside the base user control.
Inside the BaseUserControl, using a recursive method, handle MouseDown event of all child controls and redirect them to OnMouseDown of this, like this post.
Override OnHanldeCrated and call that recursive method to wire up events.
Here is the base control:
using System;
using System.Drawing;
using System.Windows.Forms;
public class BaseUserControl : UserControl
{
void WireMouseEvents(Control container)
{
foreach (Control c in container.Controls)
{
c.MouseDown += (s, e) =>
{
var p = PointToThis((Control)s, e.Location);
OnMouseDown(new MouseEventArgs(e.Button, e.Clicks, p.X, p.Y, e.Delta));
};
WireMouseEvents(c);
};
}
Point PointToThis(Control c, Point p)
{
return PointToClient(c.PointToScreen(p));
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
if (Control.ModifierKeys == (Keys.Control | Keys.Alt | Keys.Shift))
MessageBox.Show("Handled!");
// Your custom logic
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
WireMouseEvents(this);
}
}
Answering issue 1
The same piece of code in a lot of places (maintainability)
It's not always applicable but if you find yourself using this behavior a lot, you could inherit from UserControl to create BitmapExportUserControl and put Image GetImage() and override void OnMouseDown(MouseEventArgs e) in this class instead, and make all your custom controls that need this behavior inherit from BitmapExportUserControl.
Another way could to perform the bitmap export from your Form itself, and have your Form subscribe to all the MouseDown events of all its children Control objects.
Answering issue 2
Works only when I click on the base control and not it's childs
As far as I know, there is no built-in "up" event propagation (or bubbling) in WinForms as there is in WPF for example. A solution could be to expose an event that can be raised by all UserControl in your application when there is a MouseDown event on them. Your code would become:
protected override void OnMouseDown(MouseEventArgs e)
{
GlobalMouseDown.RaiseGlobalMouseDownEvent(this, e);
}
and you would have your main Form subscribe to this GlobalMouseDown.GlobalMouseDownEvent and perform the checks and bitmap export.
This is functionally equivalent to having a public method HandleMouseDown in some GlobalMouseDown class that would be called by all your UserControl MouseDownEventHandlers. The code in each UserControl would become:
protected override void OnMouseDown(MouseEventArgs e)
{
GlobalMouseDown.HandleMouseDown(this, e);
}
and you would do your checks and bitmap export in this method.

How can I remove one drawn object from winform?

I have created a winform and a picturebox, you can draw / place icons on the on the picturebox.
g2.DrawIcon(SystemIcons.Warning, new Rectangle(screenPositionX, screenPositionY, _levelWidth, _levelHeight));
like this
But my problem is I want to be able to remove the warning icons with the press of a button, but I dont know how.
I already tried g2.Clear, but this removes all the icons.
I also tried just drawing over them, but this draws over everything and I can't find the correct background color.
My question is, how do I remove a single drawn object?
You need a field that keeps track if the icons are to be drawn or not. Flip the value as a result of button presses, and when it comes time to draw (paint event) use the value to determine what to draw.
public partial class Form1 : Form
{
bool showIcons = true;
public Form1()
{
InitializeComponent();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
var wt = pictureBox1.ClientSize.Width;
var ht = pictureBox1.ClientSize.Height;
// draw grid
for (int i = 0; i < wt; i+=32)
{
e.Graphics.DrawLine(Pens.Black, i, 0, i, ht);
}
for (int j = 0; j < ht; j+=32)
{
e.Graphics.DrawLine(Pens.Black, 0, j, wt, j);
}
if (showIcons)
{
// draw icons
e.Graphics.DrawIcon(SystemIcons.Warning, 5*32-1, 2*32-1);
}
}
private void drawButton_Click(object sender, EventArgs e)
{
showIcons = true;
pictureBox1.Refresh();
}
private void clearButton_Click(object sender, EventArgs e)
{
showIcons = false;
pictureBox1.Refresh();
}
}
After you have painted something on a paper, you can't really take it back without either painting it over completely with the correct color, or discarding (clearing) everything and repainting only those objects that should remain. The same goes for a PictureBox.
If this icon is the only one that can be visible sometimes, and not visible other times, you could introduce a bool field in your form class. If that bool is true, you draw the icon, otherwise you don't. Then, when the user clicks the button, you can change the value of that field, and refresh the form.
Since you are showing very little code, I don't know any names of classes or methods in your solution, so consider this pseudo code!
// Pseudo code
class MyForm : Form
{
// The field that decides whether to draw the icon
private bool showWarningIcon = true;
// The button click handler
public void OnButtonClick()
{
showWarningIcon = false;
Invalidate();
}
// The paint handler
public override void OnPaint(PaintEventArgs e)
{
// Draw other things, then:
if (showWarningIcon)
{
g2.DrawIcon(SystemIcons.Warning, new Rectangle(screenPositionX, screenPositionY, _levelWidth, _levelHeight));
}
}
}

How do I make custom buttons in Visual Studio?

I'm trying to make a launcher for a game that fixes some of its bugs.
Right now I'm just working on the interface and I want to make custom buttons, not just those generic squares, but I can't figure out how.
Here's some example images.
I just threw those buttons together quickly, but that's what I want.
I want the button to highlight when I mouse over it, without it being inside of the default square buttons.
This can be done with a custom drawn button. This demo from MSDN shows you how to override OnPaint and swap the bitmaps by responding to OnMouseDown and OnMouseUp. To get the image to change on hover instead, just swap the bitmaps during OnEnter and OnLeave.
Here's a cut-down example from the linked page:
public class PictureButton : Control
{
Image staticImage, hoverImage;
bool pressed = false;
// staticImage is the primary default button image
public Image staticImage
{
get {
return this.staticImage;
}
set {
this.staticImage = value;
}
}
// hoverImage is what appears when the mouse enters
public Image hoverImage
{
get {
return this.hoverImage;
}
set {
this.hoverImage = value;
}
}
protected override void OnEnter(EventArgs e)
{
this.pressed = true;
this.Invalidate();
base.OnEnter(e);
}
protected override void OnLeave(EventArgs e)
{
this.pressed = false;
this.Invalidate();
base.OnLeave(e);
}
protected override void OnPaint(PaintEventArgs e)
{
if (this.pressed && this.hoverImage != null)
e.Graphics.DrawImage(this.hoverImage, 0, 0);
else
e.Graphics.DrawImage(this.staticImage, 0, 0);
base.OnPaint(e);
}
}
I used a picture box, and then added in my button picture with a transparent background. Then added a click event, mouse enter, and mouse leave event.

Winforms Control

I have a button which I use all the time as a little pick button next to a combobox. When I click the button I open a larger full list. This side of things work well and I do not have a problem with this..
My problem lies when someone said to me can you change that ugly icon you picked to my nice icon.
I went crap, I have hundreds of these buttons on many forms. So I thought I will create a custom control called PickButton (which is a standard button and heap of default proeprties set) and drop these on the form everywhere instead. In the code of the PickButton custom control I set some properties and the image to the customers nice icon.
So I drop the PickButton from my toolbox onto the form, so far things are looking pretty good and I am feeling a bit clever. Now I think to myself I will change back to my nice icon not the crappy one the customer picked and change the code in the PickButton custom control. But I cannot get rid of that customers icon, because the code when the PickButton run happens before the code in the designer file which has the customers icon.
So my aim was to have a PickButton control and be able to change the icon and other properties in one place and all the properties would be set when an instance of the control is created and displayed on the form.
Was I not so clever and went about achieving the task the wrong way???
This is my PickButton custom control class
public class PickButton : Button
{
public PickButton()
{
InitialiseButton();
}
internal void InitialiseButton()
{
this.ImageAlign = ContentAlignment.MiddleCenter;
this.Image = WindowsFormsApplication1.Properties.Resources.Cancel.ToBitmap();
this.Size = new Size( 28, 28 );
this.Dock = DockStyle.Fill;
this.Margin = new Padding( 0, 2, 2, 0 );
this.Text = string.Empty;
}
}
Now I drop one onto my form and the code in the designer is as follows
//
// pickButton1
//
this.pickButton1.Dock = System.Windows.Forms.DockStyle.Fill;
this.pickButton1.Image = ((System.Drawing.Image)(resources.GetObject("pickButton1.Image")));
this.pickButton1.Location = new System.Drawing.Point(0, 0);
this.pickButton1.Margin = new System.Windows.Forms.Padding(0, 2, 2, 0);
this.pickButton1.Name = "pickButton1";
this.pickButton1.Size = new System.Drawing.Size(284, 262);
this.pickButton1.TabIndex = 0;
this.pickButton1.Text = "pickButton1";
this.pickButton1.UseVisualStyleBackColor = true;
Now I want to change the image so I change my PickButton code to use a different icon
this.Image = WindowsFormsApplication1.Properties.Resources.Browse.ToBitmap();
Run the application andd the first icon is still the one being displayed because of this line of code in the designer file
this.pickButton1.Image = ((System.Drawing.Image)(resources.GetObject("pickButton1.Image")));
The concept of setting all the properties in one place was a good idea, it just wasn't implemented quite right. I would make this class inherit from UserControl instead of from Button. By making it a UserControl, you can use the designer to set all the properties you want, like the default Image for the button. Set that in the designer, then just drag and drop your UserControl from the toolbox onto your forms. If you are only using your "PickButton" control with comboboxes, I would put the combobox on the UserControl as well. If you ever want to change your button image in the future (or any other property for that matter), you will be able to change it in ctlPickButton and that will propogate the changes to all the instances used throughout your project(s).
ctlPickButton:
public partial class ctlPickButton : UserControl
{
public event EventHandler pickButtonClicked;
public ctlPickButton()
{
InitializeComponent();
}
//Allows buttons image to be set in code if necessary
public Image Image
{
get
{
return button1.Image;
}
set
{
if (Image != null)
{
button1.Image = value;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
if (pickButtonClicked != null)
{
pickButtonClicked(sender, e);
}
}
}
Demo Form:
public Form1()
{
InitializeComponent();
ctlPickButton1.pickButtonClicked += new EventHandler(ctlPickButton1_pickButtonClicked);
ctlPickButton2.pickButtonClicked += new EventHandler(ctlPickButton2_pickButtonClicked);
}
void ctlPickButton2_pickButtonClicked(object sender, EventArgs e)
{
if (comboBox2.SelectedItem != null)
{
MessageBox.Show(comboBox2.SelectedItem.ToString());
}
}
void ctlPickButton1_pickButtonClicked(object sender, EventArgs e)
{
if (comboBox1.SelectedItem != null)
{
MessageBox.Show(comboBox1.SelectedItem.ToString());
}
}
private void Form1_Load(object sender, EventArgs e)
{
comboBox1.Items.Add("French");
comboBox1.Items.Add("Spanish");
comboBox1.Items.Add("English");
comboBox1.Items.Add("German");
comboBox2.Items.Add("Pizza");
comboBox2.Items.Add("Hamburger");
comboBox2.Items.Add("Potato");
comboBox2.Items.Add("Chicken");
//Shows how the default image set in the designer can be overwritten for a
//specific instance using the "Image" property
ctlPickButton2.Image = Testbed.Properties.Resources.searchIcon2;
}
}
Image of ctlPickButton in designer
I think I've found a simple, clean solution:
In the CustomButton class (which inherits from System.Windows.Forms.Button), override the Refresh() method, and set the image of the button to the one you want to see:
public class CustomButton : Button
{
public override void Refresh()
{
Image = MyResources.HappyFace;
}
}
In the form that will hold an instance of your CustomButton, simply call customButton.Refresh() in the constructor, after InitializeComponent():
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
customButton.Refresh();
}
}
I've put a demo application up on Github.

How can I get an image of control to another control?

Look this class:
public class ControlContainer : Control
{
public ControlContainer() { DoubleBuffered = true; SetStyle(ControlStyles.SupportsTransparentBackColor, true); }
Control _Control;
public Control Control { get { return _Control; } set { _Control = value; SetHandlers(); } }
public void SetHandlers()
{
if (_Control == null) return;
Region = _Control.Region;
Size = _Control.Size;
_Control.Invalidated += new InvalidateEventHandler(_Control_Invalidated);
}
void _Control_Invalidated(object sender, InvalidateEventArgs e)
{
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
if (_Control == null) return;
Bitmap p = new Bitmap(_Control.Width, _Control.Height);
_Control.DrawToBitmap(p, new Rectangle(0, 0, _Control.Width, _Control.Height));
e.Graphics.DrawImage((Image)p, 0, 0);
base.OnPaint(e);
}
}
As you see the mission of this control is to get an image of another control and draws it.
But if we handled a TextBox control named 'textBox1' as the following:
Suppose we have added a new instance of the type ControlContainer above and its name is controlContainer1 and we assign the value of the property 'Control' of controlContainer1 to textBox1.
Why if I write something in textBox1 the event 'Invalidated' doesn't fire? and why the pointer of the writing "|" doesn't appear by the method 'DrawToBitmap"?
Short answer: Because the TextBox control doesn't support the event Invalidated. Perhaps it will help if you call Invalidate() on event TextChanged of the textbox.
Longs answer: It's beacuse how windows is internal handling a Win32 native textbox. If you start editing, a control without borders, the same color at the same location as the textbox will be created. Now all events go to this Win32 internal editbox. You can observe this behaviour if you override the OnPaint for a textbox and fill the whole textbox with FillRectangle. As soon as the textbox is edited, you see the new editbox in the (original) color of the proprty BackColor.

Categories

Resources