Custom combobox control border flickers when mouse hovers in and out - c#

I am developing a custom combobox control dervied from the standard combobox that will look flat and borderless based on the article here:
http://www.codeproject.com/Articles/6971/Making-Standard-ComboBox-appear-flat
According to various sources I will have to override the windows procedure to do that so here is my WndProc override where I have blocked several messages from reaching the control:
protected override void WndProc(ref Message m)
{
IntPtr hDC = IntPtr.Zero;
Graphics gdc = null;
switch (m.Msg)
{
// Block these messages
case WM_CHANGEUISTATE:
case WM_MOUSEACTIVATE:
case WM_MOUSEFIRST:
case WM_MOUSELAST:
case WM_MOUSEHOVER:
case WM_MOUSELEAVE:
m.Result = (IntPtr)1;
break;
// Here we paint the border when non-client paint is received
case WM_NC_PAINT:
hDC = GetWindowDC(this.Handle);
gdc = Graphics.FromHdc(hDC);
SendMessage(this.Handle, WM_ERASEBKGND, hDC, 0);
SendPrintClientMsg(); // Send to draw client area
PaintFlatControlBorder(this, gdc);
m.Result = (IntPtr) 1; // Indicate message has been processed
ReleaseDC(m.HWnd, hDC);
gdc.Dispose();
break;
case WM_PAINT:
base.WndProc(ref m);
// Flatten the border area again
hDC = GetWindowDC(this.Handle);
gdc = Graphics.FromHdc(hDC);
Pen p = new Pen((this.Enabled? BackColor:SystemColors.Control), 2);
gdc.DrawRectangle(p, new Rectangle(2, 2, this.Width-3, this.Height-3));
PaintFlatDropDown(this, gdc);
PaintFlatControlBorder(this, gdc);
ReleaseDC(m.HWnd, hDC);
gdc.Dispose();
break;
default:
base.WndProc(ref m);
break;
}
}
Here is my constructor:
public FlatComboBox() : base()
{
this.FlatStyle = FlatStyle.Flat;
this.SetStyle(ControlStyles.DoubleBuffer | ControlStyles.ResizeRedraw, true);
}
What other wndproc messages do I have to block or is there another way around this?
Here is a good reference of all wndproc messages:
https://www.autoitscript.com/autoit3/docs/appendix/WinMsgCodes.htm
I am using .NET v4.5.
Thanks in advance.

I have done this before with a combo box to give it a customisable flat appearance, I am showing the full code so that is easier to get the idea of how it works, however it uses a custom drop down menu and menu items to complete the appearance.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace Custom.Controls
{
public class CustomComboBox : UserControl
{
private const int DEFAULT_WIDTH = 121;
private const int DEFAULT_HEIGHT = 20;
private CustomContextMenuStrip _popupControl;
private CustomToolStripMenuItem _selectedItem;
private bool _bDroppedDown;
private Color _borderColour;
private Color _dropDownButtonColour;
private Color _droppedDownArrowColour;
private Color _closedArrowColour;
private Color _dropDownBackColour;
private Color _dropDownForeColour;
private Color _dropDownBorderColour;
public Color BorderColour
{
get
{
return _borderColour;
}
set
{
_borderColour = value;
this.Invalidate();
}
}
public Color ClosedArrowColour
{
get
{
return _closedArrowColour;
}
set
{
_closedArrowColour = value;
this.Invalidate();
}
}
public Color DroppedDownArrowColour
{
get
{
return _droppedDownArrowColour;
}
set
{
_droppedDownArrowColour = value;
this.Invalidate();
}
}
public Color DropDownButtonColour
{
get
{
return _dropDownButtonColour;
}
set
{
_dropDownButtonColour = value;
this.Invalidate();
}
}
public Color DropDownBorderColour
{
get
{
return _dropDownBorderColour;
}
set
{
_dropDownBorderColour = value;
_popupControl.BorderColour = value;
this.Invalidate();
}
}
public Color DropDownBackColour
{
get
{
return _dropDownBackColour;
}
set
{
_dropDownBackColour = value;
_popupControl.BackColor = value;
this.Invalidate();
}
}
public Color DropDownForeColour
{
get
{
return _dropDownForeColour;
}
set
{
_dropDownForeColour = value;
_popupControl.ForeColor = value;
this.Invalidate();
}
}
public bool DropShadowEnabled
{
get
{
return _popupControl.DropShadowEnabled;
}
set
{
_popupControl.DropShadowEnabled = value;
this.Invalidate();
}
}
public bool DroppedDown
{
get
{
return _bDroppedDown;
}
set
{
_bDroppedDown = value;
if (value)
{
_popupControl.Show(this, new Point(0, this.Height), ToolStripDropDownDirection.BelowRight);
}
else
{
_popupControl.Hide();
}
this.Invalidate();
}
}
public ToolStripItemCollection Items
{
get
{
return _popupControl.Items;
}
}
public CustomToolStripMenuItem SelectedItem
{
get
{
return _selectedItem;
}
}
public CustomComboBox()
{
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.Selectable, true);
this.SetStyle(ControlStyles.UserMouse, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SuspendLayout();
this.ResumeLayout(false);
_popupControl = new CustomContextMenuStrip();
_popupControl.BackColor = this.BackColor;
_popupControl.Closed += new ToolStripDropDownClosedEventHandler(PopupControl_Closed);
_popupControl.ItemClicked += new ToolStripItemClickedEventHandler(PopupControl_ItemClicked);
this.Width = DEFAULT_WIDTH;
this.Height = DEFAULT_HEIGHT;
_selectedItem = null;
_bDroppedDown = false;
this.BackColor = SystemColors.Control;
this.ForeColor = SystemColors.ControlText;
_borderColour = SystemColors.ActiveBorder;
_dropDownButtonColour = SystemColors.ButtonFace;
_droppedDownArrowColour = SystemColors.ControlLight;
_closedArrowColour = SystemColors.ControlDark;
//Set these via the properties so they take effect on _popupControl
this.DropDownBackColour = SystemColors.Control;
this.DropDownForeColour = SystemColors.ControlText;
this.DropDownBorderColour = SystemColors.ActiveBorder;
}
protected override void OnFontChanged(EventArgs e)
{
_popupControl.Font = this.Font;
base.OnFontChanged(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
this.Invalidate();
this.DroppedDown = !this.DroppedDown;
base.OnMouseDown(e);
}
protected override void OnMouseWheel(MouseEventArgs e)
{
int nIndex = this.Items.IndexOf(_selectedItem);
if (e.Delta < 0)
{
if (nIndex < (this.Items.Count - 1))
{
_selectedItem = (CustomToolStripMenuItem)this.Items[nIndex + 1];
OnSelectedItemChanged(EventArgs.Empty);
this.Invalidate();
}
}
else if (e.Delta > 0)
{
if (nIndex > 0)
{
_selectedItem = (CustomToolStripMenuItem)this.Items[nIndex - 1];
OnSelectedItemChanged(EventArgs.Empty);
this.Invalidate();
}
}
base.OnMouseWheel(e);
}
protected override void OnPaint(PaintEventArgs e)
{
Rectangle boundingRect = new Rectangle(0, 0, this.Width, this.Height);
using (SolidBrush brush = new SolidBrush(this.BackColor))
{
e.Graphics.FillRectangle(brush, boundingRect);
}
ControlPaint.DrawBorder(e.Graphics, boundingRect, _borderColour, ButtonBorderStyle.Solid);
//Draw the dropdown button background
int nButtonWidth = SystemInformation.VerticalScrollBarWidth + SystemInformation.VerticalFocusThickness;
Rectangle dropDownButtonRect = new Rectangle(this.Width - nButtonWidth, 0, nButtonWidth, this.Height);
using (SolidBrush brush = new SolidBrush(_dropDownButtonColour))
{
e.Graphics.FillRectangle(brush, dropDownButtonRect);
}
//Draw the dropdown button arrow
using (GraphicsPath path = new GraphicsPath())
{
Point pTopLeft = new Point()
{
X = (int)Math.Round((float)dropDownButtonRect.X + ((float)dropDownButtonRect.Width * 0.30f)),
Y = (int)Math.Round((float)dropDownButtonRect.Y + ((float)dropDownButtonRect.Height * 0.35f))
};
Point pTopRight = new Point()
{
X = (int)Math.Round((float)dropDownButtonRect.X + ((float)dropDownButtonRect.Width * 0.70f)),
Y = (int)Math.Round((float)dropDownButtonRect.Y + ((float)dropDownButtonRect.Height * 0.35f))
};
Point pBottom = new Point()
{
X = (int)Math.Round((float)dropDownButtonRect.X + ((float)dropDownButtonRect.Width * 0.5f)),
Y = (int)Math.Round((float)dropDownButtonRect.Y + ((float)dropDownButtonRect.Height * 0.65f))
};
path.AddLine(pTopLeft, pTopRight);
path.AddLine(pTopRight, pBottom);
SmoothingMode previousSmoothingMode = e.Graphics.SmoothingMode;
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
{
using (SolidBrush brush = new SolidBrush(_bDroppedDown ? _droppedDownArrowColour : _closedArrowColour))
{
e.Graphics.FillPath(brush, path);
}
}
e.Graphics.SmoothingMode = previousSmoothingMode;
}
if (_selectedItem != null)
{
using (SolidBrush brush = new SolidBrush(this.ForeColor))
{
SizeF stringSize = e.Graphics.MeasureString(_selectedItem.Text, this.Font);
e.Graphics.DrawString(_selectedItem.Text, this.Font, brush, new Point(0, (this.Height / 2) - ((int)stringSize.Height / 2)));
}
}
}
protected override void OnSizeChanged(EventArgs e)
{
if (_popupControl != null)
{
_popupControl.MaximumItemSize = new Size(this.Width - 1, _popupControl.MaximumItemSize.Height);
_popupControl.Width = this.Width;
}
base.OnSizeChanged(e);
}
private void OnSelectedItemChanged(EventArgs e)
{
if (SelectedItemChanged != null)
{
SelectedItemChanged(this, e);
}
}
public void SelectFirstItem()
{
if (this.Items != null && this.Items.Count > 0)
{
_selectedItem = (CustomToolStripMenuItem)this.Items[0];
OnSelectedItemChanged(EventArgs.Empty);
}
}
public event EventHandler SelectedItemChanged;
private void PopupControl_Closed(object sender, ToolStripDropDownClosedEventArgs e)
{
this.DroppedDown = false;
this.Invalidate(true);
}
private void PopupControl_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
_selectedItem = (CustomToolStripMenuItem)e.ClickedItem;
OnSelectedItemChanged(EventArgs.Empty);
}
}
}

Related

Make a CustomListBox to add BorderColor then can't use ListBox normal properties/methods

I'm trying to change the border color of a ListBox.
I made this custom control where i have a function that makes the border by drawing.
After had it working, noticed that can't use ListBox.Items anymore, methods such as .Add() or .Clear().
Code of the Custom ListBox:
class CustomListBox : UserControl
{
//Fields
private Color borderColor = Color.MediumSlateBlue;
private int borderSize = 1;
//Items
private ListBox Listb;
//Properties
[Category("Custom")]
public Color BorderColor
{
get { return borderColor; }
set
{
borderColor = value;
}
}
[Category("Custom")]
public int BorderSize
{
get { return borderSize; }
set
{
borderSize = value;
this.Padding = new Padding(borderSize);//Border Size
AdjustListBoxDimensions();
}
}
public CustomListBox()
{
Listb = new ListBox();
this.SuspendLayout();
// ListBox
Listb.BorderStyle = BorderStyle.None;
Listb.DrawMode = DrawMode.OwnerDrawFixed;
Listb.ForeColor = Color.FromArgb(((int)(((byte)(249)))), ((int)(((byte)(249)))), ((int)(((byte)(249)))));
Listb.FormattingEnabled = true;
Listb.ItemHeight = 24;
Listb.Location = new Point(567, 64);
Listb.Name = "CustomListBox";
Listb.Size = new Size(235, 936);
Listb.DrawItem += new DrawItemEventHandler(Listb_DrawItem);
this.MinimumSize = new Size(200, 30);
this.Size = new Size(200, 30);
this.Padding = new Padding(borderSize);//Border Size
this.Font = new Font(this.Font.Name, 12F);
this.ResumeLayout();
AdjustListBoxDimensions();
}
// Highlight event
private void Listb_DrawItem(object sender, DrawItemEventArgs e)
{
Color backgroundColor = Color.FromArgb(50, 50, 50);
Color horizontalColor = Color.FromArgb(100, 100, 100);
if (e.Index >= 0)
{
SolidBrush sb = new SolidBrush(((e.State & DrawItemState.Selected) == DrawItemState.Selected) ? horizontalColor : backgroundColor);
e.Graphics.FillRectangle(sb, e.Bounds);
string text = Listb.Items[e.Index].ToString();
SolidBrush tb = new SolidBrush(e.ForeColor);
e.Graphics.DrawString(text, e.Font, tb, Listb.GetItemRectangle(e.Index).Location);
}
}
//Adjust Dimension (Still in test)
private void AdjustListBoxDimensions()
{
Listb.Location = new Point()
{
X = this.Width - this.Padding.Right - Listb.Width,
Y = Listb.Height
};
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Graphics graph = e.Graphics;
//Draw border
using (Pen penBorder = new Pen(borderColor, borderSize))
{
penBorder.Alignment = PenAlignment.Inset;
graph.DrawRectangle(penBorder, 0, 0, this.Width - 0.5F, this.Height - 0.5F);
}
}
}
My problem is that i can't use the ListBox properties/methods, is there a way to inherit them?
Since you've made your own control, you have to make your own properties and methods, calling Listbox's methods and getters/setters
something like this:
public void Add(object item)
{
this.Listb.Items.Add(item);
}
public ObjectCollection ListItems
{
get
{
return this.Listb.Items;
}
set
{
this.Listb.Items.AddRange(value);
}
}
//etc...
public void Add(object item)
{
this.Listb.BeginUpdate();
this.Listb.Items.Add(item);
this.Listb.EndUpdate();
}
public void Clear()
{
this.Listb.BeginUpdate();
this.Listb.Items.Clear();
this.Listb.EndUpdate();
}
public int SelectedIndex()
{
return this.Listb.SelectedIndex;
}
public object Item(int index)
{
return this.Listb.Items[index];
}
Add to code:
Dock=Fill
This.control.add(listb)

The problem with the flickering of the buttons in the project

Good time of day.
The problem is very strange and incomprehensible.
After successfully creating the button, and then using it, I noticed a very unpleasant bug, flickering buttons.
Namely, it is played very easily, open the project, and press the ALT button.
For some reason, the buttons blink once, and that's it. I don't understand why this happens and how to fix it.
Please help.
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace TestProject
{
public class RoundButton : Control
{
public Color BackColor2 { get; set; }
public Color ButtonBorderColor { get; set; }
public int ButtonRoundRadius { get; set; }
public Color ButtonHighlightColor { get; set; }
public Color ButtonHighlightColor2 { get; set; }
public Color ButtonHighlightForeColor { get; set; }
public Color ButtonPressedColor { get; set; }
public Color ButtonPressedColor2 { get; set; }
public Color ButtonPressedForeColor { get; set; }
private bool IsHighlighted;
private bool IsPressed;
public RoundButton()
{
Size = new Size(100, 40);
ButtonRoundRadius = 30;
BackColor = Color.Gainsboro;
BackColor2 = Color.Silver;
ButtonBorderColor = Color.Black;
ButtonHighlightColor = Color.Orange;
ButtonHighlightColor2 = Color.OrangeRed;
ButtonHighlightForeColor = Color.Black;
ButtonPressedColor = Color.Red;
ButtonPressedColor2 = Color.Maroon;
ButtonPressedForeColor = Color.White;
}
protected override CreateParams CreateParams
{
get
{
CreateParams createParams = base.CreateParams;
createParams.ExStyle |= 0x00000020; // WS_EX_TRANSPARENT
return createParams;
}
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
var foreColor = IsPressed ? ButtonPressedForeColor : IsHighlighted ? ButtonHighlightForeColor : ForeColor;
var backColor = IsPressed ? ButtonPressedColor : IsHighlighted ? ButtonHighlightColor : BackColor;
var backColor2 = IsPressed ? ButtonPressedColor2 : IsHighlighted ? ButtonHighlightColor2 : BackColor2;
using (var pen = new Pen(ButtonBorderColor, 1))
e.Graphics.DrawPath(pen, Path);
using (var brush = new LinearGradientBrush(ClientRectangle, backColor, backColor2, LinearGradientMode.Vertical))
e.Graphics.FillPath(brush, Path);
using (var brush = new SolidBrush(foreColor))
{
var sf = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
var rect = ClientRectangle;
rect.Inflate(-4, -4);
e.Graphics.DrawString(Text, Font, brush, rect, sf);
}
base.OnPaint(e);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
}
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
IsHighlighted = true;
Parent.Invalidate(Bounds, false);
Invalidate();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
IsHighlighted = false;
IsPressed = false;
Parent.Invalidate(Bounds, false);
Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
IsPressed = true;
Parent.Invalidate(Bounds, false);
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
Parent.Invalidate(Bounds, false);
Invalidate();
IsPressed = false;
}
protected GraphicsPath Path
{
get
{
var rect = ClientRectangle;
rect.Inflate(-1, -1);
return GetRoundedRectangle(rect, ButtonRoundRadius);
}
}
public static GraphicsPath GetRoundedRectangle(Rectangle rect, int d)
{
var gp = new GraphicsPath();
gp.AddArc(rect.X, rect.Y, d, d, 180, 90);
gp.AddArc(rect.X + rect.Width - d, rect.Y, d, d, 270, 90);
gp.AddArc(rect.X + rect.Width - d, rect.Y + rect.Height - d, d, d, 0, 90);
gp.AddArc(rect.X, rect.Y + rect.Height - d, d, d, 90, 90);
gp.CloseFigure();
return gp;
}
}
}
When pressing the ALT key the OnPaint() event gets called and the control is redrawn (if you put a breakpoint you will see it).
It comes from the base class of Control class.
you can solve this by adding this method to the parent form of the custom control:
protected override void WndProc(ref Message msg)
{
if (msg.Msg == 0x128) return;
base.WndProc(ref msg);
}
I guess the reason is that ALT key is used for other functionality of Control,
for example, selecting a menu item from MenuStrip control.

How to put some color on image

I have here the circle image, what I want to do is to put some color in specific position.. For example, when I click the button1, the left side of the circle will be filled by red, and when I click the button2, the right side will be filled by as well, and when I click the button1 again, the color will be removed, and so on...
I've done some research about it, and found out 2 ways to do it. First is, to layover the circle with another image. Second is to draw, and use the Graphics class in C#..
Now, my question is, is there another possible way to do it? What is the best way?
P.S: The purpose of this is for the tooth chart. :)
Here's a Resizable, Clickable, UserControl based on qing`s post. You can click on the regions directly to toggle them, or change them via code.
public partial class ToothChart : UserControl
{
public ToothChart()
{
InitializeComponent();
this.DoubleBuffered = true;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
if (this.ParentForm != null)
{
this.ParentForm.FormClosing += (s, evt) => { OnHandleDestroyed(new EventArgs()); };
}
}
protected override void OnHandleDestroyed(EventArgs e)
{
base.OnHandleDestroyed(e);
if (this._pathTop != null)
{
this._pathTop.Dispose();
this._pathTop = null;
}
if (this._pathRight != null)
{
this._pathRight.Dispose();
this._pathRight = null;
}
if (this._pathBottom != null)
{
this._pathBottom.Dispose();
this._pathBottom = null;
}
if (this._pathLeft != null)
{
this._pathLeft.Dispose();
this._pathLeft = null;
}
if (this._pathCenter != null)
{
this._pathCenter.Dispose();
this._pathCenter = null;
}
}
private GraphicsPath _pathTop = null;
private GraphicsPath _pathLeft = null;
private GraphicsPath _pathBottom = null;
private GraphicsPath _pathRight = null;
private GraphicsPath _pathCenter = null;
private bool _TopRegion = false;
public bool TopRegion
{
get
{
return _TopRegion;
}
set
{
if (_TopRegion != value)
{
_TopRegion = value;
this.Invalidate();
}
}
}
private bool _RightRegion = false;
public bool RightRegion
{
get
{
return _RightRegion;
}
set
{
if (_RightRegion != value)
{
_RightRegion = value;
this.Invalidate();
}
}
}
private bool _BottomRegion = false;
public bool BottomRegion
{
get
{
return _BottomRegion;
}
set
{
if (_BottomRegion != value)
{
_BottomRegion = value;
this.Invalidate();
}
}
}
private bool _LeftRegion = false;
public bool LeftRegion
{
get
{
return _LeftRegion;
}
set
{
if (_LeftRegion != value)
{
_LeftRegion = value;
this.Invalidate();
}
}
}
private bool _CenterRegion = false;
public bool CenterRegion
{
get
{
return _CenterRegion;
}
set
{
if (_CenterRegion != value)
{
_CenterRegion = value;
this.Invalidate();
}
}
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
if (this.IsHandleCreated && this._pathTop != null)
{
this.UpdateRegions();
}
}
private void UpdateRegions()
{
int diameterBig = Math.Min(this.Width, this.Height) - 10;
int diameterSmall = Math.Min(this.Width, this.Height) / 3;
if (diameterBig > 0 && diameterSmall > 0)
{
Point _centerPoint = new Point(this.Width / 2, this.Height / 2);
Rectangle rectangle = new Rectangle(_centerPoint.X - diameterBig / 2, _centerPoint.Y - diameterBig / 2, diameterBig, diameterBig);
Rectangle rectangle2 = new Rectangle(_centerPoint.X - diameterSmall / 2, _centerPoint.Y - diameterSmall / 2, diameterSmall, diameterSmall);
_pathTop.Reset();
_pathTop.AddArc(rectangle, 225, 90);
_pathTop.AddArc(rectangle2, -45, -90);
_pathLeft.Reset();
_pathLeft.AddArc(rectangle, 135, 90);
_pathLeft.AddArc(rectangle2, -135, -90);
_pathBottom.Reset();
_pathBottom.AddArc(rectangle, 45, 90);
_pathBottom.AddArc(rectangle2, -225, -90);
_pathRight.Reset();
_pathRight.AddArc(rectangle, -45, 90);
_pathRight.AddArc(rectangle2, -315, -90);
_pathCenter.Reset();
_pathCenter.AddEllipse(rectangle2);
this.Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
if (this.IsHandleCreated)
{
if (this._pathTop == null)
{
this._pathTop = new GraphicsPath();
this._pathRight = new GraphicsPath();
this._pathBottom = new GraphicsPath();
this._pathLeft = new GraphicsPath();
this._pathCenter = new GraphicsPath();
this.UpdateRegions();
}
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
if (this.TopRegion)
{
e.Graphics.FillPath(Brushes.Blue, _pathTop);
}
e.Graphics.DrawPath(Pens.Black, _pathTop);
if (this.RightRegion)
{
e.Graphics.FillPath(Brushes.DarkRed, _pathRight);
}
e.Graphics.DrawPath(Pens.Black, _pathRight);
if (this.BottomRegion)
{
e.Graphics.FillPath(Brushes.Teal, _pathBottom);
}
e.Graphics.DrawPath(Pens.Black, _pathBottom);
if (this.LeftRegion)
{
e.Graphics.FillPath(Brushes.Yellow, _pathLeft);
}
e.Graphics.DrawPath(Pens.Black, _pathLeft);
if (this.CenterRegion)
{
e.Graphics.FillPath(Brushes.LightGreen, _pathCenter);
}
e.Graphics.DrawPath(Pens.Black, _pathCenter);
}
}
protected override void OnMouseClick(MouseEventArgs e)
{
base.OnMouseClick(e);
Point p = new Point(e.X, e.Y);
if (this._pathTop.IsVisible(p))
{
this.TopRegion = !this.TopRegion;
}
else if (this._pathRight.IsVisible(p))
{
this.RightRegion = !this.RightRegion;
}
else if (this._pathBottom.IsVisible(p))
{
this.BottomRegion = !this.BottomRegion;
}
else if (this._pathLeft.IsVisible(p))
{
this.LeftRegion = !this.LeftRegion;
}
else if (this._pathCenter.IsVisible(p))
{
this.CenterRegion = !this.CenterRegion;
}
}
}

C# progress bar bad render

I created custom progress bar with image but i have problem. From 50% on progress bar are showing spaces.
Progress Bar here: http://i.stack.imgur.com/d686B.png
There is control library code:
public partial class UserControl1: UserControl
{
public UserControl1() : base()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}
private void UserControl1_Load(object sender, EventArgs e)
{
}
bool _revealImage = true;
int _maximum = 100;
int _value = 0;
Image _progressbarImage = Properties.Resources.progress;
Color _progressbarColor = Color.Transparent;
protected override void OnResize(EventArgs e)
{
// Invalidate the control to get a repaint.
this.Invalidate();
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Graphics g = e.Graphics;
Rectangle r = this.ClientRectangle;
ControlPaint.DrawBorder(g, r, Color.Transparent, ButtonBorderStyle.Solid);
float percent = (float)_value / (float)(_maximum);
//r.Inflate(-1, -1);
r.Width = (int)((float)r.Width * percent);
if ((r.Width < 1))
{
return;
}
if (_progressbarImage == null)
{
using (SolidBrush b = new SolidBrush(_progressbarColor))
{
g.FillRectangle(b, r);
b.Dispose();
}
}
else
{
g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
if (_revealImage)
{
g.DrawImage(_progressbarImage, r, r, GraphicsUnit.Pixel);
}
else
{
g.DrawImage(_progressbarImage, r);
}
}
g.Dispose();
}
public int Maximum
{
get
{
return _maximum;
}
set
{
_maximum = value;
this.Invalidate();
}
}
public int Value
{
get
{
return _value;
}
set
{
// _value = value;
int oldValue = _value;
// Make sure that the value does not stray outside the valid range.
if (value < 0)
{
_value = 0;
}
else if (value > Maximum)
{
_value = Maximum;
}
else
{
_value = value;
}
// Invalidate only the changed area.
float percent;
Rectangle newValueRect = this.ClientRectangle;
Rectangle oldValueRect = this.ClientRectangle;
// Use a new value to calculate the rectangle for progress.
percent = (float)(_value - 0) / (float)(Maximum - 0);
newValueRect.Width = (int)((float)newValueRect.Width * percent);
// Use an old value to calculate the rectangle for progress.
percent = (float)(oldValue - 0) / (float)(Maximum - 0);
oldValueRect.Width = (int)((float)oldValueRect.Width * percent);
Rectangle updateRect = new Rectangle();
// Find only the part of the screen that must be updated.
if (newValueRect.Width > oldValueRect.Width)
{
updateRect.X = oldValueRect.Size.Width;
updateRect.Width = newValueRect.Width - oldValueRect.Width;
}
else
{
updateRect.X = newValueRect.Size.Width;
updateRect.Width = oldValueRect.Width - newValueRect.Width;
}
updateRect.Height = this.Height;
// Invalidate the intersection region only.
this.Invalidate(updateRect);
}
}
public Image ProgressbarImage
{
get
{
return _progressbarImage;
}
set
{
_progressbarImage = value;
this.Invalidate();
}
}
public Color ProgressbarColor
{
get
{
return _progressbarColor;
}
set
{
_progressbarColor = value;
this.Invalidate();
}
}
public bool RevealImage
{
get
{
return _revealImage;
}
set
{
_revealImage = value;
this.Invalidate();
}
}
}
Thank you for help.

Mouse down between two pieces of a puzzle

I am building a puzzle game in winforms, and i want to recognize the mousedown over any piece, and move it with the mousemove.
The issue is that when i touch the transparent part of the puzzle piece, i want to verify if there is any piece behind that one, and if so, start the mousemove of that other piece, got it?
I am also able to recognize if the mousedown was over the image, or if it happens in the transparent part of the puzzle piece. My problem is to get the best way to pass the mouse event to the piece behind.
Many Thanks in advance.
UPDATE 1
The following is the class for the puzzle piece:
class Peça : DrawingArea
{
private Point _Offset = Point.Empty;
public Image imagem
{
get;
set;
}
protected override void OnDraw()
{
Rectangle location = new Rectangle(0, 0, imagem.Width, imagem.Height);
this.graphics.DrawImage(imagem, location);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (_Offset != Point.Empty)
{
Point newlocation = this.Location;
newlocation.X += e.X - _Offset.X;
newlocation.Y += e.Y - _Offset.Y;
this.Location = newlocation;
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
_Offset = Point.Empty;
}
protected override void OnMouseDown(MouseEventArgs e)
{
Down(e);
//Console.WriteLine(color.ToString());
}
public void Down(MouseEventArgs e)
{
Bitmap b = new Bitmap(imagem);
Color? color = null;
try
{
color = b.GetPixel(e.X, e.Y);
if (color.Value.A != 0 && color != null)
{
if (e.Button == MouseButtons.Left)
{
_Offset = new Point(e.X, e.Y);
this.BringToFront();
}
}
}
catch {
}
}
}
The following code, is my DrawingArea (Panel) that i create in order to work with transparency:
abstract public class DrawingArea : Panel
{
protected Graphics graphics;
abstract protected void OnDraw();
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x00000020; //WS_EX_TRANSPARENT
return cp;
}
}
public DrawingArea()
{
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}
protected override void OnPaint(PaintEventArgs e)
{
this.graphics = e.Graphics;
this.graphics.TextRenderingHint =
System.Drawing.Text.TextRenderingHint.AntiAlias;
this.graphics.InterpolationMode =
System.Drawing.Drawing2D.InterpolationMode.HighQualityBilinear;
this.graphics.PixelOffsetMode =
System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
this.graphics.SmoothingMode =
System.Drawing.Drawing2D.SmoothingMode.HighQuality;
OnDraw();
}
}
And you can also see my Form code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
}
protected override CreateParams CreateParams
{
get
{
CreateParams handleParam = base.CreateParams;
handleParam.ExStyle |= 0x02000000;
return handleParam;
}
}
}
Those are my pieces, and when i touch the transparent space in the first piece, i want to pick up the second one and move it on mouseMouse instead of doing nothing...
It looks like this:
Apologize my bad english.
UPDATE 2
I think i am getting very close to the solution, but something strange happens now, when i touch the piece behind another one, it disappear...
What am i doing wrong?
SOME CODE UPDATES
Piece Class:
class Peça : DrawingArea
{
private Point _Offset = Point.Empty;
public Boolean movable = false;
public Image imagem
{
get;
set;
}
protected override void OnDraw()
{
Rectangle location = new Rectangle(0, 0, imagem.Width, imagem.Height);
this.graphics.DrawImage(imagem, location);
}
public void Move(MouseEventArgs e)
{
if (_Offset != Point.Empty)
{
Point newlocation = this.Location;
newlocation.X += e.X - _Offset.X;
newlocation.Y += e.Y - _Offset.Y;
this.Location = newlocation;
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
_Offset = Point.Empty;
movable = false;
}
protected override void OnMouseDown(MouseEventArgs e)
{
Down(e);
//Console.WriteLine(color.ToString());
}
public Boolean Down(MouseEventArgs e, bool propaga=true)
{
Form parentForm = (this.Parent as Form);
Bitmap b = new Bitmap(imagem);
Color? color = null;
Boolean flag = false;
try
{
color = b.GetPixel(e.X, e.Y);
if (color.Value.A != 0 && color != null)
{
if (e.Button == MouseButtons.Left)
{
_Offset = new Point(e.X, e.Y);
this.BringToFront();
flag = true;
movable = true;
}
}
else
{
if(propaga)
(this.Parent as Form1).propagaEvento(this, e);
flag = false;
}
return flag;
}
catch {
return flag; }
}
}
Form1:
public partial class Form1 : Form
{
private List<Peça> peças;
private Point _Offset = Point.Empty;
public Form1()
{
InitializeComponent();
peças = new List<Peça>();
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
criaListaPecas();
associaEventosPecas();
}
private void associaEventosPecas()
{
foreach (Peça p in peças)
{
p.MouseMove += Form1_MouseMove;
}
}
private void criaListaPecas()
{
peças.Clear();
foreach (Control p in this.Controls)
{
if (p.GetType() == typeof(Peça))
peças.Add((Peça)p);
}
Console.WriteLine(peças[0].Name);
Console.WriteLine(peças[1].Name);
Console.WriteLine(peças[2].Name);
}
protected override CreateParams CreateParams
{
get
{
CreateParams handleParam = base.CreateParams;
handleParam.ExStyle |= 0x02000000;
return handleParam;
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
label1.Text = e.Location.ToString();
gereMovimento(e);
}
private void gereMovimento(MouseEventArgs e)
{
foreach (Peça p in peças)
{
if (p.movable)
{
p.Move(e);
}
}
}
internal void propagaEvento(Peça peça, MouseEventArgs e)
{
foreach (Peça p in peças)
{
if (p != peça)
{
if (p.Down(e, false))
break;
}
}
}
}
Thanks in advance again :)
Pieces can be represent as:
public class Piece
{
public Point Location {get; set;}
public int Z {get; set;}
public int ID {get; set;} // to be bound to control or a control itself?
public Image Image {get; set;} // texture?
public DockStyle PlusArea {get; set;}
public DockStyle MinusArea {get; set;} // can be None
...
public bool HitTest(Point point)
{
// assuming all of same size
if((new Rectangle(Location, new Size(...)).Contains(point))
{
switch(MinusArea)
{
case Top:
if((new Rectangle(...)).Contains(point))
return false;
...
}
}
switch(MinusArea)
{
case Top:
if((new Rectangle(...)).Contains(point))
return true;
...
}
return false;
}
Then puzzle is
public class Puzzle
{
public List<Piece> Pieces {get; set;}
public void Draw(Graphics graphics)
{
// draw all pictures with respect to z order
}
public Piece HitTest(Point point)
{
... // hittest all pieces, return highest z-order or null
}
}
It is not a complete solution, but should give you idea.
Basically:
In mouse event you call Figure.HitTest() to get figure to start moving (that's what you need).
You draw everything into owner drawn control by calling Figure.Draw().
Obviously, drag-n-drop operations are calling Invalidate().
You may have special flag to indicate figure being dragged and draw it differently (with shadows, a bit offsetted to simulate it's pulled over other pieces, etc).
Each figure is represented as rectangle and PlusArea or MinusArea (don't know how to call them better, it's this extra or missing area of piece connectors), this is simplification, you can improve it.
In general, keep a list of all your puzzle piece controls, sorted top down. When you get a mouse down event on one piece, check the transparency at that point, if it it not transparent handle the event on that piece. If it is transparent forward the event to the next piece down in your list (directly calling the event handler is probably the easiest way). Keep doing this until you either find a non transparent point, or run out of pieces.
UPDATE
Here is a link to a project showing an example of how to do this in pure GDI.
https://drive.google.com/file/d/0B42fIyGTLNv3WlJwNGVRN2txTGs/edit?usp=sharing
SOLVED :)
i have figured it out...
Here's the code to anybody how needs
(I have made it right now, so the code is not clean yet):
Piece Class:
class Peça : DrawingArea
{
private Point _Offset = Point.Empty;
public Boolean movable = false;
public Image imagem
{
get;
set;
}
protected override void OnDraw()
{
Rectangle location = new Rectangle(0, 0, imagem.Width, imagem.Height);
this.graphics.DrawImage(imagem, location);
}
public Boolean Down(Point e, bool propaga = true)
{
Bitmap b = new Bitmap(imagem);
Color? color = null;
Boolean flag = false;
try
{
color = b.GetPixel(e.X, e.Y);
if (color.Value.A != 0 && color != null)
{
flag = true;
}
else
{
flag = false;
}
return flag;
}
catch
{
return flag;
}
}
}
Form1:
public partial class Form1 : Form
{
private List<Peça> peças;
private Point _Offset = Point.Empty;
private Peça peça1, peça2, peça3, peça4;
private bool canMove;
private Peça atual;
private bool other=false;
public Form1()
{
FormBorderStyle = FormBorderStyle.None;
WindowState = FormWindowState.Maximized;
InitializeComponent();
atual = new Peça();
peça1 = new Peça();
peça2 = new Peça();
peça3 = new Peça();
peça4 = new Peça();
peça1.imagem = Properties.Resources._4p1_1;
peça2.imagem = Properties.Resources._4p1_2;
peça3.imagem = Properties.Resources._4p1_3;
peça4.imagem = Properties.Resources._4p1_4;
peças = new List<Peça>();
peça1.Name = "peça1";
peça2.Name = "peça2";
peça3.Name = "peça3";
peça4.Name = "peça4";
this.Controls.Add(peça1);
this.Controls.Add(peça2);
this.Controls.Add(peça3);
this.Controls.Add(peça4);
criaListaPecas();
foreach (Peça p in peças)
{
p.Size = new Size(p.imagem.Width, p.imagem.Height);
}
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
associaEventosPecas();
canMove = false;
}
private void associaEventosPecas()
{
foreach (Peça p in peças)
{
p.MouseMove += Form1_MouseMove;
p.MouseDown += Form1_MouseDown;
p.MouseUp += Form1_MouseUp;
}
}
private void criaListaPecas()
{
peças.Clear();
foreach (Control p in this.Controls)
{
if (p.GetType() == typeof(Peça))
peças.Add((Peça)p);
}
Console.WriteLine(peças[0].Name);
Console.WriteLine(peças[1].Name);
Console.WriteLine(peças[2].Name);
Console.WriteLine(peças[3].Name);
}
protected override CreateParams CreateParams
{
get
{
CreateParams handleParam = base.CreateParams;
handleParam.ExStyle |= 0x02000000;
return handleParam;
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (sender.GetType().Equals(typeof(Peça)))
{
label1.Text = new Point(e.Location.X + (sender as Peça).Location.X, e.Location.Y + (sender as Peça).Location.Y).ToString();
}
else
label1.Text = e.Location.ToString();
gereMovimento(sender, e);
}
private void gereMovimento(object sender, MouseEventArgs e)
{
if (canMove)
{
if (other)
{
Point p = atual.PointToClient(new Point(e.X + (sender as Peça).Location.X, e.Y + (sender as Peça).Location.Y));
Point newlocation = atual.Location;
newlocation.X += p.X - _Offset.X;
newlocation.Y += p.Y - _Offset.Y;
atual.Location = newlocation;
}
else
{
Point newlocation = atual.Location;
newlocation.X += e.X - _Offset.X;
newlocation.Y += e.Y - _Offset.Y;
atual.Location = newlocation;
}
}
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
if (sender.GetType().Equals(typeof(Peça)) && e.Button == MouseButtons.Left)
{
atual = sender as Peça;
atual.BringToFront();
criaListaPecas();
if (atual.Down(e.Location))
{
_Offset = new Point(e.X, e.Y);
canMove = true;
other = false;
}
else
{
Console.WriteLine(peças[1].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y)));
Console.WriteLine(atual.Location);
Point p = new Point();
if (peças[1].ClientRectangle.Contains(peças[1].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y)))
&& peças[1].Down(peças[1].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y))))
{
p = peças[1].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y));
atual = peças[1];
atual.BringToFront();
criaListaPecas();
_Offset = p;
canMove = true;
other = true;
}
else if (peças[2].ClientRectangle.Contains(peças[2].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y)))
&& peças[2].Down(peças[2].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y))))
{
p = peças[2].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y));
atual = peças[2];
atual.BringToFront();
criaListaPecas();
_Offset = p;
canMove = true;
other = true;
}
else if (peças[3].ClientRectangle.Contains(peças[3].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y)))
&& peças[3].Down(peças[3].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y))))
{
p = peças[3].PointToClient(new Point(e.X + atual.Location.X, e.Y + atual.Location.Y));
atual = peças[3];
atual.BringToFront();
criaListaPecas();
_Offset = p;
canMove = true;
other = true;
}
}
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
canMove = false;
}
}
Apologize the repeated and confused code, but as i said, i have made it seconds ago, and did not clean the code yet ;)

Categories