RadioButtonGroup in Winforms - c#

I'm trying to create UX that is familiar to Bootstrap 4 Radio Button Group.
I know I can change Appearance=Button but this wont give me desired effect.
This is what I get:
and this is what I want:
I found similar question, but it uses WPF and I need WinForms.
Second one suggest using list view, but in that question list is vertical, but I need horizontal align.
I'm looking for a control that has first button rounded on left and last on right (just like on second image)
My question is: Does anyone know is such control exists? If yes I'll be grateful for anu links, if no then any tips regarding how to create such control are welcome (I know how to create custom controls and paint them so links on how to get started with user controls aren't needed)
EDIT
Because my question got down voted I'm adding code that I've written to create such control.
This is result I got:
and here is code:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace UserControls
{
public sealed class RadioGroupBox: UserControl
{
public RadioGroupBox()
{
DoubleBuffered = true;
ResizeRedraw = true;
Padding = new Padding(2);
CalculateItemWidth();
}
public event EventHandler SelectedIndexChanged;
protected override Size DefaultSize => new Size(200, 30);
private int _cornerRadius = 2;
public int CornerRadius
{
get => _cornerRadius;
set
{
if(value==_cornerRadius) return;
_cornerRadius = value;
Invalidate();
}
}
private string[] _items = {"A", "B", "C"};
[Category("Data")]
//[DefaultValue(null)]
[Description("Items")]
public string[] Items
{
get => _items;
set
{
if(value==_items) return;
_items = value;
CalculateItemWidth();
Invalidate();
}
}
private int _itemWidth;
private void CalculateItemWidth()
{
if (_items == null || _items.Length == 0)
{
_itemWidth = 0;
return;
}
var width = ClientRectangle.Width - Padding.Horizontal;
_itemWidth = width / _items.Length;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
CalculateItemWidth();
//Debug.WriteLine(_itemWidth);
CalculateSelectedItem(e.Location);
}
private int _selectedIndex = -1;
public int SelectedIndex
{
get => _selectedIndex;
set
{
if(value==_selectedIndex) return;
_selectedIndex = value;
Invalidate();
}
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
_click = true;
if (_hoverPos > -1)
{
if (_selectedIndex != _hoverPos)
{
_selectedIndex = _hoverPos;
SelectedIndexChanged?.Invoke(this, e);
}
}
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
_click = false;
Invalidate();
}
private int _hoverPos = -1;
private bool _click;
private void CalculateSelectedItem(Point mouseLocation)
{
var clientRect = GetPaddedRectangle();
int pos;
if (!clientRect.Contains(mouseLocation))
{
pos = -1;
}
else
{
pos = mouseLocation.X / _itemWidth;
if (pos > _items.Length - 1)
{
pos = -1;
}
}
if (pos != _hoverPos)
{
_hoverPos = pos;
Invalidate();
}
//Debug.WriteLine(_hoverPos);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
CalculateItemWidth();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
var rect = GetPaddedRectangle();
LinearGradientBrush normalBrush = new LinearGradientBrush(rect,
Color.White,
Color.LightBlue,
LinearGradientMode.Vertical);
LinearGradientBrush hoverBlush = new LinearGradientBrush(rect,
Color.RoyalBlue,
Color.MediumBlue,
LinearGradientMode.Vertical);
LinearGradientBrush selectedBrush = new LinearGradientBrush(rect,
Color.DodgerBlue,
Color.Blue,
LinearGradientMode.Vertical);
//e.Graphics.FillRectangle(Brushes.Aqua, this.ClientRectangle);
using (GraphicsPath path = RoundedRect(rect, _cornerRadius))
{
e.Graphics.FillPath(normalBrush, path);
e.Graphics.DrawPath(Pens.DodgerBlue, path);
}
if (_items == null || _items.Length == 0) return;
if (_hoverPos > -1 || _selectedIndex>-1)
{
var flags = RectangleCorners.None;
if (_hoverPos == 0) flags = RectangleCorners.TopLeft | RectangleCorners.BottomLeft;
if(_hoverPos==_items.Length-1) flags = RectangleCorners.TopRight | RectangleCorners.BottomRight;
var rect2 = new Rectangle(rect.X + _hoverPos * _itemWidth, rect.Y, _itemWidth, rect.Height);
if (_hoverPos == _items.Length - 1)
{
rect2 = new Rectangle(rect.X + _hoverPos * _itemWidth, rect.Y, rect.Width-_hoverPos*_itemWidth, rect.Height);
}
if (_hoverPos > -1 && _selectedIndex != _hoverPos)
{
using (GraphicsPath path = RoundedRect(rect2, _cornerRadius, flags))
{
e.Graphics.FillPath(_click ? Brushes.SteelBlue : hoverBlush, path);
}
}
if (_selectedIndex > -1)
{
rect2 = new Rectangle(rect.X + _selectedIndex * _itemWidth, rect.Y, _itemWidth, rect.Height);
if (_selectedIndex == _items.Length - 1)
{
rect2 = new Rectangle(rect.X + _selectedIndex * _itemWidth, rect.Y, rect.Width - _selectedIndex * _itemWidth, rect.Height);
}
flags = RectangleCorners.None;
if (_selectedIndex == 0) flags = RectangleCorners.TopLeft | RectangleCorners.BottomLeft;
if (_selectedIndex == _items.Length - 1) flags = RectangleCorners.TopRight | RectangleCorners.BottomRight;
using (GraphicsPath path = RoundedRect(rect2, _cornerRadius, flags))
{
e.Graphics.FillPath(selectedBrush, path);
}
}
}
//pionowe linie
for (int i = 1; i <= _items.Length-1; i++)
{
e.Graphics.DrawLine(Pens.DodgerBlue,rect.X+i*_itemWidth,rect.Y, rect.X + i * _itemWidth, rect.Y+rect.Height);
}
StringFormat sf = new StringFormat
{
LineAlignment = StringAlignment.Center,
Alignment = StringAlignment.Center
};
for (var i = 0; i < _items.Length; i++)
{
string item = _items[i];
e.Graphics.DrawString(item, Font, i==_hoverPos?Brushes.White:Brushes.DodgerBlue, new Rectangle(rect.X+i* _itemWidth, rect.Y, _itemWidth, rect.Height), sf);
}
//Debug.WriteLine(MousePosition);
}
private Rectangle GetPaddedRectangle()
{
var rect = ClientRectangle;
var pad = Padding;
return new Rectangle(rect.X + pad.Left,
rect.Y + pad.Top,
rect.Width - pad.Horizontal,
rect.Height - pad.Vertical);
}
[Flags]
public enum RectangleCorners
{
None = 0, TopLeft = 1, TopRight = 2, BottomLeft = 4, BottomRight = 8,
All = TopLeft | TopRight | BottomLeft | BottomRight
}
public static GraphicsPath RoundedRect(Rectangle bounds, int radius, RectangleCorners corners = RectangleCorners.All)
{
int diameter = radius * 2;
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// Make a GraphicsPath to draw the rectangle.
PointF point1, point2;
// Upper left corner.
if ((RectangleCorners.TopLeft & corners) == RectangleCorners.TopLeft)
{
RectangleF corner = new RectangleF(bounds.X, bounds.Y,diameter, diameter);
path.AddArc(corner, 180, 90);
point1 = new PointF(bounds.X + radius, bounds.Y);
}
else point1 = new PointF(bounds.X, bounds.Y);
// Top side.
if ((RectangleCorners.TopRight & corners) == RectangleCorners.TopRight)
point2 = new PointF(bounds.Right - radius, bounds.Y);
else
point2 = new PointF(bounds.Right, bounds.Y);
path.AddLine(point1, point2);
// Upper right corner.
if ((RectangleCorners.TopRight & corners) == RectangleCorners.TopRight)
{
RectangleF corner = new RectangleF(bounds.Right - diameter, bounds.Y,diameter, diameter);
path.AddArc(corner, 270, 90);
point1 = new PointF(bounds.Right, bounds.Y + radius);
}
else point1 = new PointF(bounds.Right, bounds.Y);
// Right side.
if ((RectangleCorners.BottomRight & corners) == RectangleCorners.BottomRight)
point2 = new PointF(bounds.Right, bounds.Bottom - radius);
else
point2 = new PointF(bounds.Right, bounds.Bottom);
path.AddLine(point1, point2);
// Lower right corner.
if ((RectangleCorners.BottomRight & corners) == RectangleCorners.BottomRight)
{
RectangleF corner = new RectangleF(bounds.Right - diameter,bounds.Bottom - diameter,diameter, diameter);
path.AddArc(corner, 0, 90);
point1 = new PointF(bounds.Right - radius, bounds.Bottom);
}
else point1 = new PointF(bounds.Right, bounds.Bottom);
// Bottom side.
if ((RectangleCorners.BottomLeft & corners) == RectangleCorners.BottomLeft)
point2 = new PointF(bounds.X + radius, bounds.Bottom);
else
point2 = new PointF(bounds.X, bounds.Bottom);
path.AddLine(point1, point2);
// Lower left corner.
if ((RectangleCorners.BottomLeft & corners) == RectangleCorners.BottomLeft)
{
RectangleF corner = new RectangleF(bounds.X, bounds.Bottom - diameter,diameter, diameter);
path.AddArc(corner, 90, 90);
point1 = new PointF(bounds.X, bounds.Bottom - radius);
}
else point1 = new PointF(bounds.X, bounds.Bottom);
// Left side.
if ((RectangleCorners.TopLeft & corners) == RectangleCorners.TopLeft)
point2 = new PointF(bounds.X, bounds.Y + radius);
else
point2 = new PointF(bounds.X, bounds.Y);
path.AddLine(point1, point2);
// Join with the start point.
path.CloseFigure();
return path;
}
}
}
it isn't optimized, it's just prove of concept.
Whole point of this question is to find existing control without need to reinvent the wheel.
If I won't be able to find such control I'll create one and share it.

Related

Which method need to override to round the corner of NotiIcon's ContextMenuStrip?

In windows 11, I have found that almost every Systray Icon's Context Menu has a round corner.
As there is no Systray Menu for WPF application, I have used Window's form NotiIcon and ContextMenuStrip.
I have fully customized ToolStripProfessionalRenderer with ProfessionalColorTable to support dark mode
as per clients' requirements. Now I'm looking for rounding the corner. I was used "RoundedEdges = true;"
but not worked. I need to know exactly which method need to override to round the corner of ContextMenuStrip.
Here below is my renderer sample code.
///Menu Renderer
public class MenuRenderer : ToolStripProfessionalRenderer
{
//Fields
private Color primaryColor;
private Color textColor;
private int arrowThickness;
private WindowsTheme systrayTheme;
[Browsable(false)]
public WindowsTheme SystrayTheme
{
get { return systrayTheme; }
set { systrayTheme = value; }
}
//Constructor
public MenuRenderer(bool isMainMenu, Color primaryColor, Color textColor, Color menuItemMouseOverColor, Color menuItemMouseOverBorderColor, WindowsTheme theme)
: base(new MenuColorTable(isMainMenu, primaryColor, menuItemMouseOverColor, menuItemMouseOverBorderColor, theme))
{
RoundedEdges = true;
this.primaryColor = primaryColor;
this.systrayTheme = theme;
if (isMainMenu)
{
arrowThickness = 2;
if (textColor == Color.Empty) //Set Default Color
this.textColor = Color.Gainsboro;
else//Set custom text color
this.textColor = textColor;
}
else
{
arrowThickness = 1;
if (textColor == Color.Empty) //Set Default Color
this.textColor = Color.DimGray;
else//Set custom text color
this.textColor = textColor;
}
}
//Overrides
protected override void OnRenderItemText(ToolStripItemTextRenderEventArgs e)
{
base.OnRenderItemText(e);
e.Item.ForeColor = e.Item.Selected ? Color.White : textColor;
}
protected override void OnRenderArrow(ToolStripArrowRenderEventArgs e)
{
//Fields
var graph = e.Graphics;
var arrowSize = new Size(5, 10);
var arrowColor = e.Item.Selected ? Color.White : primaryColor;
var rect = new Rectangle(e.ArrowRectangle.Location.X, (e.ArrowRectangle.Height - arrowSize.Height) / 2,
arrowSize.Width, arrowSize.Height);
using (GraphicsPath path = new GraphicsPath())
using (Pen pen = new Pen(arrowColor, arrowThickness))
{
//Drawing
graph.SmoothingMode = SmoothingMode.AntiAlias;
path.AddLine(rect.Left, rect.Top, rect.Right, rect.Top + rect.Height / 2);
path.AddLine(rect.Right, rect.Top + rect.Height / 2, rect.Left, rect.Top + rect.Height);
graph.DrawPath(pen, path);
}
}
protected override void OnRenderGrip(ToolStripGripRenderEventArgs e)
{
Rectangle rectangle = new Rectangle(e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width, e.AffectedBounds.Height);
GraphicsPath graphicsPath = RoundedRect(rectangle, 8);
using (Pen pen = new Pen(Color.Green,5))
{
e.Graphics.DrawPath(pen, graphicsPath);
//e.Graphics.FillPath(pen.Brush, graphicsPath);
}
}
protected override void OnRenderToolStripPanelBackground(ToolStripPanelRenderEventArgs e)
{
//base.OnRenderToolStripPanelBackground(e);
Rectangle rectangle = new Rectangle(e.ToolStripPanel.Location.X, e.ToolStripPanel.Location.Y,
e.ToolStripPanel.ClientRectangle.Width, e.ToolStripPanel.ClientRectangle.Height);
GraphicsPath graphicsPath = RoundedRect(rectangle, 8);
using (Pen pen = new Pen(Color.Green, 5))
{
//e.Graphics.DrawPath(pen, graphicsPath);
e.Graphics.FillPath(pen.Brush, graphicsPath);
}
}
protected override void OnRenderStatusStripSizingGrip(ToolStripRenderEventArgs e)
{
//base.OnRenderStatusStripSizingGrip(e);
Rectangle rectangle = new Rectangle(e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width, e.AffectedBounds.Height);
GraphicsPath graphicsPath = RoundedRect(rectangle, 8);
using (Pen pen = new Pen(Color.Green,5))
{
e.Graphics.DrawPath(pen, graphicsPath);
//e.Graphics.FillPath(pen.Brush, graphicsPath);
}
}
protected override void OnRenderToolStripBorder(ToolStripRenderEventArgs e)
{
//Fields
//var graph = e.Graphics;
//var borderZise = new Size(e.AffectedBounds.Width + 10, e.AffectedBounds.Height + 10);
//var borderColor = Color.DeepPink;
//var rect = new Rectangle(e.AffectedBounds.X, (e.AffectedBounds.Height - borderZise.Height) / 2,
// borderZise.Width, borderZise.Height);
//using (GraphicsPath path = new GraphicsPath())
//using (Pen pen = new Pen(borderColor, arrowThickness))
//{
// //Drawing
// graph.SmoothingMode = SmoothingMode.AntiAlias;
// path.AddLine(rect.Left, rect.Top, rect.Right, rect.Top + rect.Height / 2);
// path.AddLine(rect.Right, rect.Top + rect.Height / 2, rect.Left, rect.Top + rect.Height);
// graph.DrawPath(pen, path);
//}
//DrawRoundedRectangle(e.Graphics, e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width - 5, e.AffectedBounds.Height, 15, Color.Red);
//Rectangle rectangle = new Rectangle(e.AffectedBounds.X, e.AffectedBounds.Y, e.AffectedBounds.Width, e.AffectedBounds.Height);
//GraphicsPath graphicsPath = RoundedRect(rectangle, 8);
//using (Pen pen = new Pen(Color.Green, 2))
//{
// //e.Graphics.DrawPath(pen, graphicsPath);
// e.Graphics.FillPath(pen.Brush, graphicsPath);
//}
}
public GraphicsPath RoundedRect(Rectangle bounds, int radius)
{
int diameter = radius * 2;
Size size = new Size(diameter, diameter);
Rectangle arc = new Rectangle(bounds.Location, size);
GraphicsPath path = new GraphicsPath();
if (radius == 0)
{
path.AddRectangle(bounds);
return path;
}
// top left arc
path.AddArc(arc, 180, 90);
// top right arc
arc.X = bounds.Right - diameter;
path.AddArc(arc, 270, 90);
// bottom right arc
arc.Y = bounds.Bottom - diameter;
path.AddArc(arc, 0, 90);
// bottom left arc
arc.X = bounds.Left;
path.AddArc(arc, 90, 90);
path.CloseFigure();
return path;
}
}
////////Custom Menu
public class CustomContextMenu : ContextMenuStrip
{
//Fields
private bool isMainMenu;
private int menuItemHeight = 20;
private int menuItemWidth = 20;
private Color menuItemTextColor = Color.Empty;
private Color primaryColor = Color.Empty;
private Color MouseOverColor = Color.Empty;
private Color MouseOverBorderColor = Color.Empty;
private WindowsTheme systrayTheme = WindowsTheme.Light;
private Bitmap menuItemHeaderSize;
//Constructor
public CustomContextMenu()
{
}
//Properties
[Browsable(false)]
public bool IsMainMenu
{
get { return isMainMenu; }
set { isMainMenu = value; }
}
[Browsable(false)]
public int MenuItemHeight
{
get { return menuItemHeight; }
set { menuItemHeight = value; }
}
[Browsable(false)]
public int MenuItemWidth
{
get { return menuItemWidth; }
set { menuItemWidth = value; }
}
[Browsable(false)]
public Color MenuItemTextColor
{
get { return menuItemTextColor; }
set { menuItemTextColor = value; }
}
[Browsable(false)]
public Color PrimaryColor
{
get { return primaryColor; }
set { primaryColor = value; }
}
[Browsable(false)]
public Color MenuItemMouseOverColor
{
get { return MouseOverColor; }
set { MouseOverColor = value; }
}
[Browsable(false)]
public Color MenuItemMouseOverBorderColor
{
get { return MouseOverBorderColor; }
set { MouseOverBorderColor = value; }
}
[Browsable(false)]
public WindowsTheme SystrayTheme
{
get { return systrayTheme; }
set { systrayTheme = value; }
}
//Private methods
private void LoadMenuItemHeight()
{
if (isMainMenu)
menuItemHeaderSize = new Bitmap(menuItemWidth, menuItemHeight);
else menuItemHeaderSize = new Bitmap(menuItemWidth-5, menuItemHeight);
foreach (Forms.ToolStripMenuItem menuItemL1 in this.Items)
{
menuItemL1.ImageScaling = ToolStripItemImageScaling.None;
if (menuItemL1.Image == null) menuItemL1.Image = menuItemHeaderSize;
foreach (Forms.ToolStripMenuItem menuItemL2 in menuItemL1.DropDownItems)
{
menuItemL2.ImageScaling = ToolStripItemImageScaling.None;
if (menuItemL2.Image == null) menuItemL2.Image = menuItemHeaderSize;
foreach (Forms.ToolStripMenuItem menuItemL3 in menuItemL2.DropDownItems)
{
menuItemL3.ImageScaling = ToolStripItemImageScaling.None;
if (menuItemL3.Image == null) menuItemL3.Image = menuItemHeaderSize;
foreach (Forms.ToolStripMenuItem menuItemL4 in menuItemL3.DropDownItems)
{
menuItemL4.ImageScaling = ToolStripItemImageScaling.None;
if (menuItemL4.Image == null) menuItemL4.Image = menuItemHeaderSize;
///Level 5++
}
}
}
}
}
//Overrides
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (this.DesignMode == false)
{
switch (SystrayTheme)
{
case WindowsTheme.Light:
{
menuItemTextColor = Color.Black;
}
break;
case WindowsTheme.Dark:
{
menuItemTextColor = Color.White;
}
break;
case WindowsTheme.HighContrast:
{
menuItemTextColor = Utility.ToDrawingColor(System.Windows.SystemColors.MenuTextColor);
}
break;
}
this.Renderer = new MenuRenderer(isMainMenu, primaryColor, menuItemTextColor, MouseOverColor, MouseOverBorderColor, SystrayTheme);
LoadMenuItemHeight();
}
}
}
Update:
Recently I was tried to override OnPaint() method. But the round corner is not smooth and also sub-menu is not rounded.
private GraphicsPath GetGraphicsPath(RectangleF rect,float radious)
{
GraphicsPath path = new GraphicsPath();
path.StartFigure();
path.AddArc(rect.X, rect.Y, radious, radious, 180, 90);
path.AddArc(rect.Width - radious, rect.Y, radious, radious, 270, 90);
path.AddArc(rect.Width - radious, rect.Height - radious, radious, radious, 0, 90);
path.AddArc(rect.X, rect.Height - radious, radious, radious, 90, 90);
path.CloseFigure();
return path;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
RectangleF rectSurface = new RectangleF(0, 0, this.Width, this.Height);
RectangleF rectBorder = new RectangleF(1, 1, this.Width - 0.8F, this.Height - 1);
if (borderRadius > 2) //Rounder
{
using (GraphicsPath pathSurface = GetGraphicsPath(rectSurface, borderRadius))
using (GraphicsPath pathBorder = GetGraphicsPath(rectBorder, borderRadius - 1F))
using (Pen penSurface = new Pen(this.BackColor, 2))
using (Pen penBorder = new Pen(borderColor, borderSize))
{
penBorder.Alignment = PenAlignment.Inset;
//Menu surface
this.Region = new Region(pathSurface);
//Draw surface border for HD result
e.Graphics.DrawPath(penSurface, pathSurface);
//Menu Border
if (borderSize >= 1)
e.Graphics.DrawPath(penBorder, pathBorder);
}
}
else //Normal contex menu
{
//Menu surface
this.Region = new Region(rectSurface);
//Menu border
if (borderSize >= 1)
{
using (Pen penBorder = new Pen(borderColor, borderSize))
{
penBorder.Alignment = PenAlignment.Inset;
e.Graphics.DrawRectangle(penBorder, 0, 0, this.Width - 1, this.Height - 1);
}
}
}
}
//Overrides
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (this.DesignMode == false)
{
//this.Parent.BackColorChanged += Parent_BackColorChanged;
switch (SystrayTheme)
{
case WindowsTheme.Light:
{
menuItemTextColor = Color.Black;
}
break;
case WindowsTheme.Dark:
{
menuItemTextColor = Color.White;
}
break;
case WindowsTheme.HighContrast:
{
menuItemTextColor = Utility.ToDrawingColor(System.Windows.SystemColors.MenuTextColor);
}
break;
}
this.RenderMode = ToolStripRenderMode.Professional;
this.Renderer = new MenuRenderer(isMainMenu, primaryColor, menuItemTextColor, MouseOverColor, MouseOverBorderColor, SystrayTheme);
LoadMenuItemHeight();
}
}
private void Parent_BackColorChanged(object sender, EventArgs e)
{
if (this.DesignMode)
this.Invalidate();
}
Thanks in Advance. Any help will be appreciated.
You can find the solution on this page: https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/apply-rounded-corners
I also include the solution here just in case the external link gets deleted.
// The enum flag for DwmSetWindowAttribute's second parameter, which tells the function what attribute to set.
public enum DWMWINDOWATTRIBUTE
{
DWMWA_WINDOW_CORNER_PREFERENCE = 33
}
// The DWM_WINDOW_CORNER_PREFERENCE enum for DwmSetWindowAttribute's third parameter, which tells the function
// what value of the enum to set.
public enum DWM_WINDOW_CORNER_PREFERENCE
{
DWMWCP_DEFAULT = 0,
DWMWCP_DONOTROUND = 1,
DWMWCP_ROUND = 2,
DWMWCP_ROUNDSMALL = 3
}
// Import dwmapi.dll and define DwmSetWindowAttribute in C# corresponding to the native function.
[DllImport("dwmapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern long DwmSetWindowAttribute(IntPtr hwnd,
DWMWINDOWATTRIBUTE attribute,
ref DWM_WINDOW_CORNER_PREFERENCE pvAttribute,
uint cbAttribute);
And to apply the rounded corners to the ContextMenuStrip:
ContextMenuStrip notifyIconMenu = new ContextMenuStrip();
...
var attribute = DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE;
var preference = DWM_WINDOW_CORNER_PREFERENCE.DWMWCP_ROUND;
DwmSetWindowAttribute(notifyIconMenu.Handle, attribute, ref preference, sizeof(uint));
UPD
Here is the implementation of the submenu:
ToolStripMenuItem submenu = new ToolStripMenuItem("submenu");
notifyIconMenu.Items.Add(submenu);
ToolStripMenuItem submenuItem1 = new ToolStripMenuItem("submenu1");
submenu.DropDownItems.Add(submenuItem1);
DwmSetWindowAttribute(submenu.DropDown.Handle, attribute, ref preference, sizeof(uint));
In case you want to go the proprietary implementation route for rounded corners, here's a method for creating "smooth" ones:
Graphics graphics = ...;
...
graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
Did you try to set different RendererMode in CustomContextMenu other than the default one?
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
if (this.DesignMode == false)
{
switch (SystrayTheme)
{
case WindowsTheme.Light:
{
menuItemTextColor = Color.Black;
}
break;
case WindowsTheme.Dark:
{
menuItemTextColor = Color.White;
}
break;
case WindowsTheme.HighContrast:
{
menuItemTextColor = Utility.ToDrawingColor(System.Windows.SystemColors.MenuTextColor);
}
break;
}
this.RenderMode = ToolStripRenderMode.Professional;
this.Renderer = new MenuRenderer(isMainMenu, primaryColor, menuItemTextColor, MouseOverColor, MouseOverBorderColor, SystrayTheme);
LoadMenuItemHeight();
}
}

Laggy snipping tool

I'm trying to create snipping tool like this one https://app.prntscr.com/en/index.html. I'm able to modify some codes and managed to show resize handles with selected rectangle(snip), handles work fine and i can move the selected rectangle and resize it. But when I compare it with tool mentioned above it is very slow, can someone point some problems in my code to make it fast?
The tool i'm making look like this:
/*
* Credit for this portion of Imgur Snipping Tool goes to Hans Passant from stackoverflow.com
* http://stackoverflow.com/questions/3123776/net-equivalent-of-snipping-tool
*
* Modified to work with multiple monitors.
*/
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using Free_Snipping_Tool.Classes;
namespace Free_Snipping_Tool
{
public partial class SnippingTool2 : Form
{
public enum ScreenshotType
{
FULL, ACTIVE, DEFINED
}
private int oldX;
private int oldY;
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
Rectangle topLeft = new Rectangle();
Rectangle topMiddle = new Rectangle();
Rectangle topRight = new Rectangle();
Rectangle bottomLeft = new Rectangle();
Rectangle bottomMiddle = new Rectangle();
Rectangle bottomRight = new Rectangle();
Rectangle leftMiddle = new Rectangle();
Rectangle rightMiddle = new Rectangle();
bool topLeftSelected = false;
bool topMiddleSelected = false;
bool topRightSelected = false;
bool bottomLeftSelected = false;
bool bottomMiddleSelected = false;
bool bottomRightSelected = false;
bool leftMiddleSelected = false;
bool rightMiddleSelected = false;
bool rectangleSelected = false;
[DllImport("user32.dll")]
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("user32.dll")]
private static extern int GetForegroundWindow();
SnipSizeControl lbl = new SnipSizeControl();
floatingControlRight fcR = new floatingControlRight();
floatingControlBottom fcB = new floatingControlBottom();
public static Image Snip(ScreenshotType type)
{
switch (type)
{
case ScreenshotType.FULL:
return CreateScreenshot(0, 0, SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height);
case ScreenshotType.DEFINED:
SnippingTool2 snipper = new SnippingTool2(CreateScreenshot(0, 0, SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height), new Point(SystemInformation.VirtualScreen.Left, SystemInformation.VirtualScreen.Top));
if (snipper.ShowDialog() == DialogResult.OK)
{
return snipper.Image;
}
break;
case ScreenshotType.ACTIVE:
RECT windowRectangle;
GetWindowRect((System.IntPtr)GetForegroundWindow(), out windowRectangle);
return CreateScreenshot(windowRectangle.Left, windowRectangle.Top, windowRectangle.Right - windowRectangle.Left, windowRectangle.Bottom - windowRectangle.Top);
}
return null;
}
private static Bitmap CreateScreenshot(int left, int top, int width, int height)
{
Bitmap bmp = new Bitmap(width, height);
Graphics g = Graphics.FromImage(bmp);
g.CopyFromScreen(left, top, 0, 0, new Size(width, height));
g.Dispose();
return bmp;
}
public SnippingTool2(Image screenShot, Point startPos)
{
InitializeComponent();
lbl.Text = "0x0";
lbl.Visible = true;
lbl.BackColor = Color.Black;
lbl.ForeColor = Color.White;
lbl.Visible = false;
this.Controls.Add(lbl);
this.BackgroundImage = screenShot;
Size s = screenShot.Size;
this.ShowInTaskbar = false;
this.FormBorderStyle = FormBorderStyle.None;
this.StartPosition = FormStartPosition.Manual;
this.Size = s;
this.Location = startPos;
this.DoubleBuffered = true;
}
public Image Image { get; set; }
private Rectangle rcSelect = new Rectangle();
private Point pntStart;
bool handleSelected = false;
protected override void OnMouseDown(MouseEventArgs e)
{
if ((e.X > topLeft.X) && (e.X < topLeft.X + topLeft.Width) && (e.Y > topLeft.Y) && (e.Y < topLeft.Y + topLeft.Height))
{
this.Cursor = Cursors.SizeNWSE;
topLeftSelected = true;
handleSelected = true;
}
else if ((e.X > topMiddle.X) && (e.X < topMiddle.X + topMiddle.Width) && (e.Y > topMiddle.Y) && (e.Y < topMiddle.Y + topMiddle.Height))
{
this.Cursor = Cursors.SizeNS;
topMiddleSelected = true;
handleSelected = true;
}
else if ((e.X > topRight.X) && (e.X < topRight.X + topRight.Width) && (e.Y > topRight.Y) && (e.Y < topRight.Y + topRight.Height))
{
this.Cursor = Cursors.SizeNESW;
topRightSelected = true;
handleSelected = true;
}
else if ((e.X > bottomLeft.X) && (e.X < bottomLeft.X + bottomLeft.Width) && (e.Y > bottomLeft.Y) && (e.Y < bottomLeft.Y + bottomLeft.Height))
{
this.Cursor = Cursors.SizeNESW;
bottomLeftSelected = true;
handleSelected = true;
}
else if ((e.X > bottomMiddle.X) && (e.X < bottomMiddle.X + bottomMiddle.Width) && (e.Y > bottomMiddle.Y) && (e.Y < bottomMiddle.Y + bottomMiddle.Height))
{
this.Cursor = Cursors.SizeNS;
bottomMiddleSelected = true;
handleSelected = true;
}
else if ((e.X > bottomRight.X) && (e.X < bottomRight.X + bottomRight.Width) && (e.Y > bottomRight.Y) && (e.Y < bottomRight.Y + bottomRight.Height))
{
this.Cursor = Cursors.SizeNWSE;
bottomRightSelected = true;
handleSelected = true;
}
else if ((e.X > leftMiddle.X) && (e.X < leftMiddle.X + leftMiddle.Width) && (e.Y > leftMiddle.Y) && (e.Y < leftMiddle.Y + leftMiddle.Height))
{
this.Cursor = Cursors.SizeWE;
leftMiddleSelected = true;
handleSelected = true;
}
else if ((e.X > rightMiddle.X) && (e.X < rightMiddle.X + rightMiddle.Width) && (e.Y > rightMiddle.Y) && (e.Y < rightMiddle.Y + rightMiddle.Height))
{
this.Cursor = Cursors.SizeWE;
rightMiddleSelected = true;
handleSelected = true;
}
else if ((e.X > rcSelect.X) && (e.X < rcSelect.X + rcSelect.Width) && (e.Y > rcSelect.Y) && (e.Y < rcSelect.Y + rcSelect.Height))
{
this.Cursor = Cursors.SizeAll;
rectangleSelected = true;
handleSelected = true;
}
else
{
this.Cursor = Cursors.Cross;
topLeftSelected = false;
topMiddleSelected = false;
topRightSelected = false;
bottomLeftSelected = false;
bottomMiddleSelected = false;
bottomRightSelected = false;
leftMiddleSelected = false;
rightMiddleSelected = false;
rectangleSelected = false;
handleSelected = false;
}
// Start the snip on mouse down
if (e.Button != MouseButtons.Left) return;
if (handleSelected == true) return;
pntStart = e.Location;
rcSelect = new Rectangle(e.Location, new Size(0, 0));
lbl.Visible = true;
lbl.Location = new Point(e.X + 10, e.Y + 10); ;
lbl.Value = string.Format("0x0");
lbl.Refresh();
fcR.Visible = false;
oldX = e.X;
oldY = e.Y;
this.Refresh();
}
protected override CreateParams CreateParams
{
get
{
var cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
// Modify the selection on mouse move
if ((e.X > topLeft.X) && (e.X < topLeft.X + topLeft.Width) && (e.Y > topLeft.Y) && (e.Y < topLeft.Y + topLeft.Height))
{
this.Cursor = Cursors.SizeNWSE;
}
else if ((e.X > topMiddle.X) && (e.X < topMiddle.X + topMiddle.Width) && (e.Y > topMiddle.Y) && (e.Y < topMiddle.Y + topMiddle.Height))
{
this.Cursor = Cursors.SizeNS;
}
else if ((e.X > topRight.X) && (e.X < topRight.X + topRight.Width) && (e.Y > topRight.Y) && (e.Y < topRight.Y + topRight.Height))
{
this.Cursor = Cursors.SizeNESW;
}
else if ((e.X > bottomLeft.X) && (e.X < bottomLeft.X + bottomLeft.Width) && (e.Y > bottomLeft.Y) && (e.Y < bottomLeft.Y + bottomLeft.Height))
{
this.Cursor = Cursors.SizeNESW;
}
else if ((e.X > bottomMiddle.X) && (e.X < bottomMiddle.X + bottomMiddle.Width) && (e.Y > bottomMiddle.Y) && (e.Y < bottomMiddle.Y + bottomMiddle.Height))
{
this.Cursor = Cursors.SizeNS;
}
else if ((e.X > bottomRight.X) && (e.X < bottomRight.X + bottomRight.Width) && (e.Y > bottomRight.Y) && (e.Y < bottomRight.Y + bottomRight.Height))
{
this.Cursor = Cursors.SizeNWSE;
}
else if ((e.X > leftMiddle.X) && (e.X < leftMiddle.X + leftMiddle.Width) && (e.Y > leftMiddle.Y) && (e.Y < leftMiddle.Y + leftMiddle.Height))
{
this.Cursor = Cursors.SizeWE;
}
else if ((e.X > rightMiddle.X) && (e.X < rightMiddle.X + rightMiddle.Width) && (e.Y > rightMiddle.Y) && (e.Y < rightMiddle.Y + rightMiddle.Height))
{
this.Cursor = Cursors.SizeWE;
}
else if ((e.X > rcSelect.X) && (e.X < rcSelect.X + rcSelect.Width) && (e.Y > rcSelect.Y) && (e.Y < rcSelect.Y + rcSelect.Height))
{
this.Cursor = Cursors.SizeAll;
}
else
{
this.Cursor = Cursors.Cross;
}
if (e.Button == MouseButtons.Left && handleSelected == true)
{
Rectangle backupRect = rcSelect;
if (topLeftSelected == true)
{
rcSelect.X += e.X - oldX;
rcSelect.Width -= e.X - oldX;
rcSelect.Y += e.Y - oldY;
rcSelect.Height -= e.Y - oldY;
lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
this.Refresh();
}
else if (topRightSelected == true)
{
rcSelect.Width += e.X - oldX;
rcSelect.Y += e.Y - oldY;
rcSelect.Height -= e.Y - oldY;
lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
this.Refresh();
}
else if (topMiddleSelected == true)
{
rcSelect.Y += e.Y - oldY;
rcSelect.Height -= e.Y - oldY;
lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
this.Refresh();
}
else if (bottomLeftSelected == true)
{
rcSelect.Width -= e.X - oldX;
rcSelect.X += e.X - oldX;
rcSelect.Height += e.Y - oldY;
lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
this.Refresh();
}
else if (bottomMiddleSelected == true)
{
rcSelect.Height += e.Y - oldY;
lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
this.Refresh();
}
else if (bottomRightSelected == true)
{
rcSelect.Width += e.X - oldX;
rcSelect.Height += e.Y - oldY;
lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
this.Refresh();
}
else if (rightMiddleSelected == true)
{
rcSelect.Width += e.X - oldX;
lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
this.Refresh();
}
else if (leftMiddleSelected == true)
{
rcSelect.X += e.X - oldX;
rcSelect.Width -= e.X - oldX;
lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
this.Refresh();
}
else if (rectangleSelected == true)
{
rcSelect.X = rcSelect.X + e.X - oldX;
rcSelect.Y = rcSelect.Y + e.Y - oldY;
lbl.Location = new Point(rcSelect.X, rcSelect.Y - 25);
lbl.Value = string.Format("{0}x{1}", rcSelect.Width, rcSelect.Height);
this.Refresh();
}
}
else if (e.Button == MouseButtons.Left)
{
int x1 = Math.Min(e.X, pntStart.X);
int y1 = Math.Min(e.Y, pntStart.Y);
int x2 = Math.Max(e.X, pntStart.X);
int y2 = Math.Max(e.Y, pntStart.Y);
rcSelect = new Rectangle(x1, y1, x2 - x1, y2 - y1);
this.Refresh();
lbl.Location = new Point(x1, y1 - 25);
lbl.Value = string.Format("{0}x{1}", x2 - x1, y2 - y1);
lbl.Refresh();
}
oldX = e.X;
oldY = e.Y;
}
protected override void OnMouseUp(MouseEventArgs e)
{
// Complete the snip on mouse-up
if (rcSelect.Width <= 0 || rcSelect.Height <= 0) return;
Image = new Bitmap(rcSelect.Width, rcSelect.Height);
using (Graphics gr = Graphics.FromImage(Image))
{
gr.DrawImage(this.BackgroundImage, new Rectangle(0, 0, Image.Width, Image.Height),
rcSelect, GraphicsUnit.Pixel);
}
topLeftSelected = false;
topMiddleSelected = false;
topRightSelected = false;
bottomLeftSelected = false;
bottomMiddleSelected = false;
bottomRightSelected = false;
leftMiddleSelected = false;
rightMiddleSelected = false;
rectangleSelected = false;
handleSelected = false;
//DialogResult = DialogResult.OK;
}
protected override void OnPaint(PaintEventArgs e)
{
// Draw the current selection
using (Brush br = new SolidBrush(Color.FromArgb(30, Color.Black)))
{
int x1 = rcSelect.X; int x2 = rcSelect.X + rcSelect.Width;
int y1 = rcSelect.Y; int y2 = rcSelect.Y + rcSelect.Height;
e.Graphics.FillRectangle(br, new Rectangle(0, 0, x1, this.Height));
e.Graphics.FillRectangle(br, new Rectangle(x2, 0, this.Width - x2, this.Height));
e.Graphics.FillRectangle(br, new Rectangle(x1, 0, x2 - x1, y1));
e.Graphics.FillRectangle(br, new Rectangle(x1, y2, x2 - x1, this.Height - y2));
}
using (Pen pen = new Pen(Color.Red, 1))
{
e.Graphics.DrawRectangle(pen, rcSelect);
}
//Resize Controls
//Top left
topLeft = new Rectangle(rcSelect.X - 3, rcSelect.Y - 3, 6, 6);
using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
{
e.Graphics.FillRectangle(br, topLeft);
}
//Top middle
topMiddle = new Rectangle((rcSelect.X - 3) + (rcSelect.Width) / 2, rcSelect.Y - 3, 6, 6);
using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
{
e.Graphics.FillRectangle(br, topMiddle);
}
//Top right
topRight = new Rectangle((rcSelect.X - 3) + rcSelect.Width, rcSelect.Y - 3, 6, 6);
using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
{
e.Graphics.FillRectangle(br, topRight);
}
//Bottom left
bottomLeft = new Rectangle(rcSelect.X - 3, (rcSelect.Y - 3) + rcSelect.Height, 6, 6);
using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
{
e.Graphics.FillRectangle(br, bottomLeft);
}
//Bottom middle
bottomMiddle = new Rectangle((rcSelect.X - 3) + (rcSelect.Width) / 2, (rcSelect.Y - 3) + rcSelect.Height, 6, 6);
using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
{
e.Graphics.FillRectangle(br, bottomMiddle);
}
//Bottom right
bottomRight = new Rectangle((rcSelect.X - 3) + rcSelect.Width, (rcSelect.Y - 3) + rcSelect.Height, 6, 6);
using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
{
e.Graphics.FillRectangle(br, bottomRight);
}
//Left middle
leftMiddle = new Rectangle(rcSelect.X - 3, (rcSelect.Y - 3) + (rcSelect.Height) / 2, 6, 6);
using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
{
e.Graphics.FillRectangle(br, leftMiddle);
}
//Right middle
rightMiddle = new Rectangle((rcSelect.X - 3) + rcSelect.Width, (rcSelect.Y - 3) + (rcSelect.Height) / 2, 6, 6);
using (Brush br = new SolidBrush(Color.FromArgb(200, Color.Red)))
{
e.Graphics.FillRectangle(br, rightMiddle);
}
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
// Allow canceling the snip with the Escape key
if (keyData == Keys.Escape) this.DialogResult = DialogResult.Cancel;
return base.ProcessCmdKey(ref msg, keyData);
}
}
}
Edit:
Project file: https://www.dropbox.com/s/k1afggj6inye4kp/Free%20Snipipng%20Tool-project.rar?dl=0
I will cite the official Windows documentation. Although the doc is about Native API, Winforms uses the same underlying technology:
An application invalidates a portion of a window and sets the update
region by using the InvalidateRect or InvalidateRgn function. These
functions add the specified rectangle or region to the update region,
combining the rectangle or region with anything the system or the
application may have previously added to the update region.
The InvalidateRect and InvalidateRgn functions do not generate
WM_PAINT messages. Instead, the system accumulates the changes made by
these functions and its own changes. By accumulating changes, a window
processes all changes at once instead of updating bits and pieces one
step at a time.
Calling .NET Refresh() is equivalent of calling InvalidateAll() + Update(). InvalidateAll marks the whole screen as invalid, and Update() forces the process of redrawing what's invalid, so the whole screen.
You can optimize your program if you just invalidate "manually" what you know has changed.
That's what I did in my modified sample. Instead of calling Refresh(), I added a new oldRcRect variable to be able to invalidate old state and new state, and a RefreshOnMove() method like this (and I replaced most Refresh calls by a RefreshOnMove call) :
private void RefreshOnMove()
{
// invalidate the old rect (+ size of red box)
var rc = oldRcSelect;
rc.Inflate(3, 3);
Invalidate(rc);
// invalidate the new rect (+ size of red box)
// note you can almost omit this second one, but if you move the mouse really fast, you'll see some red box not fully displayed
// but the benefit is small, something like a 3 x width/height rectangle
rc = rcSelect;
rc.Inflate(3, 3);
Invalidate(rc);
// each time you call invalidate, you just accumulate a change
// to the change region, nothing actually changes on the screen
// now, ask Windows to process the combination of changes
Update();
}
PS: about my comment about inner region, I just mean it may be possible to avoid invalidating the content of the white box each time as well, but it's more complex.

Free-form selection crop C#

How can I make a free-form selection (like in paint or photoshop) in a picture box and then crop that selection and save it to a folder?
I already did a rectangle crop but I want that free-form selection..
Here is my rectangle crop:
Image img;
bool mouseClicked;
Point startPoint = new Point();
Point endPoint = new Point();
Rectangle rectCropArea;
private void Button1_Click(System.Object sender, System.EventArgs e)
{
}
private void OnLoad(System.Object sender, System.EventArgs e)
{
loadPrimaryImage();
}
private void loadPrimaryImage()
{
img = Image.FromFile("..\\..\\images.jpg");
PictureBox1.Image = img;
}
private void PicBox_MouseUp(System.Object sender, System.Windows.Forms.MouseEventArgs e)
{
mouseClicked = false;
if ((endPoint.X != -1)) {
Point currentPoint = new Point(e.X, e.Y);
Y1.Text = e.X.ToString();
Y2.Text = e.Y.ToString();
}
endPoint.X = -1;
endPoint.Y = -1;
startPoint.X = -1;
startPoint.Y = -1;
}
private void PicBox_MouseDown(System.Object sender, System.Windows.Forms.MouseEventArgs e)
{
mouseClicked = true;
startPoint.X = e.X;
startPoint.Y = e.Y;
//Display coordinates
X1.Text = startPoint.X.ToString();
Y1.Text = startPoint.Y.ToString();
endPoint.X = -1;
endPoint.Y = -1;
rectCropArea = new Rectangle(new Point(e.X, e.Y), new Size());
}
private void PicBox_MouseMove(System.Object sender, System.Windows.Forms.MouseEventArgs e)
{
Point ptCurrent = new Point(e.X, e.Y);
if ((mouseClicked)) {
if ((endPoint.X != -1)) {
//Display Coordinates
X1.Text = startPoint.X.ToString();
Y1.Text = startPoint.Y.ToString();
X2.Text = e.X.ToString();
Y2.Text = e.Y.ToString();
}
endPoint = ptCurrent;
if ((e.X > startPoint.X & e.Y > startPoint.Y)) {
rectCropArea.Width = e.X - startPoint.X;
rectCropArea.Height = e.Y - startPoint.Y;
} else if ((e.X < startPoint.X & e.Y > startPoint.Y)) {
rectCropArea.Width = startPoint.X - e.X;
rectCropArea.Height = e.Y - startPoint.Y;
rectCropArea.X = e.X;
rectCropArea.Y = startPoint.Y;
} else if ((e.X > startPoint.X & e.Y < startPoint.Y)) {
rectCropArea.Width = e.X - startPoint.X;
rectCropArea.Height = startPoint.Y - e.Y;
rectCropArea.X = startPoint.X;
rectCropArea.Y = e.Y;
} else {
rectCropArea.Width = startPoint.X - e.X;
rectCropArea.Height = startPoint.Y - e.Y;
rectCropArea.X = e.X;
rectCropArea.Y = e.Y;
}
PictureBox1.Refresh();
}
}
private void PicBox_Paint(System.Object sender, System.Windows.Forms.PaintEventArgs e)
{
Pen drawLine = new Pen(Color.Red);
drawLine.DashStyle = DashStyle.Dash;
e.Graphics.DrawRectangle(drawLine, rectCropArea);
}
private void btnCrop_Click(System.Object sender, System.EventArgs e)
{
PictureBox2.Refresh();
Bitmap sourceBitmap = new Bitmap(PictureBox1.Image, PictureBox1.Width, PictureBox1.Height);
Graphics g = PictureBox2.CreateGraphics();
if (!(CheckBox1.Checked)) {
g.DrawImage(sourceBitmap, new Rectangle(0, 0, PictureBox2.Width, PictureBox2.Height), rectCropArea, GraphicsUnit.Pixel);
sourceBitmap.Dispose();
} else {
int x1 = 0;
int x2 = 0;
int y1 = 0;
int y2 = 0;
try {
x1 = Convert.ToInt32(CX1.Text);
x2 = Convert.ToInt32(CX2.Text);
y1 = Convert.ToInt32(CY1.Text);
y2 = Convert.ToInt32(CY2.Text);
} catch (Exception ex) {
MessageBox.Show("Enter valid Coordinates (only Integer values)");
}
if (((x1 < x2 & y1 < y2))) {
rectCropArea = new Rectangle(x1, y1, x2 - x1, y2 - y1);
} else if ((x2 < x1 & y2 > y1)) {
rectCropArea = new Rectangle(x2, y1, x1 - x2, y2 - y1);
} else if ((x2 > x1 & y2 < y1)) {
rectCropArea = new Rectangle(x1, y2, x2 - x1, y1 - y2);
} else {
rectCropArea = new Rectangle(x2, y2, x1 - x2, y1 - y2);
}
PictureBox1.Refresh();
//This repositions the dashed box to new location as per coordinates entered.
g.DrawImage(sourceBitmap, new Rectangle(0, 0, PictureBox2.Width, PictureBox2.Height), rectCropArea, GraphicsUnit.Pixel);
sourceBitmap.Dispose();
}
}
private void pictureBox1_MouseClick(System.Object sender, System.Windows.Forms.MouseEventArgs e)
{
PictureBox1.Refresh();
}
private void CheckBox1_CheckedChanged(System.Object sender, System.EventArgs e)
{
if ((CheckBox1.Checked)) {
CX1.Visible = true;
Label10.Visible = true;
CY1.Visible = true;
Label9.Visible = true;
CX2.Visible = true;
Label8.Visible = true;
CY2.Visible = true;
Label7.Visible = true;
X1.Text = "0";
X2.Text = "0";
Y1.Text = "0";
Y2.Text = "0";
} else {
CX1.Visible = false;
Label10.Visible = false;
CY1.Visible = false;
Label9.Visible = false;
CX2.Visible = false;
Label8.Visible = false;
CY2.Visible = false;
Label7.Visible = false;
}
}
public Form1()
{
Load += OnLoad;
}
}
To copy a free-form selection you need to work with polygons.
Here is a complete example. Just paste this into a new solution and try it out (just change the path to the images).
It will create 2 pictureboxes and load an image into the first one and also create an image. Then you can click on the first image and when you have clicked 2 times it will start to show a selection, when you are finished just press the button and it will copy the selection to the other pictureBox and then save it as an png image.
What it does is to create a brush from the first image and then paint the polygon onto another image and set the other pixels in the rectangle to a background color of your choice, in this case the color: Color.Transparent.
Example:
public partial class Form1 : Form {
private List<Point> _points = new List<Point>();
private PictureBox _pictureBox1;
private PictureBox _pictureBox2;
private Button _button1;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
Size = new Size(1366, 675);
_pictureBox1 = new PictureBox {
Location = new Point(12, 51),
Size = new Size(651, 474),
BorderStyle = BorderStyle.FixedSingle
};
_pictureBox2 = new PictureBox
{
Location = new Point(669, 51),
Size = new Size(651, 474),
BorderStyle = BorderStyle.FixedSingle
};
_button1 = new Button {
Text = #"Copy selected area",
Location = new Point(13, 13),
Size = new Size(175, 23)
};
Controls.AddRange(new Control[] { _pictureBox1, _pictureBox2, _button1 });
_pictureBox1.Image = Image.FromFile(#"d:\temp\Hopetoun_falls.jpg");
_points = new List<Point>();
_pictureBox1.MouseDown += delegate(object o, MouseEventArgs args) { _points.Add(args.Location); _pictureBox1.Refresh(); };
_pictureBox1.Paint += pictureBox1_Paint;
_button1.Click += button_Click;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e) {
if (_points.Count < 2) {
return;
}
var max = _points.Count;
for (int i = 1; i < max; i++) {
e.Graphics.DrawLine(Pens.Red, _points[i-1].X, _points[i-1].Y, _points[i].X, _points[i].Y);
}
e.Graphics.DrawLine(Pens.Red, _points[max - 1].X, _points[max - 1].Y, _points[0].X, _points[0].Y);
}
private static Bitmap GetSelectedArea(Image source, Color bgColor, List<Point> points) {
var bigBm = new Bitmap(source);
using (var gr = Graphics.FromImage(bigBm)) {
// Set the background color.
gr.Clear(bgColor);
// Make a brush out of the original image.
using (var br = new TextureBrush(source)) {
// Fill the selected area with the brush.
gr.FillPolygon(br, points.ToArray());
// Find the bounds of the selected area.
var sourceRect = GetPointListBounds(points);
// Make a bitmap that only holds the selected area.
var result = new Bitmap(sourceRect.Width, sourceRect.Height);
// Copy the selected area to the result bitmap.
using (var resultGr = Graphics.FromImage(result)) {
var destRect = new Rectangle(0, 0, sourceRect.Width, sourceRect.Height);
resultGr.DrawImage(bigBm, destRect, sourceRect, GraphicsUnit.Pixel);
}
// Return the result.
return result;
}
}
}
private static Rectangle GetPointListBounds(List<Point> points) {
int xmin = points[0].X;
int xmax = xmin;
int ymin = points[0].Y;
int ymax = ymin;
for (int i = 1; i < points.Count; i++) {
if (xmin > points[i].X) xmin = points[i].X;
if (xmax < points[i].X) xmax = points[i].X;
if (ymin > points[i].Y) ymin = points[i].Y;
if (ymax < points[i].Y) ymax = points[i].Y;
}
return new Rectangle(xmin, ymin, xmax - xmin, ymax - ymin);
}
private void button_Click(object sender, EventArgs e) {
if (_points.Count < 3) {
return;
}
var img = GetSelectedArea(_pictureBox1.Image, Color.Transparent, _points);
_pictureBox2.Image = img;
_pictureBox2.Image.Save(#"d:\temp\sample.png", ImageFormat.Png);
}
}

Picturebox zooming works on preloaded image but not graphics (C#/Winforms)

I have a picturebox that I want the user of my winform to be able to interact with by zooming and dragging the image. Right now that form looks like this
I have the drag and zoom methods implemented. Those look like this.
private void pictureBox1_MouseWheel(object sender, MouseEventArgs e)
{
// If the mouse wheel is moved forward (Zoom in)
if (e.Delta > 0)
{
// Change the size of the picturebox, multiply it by the ZOOMFACTOR
pictureBox1.Width = (int)(pictureBox1.Width * 1.25);
pictureBox1.Height = (int)(pictureBox1.Height * 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
pictureBox1.Top = (int)(e.Y - 1.25 * (e.Y - pictureBox1.Top));
pictureBox1.Left = (int)(e.X - 1.25 * (e.X - pictureBox1.Left));
}
else
{
pictureBox1.Width = (int)(pictureBox1.Width / 1.25);
pictureBox1.Height = (int)(pictureBox1.Height / 1.25);
// Formula to move the picturebox, to zoom in the point selected by the mouse cursor
pictureBox1.Top = (int)(e.Y - 0.80 * (e.Y - pictureBox1.Top));
pictureBox1.Left = (int)(e.X - 0.80 * (e.X - pictureBox1.Left));
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
dragging = false;
Console.WriteLine("Dragging: false");
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
dragging = true;
xDrag = e.X;
yDrag = e.Y;
Console.WriteLine("Dragging: true");
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
Control c = sender as Control;
if (dragging && c != null)
{
c.Top = e.Y + c.Top - yDrag;
c.Left = e.X + c.Left - xDrag;
Console.WriteLine(e.Location.ToString());
}
}
The dragging works fine but the zooming does not work as expected. Instead of zooming in it pushes the image up and to the left as I "zoom in"
Out of curiousity I loaded a bitmap I saved off Google in to test the zoom and it works fine with bitmap images from files; it just doesn't work with images drawn with the Graphics object and I have no idea why. Any help would be greatly appreciated.
Paint event code for picturebox
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
RunEntry entry = this.passedHistory.SelectedItem as RunEntry;
if (entry == null)
{
return;
}
else if(entry.FileRead != null && checkBox1.Checked) //If ANS is selected and show all, get TRE
{
foreach(RunEntry r in passedHistory.Items)
{
if (r.TreComponentRead != null)
{
string ansName = Path.GetFileNameWithoutExtension(entry.FileName);
string treName = Path.GetFileNameWithoutExtension(r.FileName);
if(ansName.Equals(treName, StringComparison.OrdinalIgnoreCase))
{
entry.TreComponentRead = r.TreComponentRead;
}
}
}
}
if (isDraw && entry.FileRead != null)
{
//Preparing to draw
Graphics g = e.Graphics;
g.ScaleTransform(2f, 2f);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.Bicubic;
AnsFile objToDraw = entry.FileRead;
Pen pen = new Pen(Color.Black);
//Getting size of bitmap
int maxWidth = 0, maxHeight = 0;
foreach (AnsJoint joint in objToDraw.AnsJoints)
{
if (joint.Location.X.Length > maxWidth)
{
maxWidth = (int)joint.Location.X.Length;
}
if (joint.Location.Y.Length > maxHeight)
{
maxHeight = (int)joint.Location.Y.Length;
}
}
//Drawing joints //TODO: (Trello: Improve math behind visualizer)
foreach (AnsJoint joint in objToDraw.AnsJoints)
{
PointF jointPoint = this.ToCartesian(new PointF((float)joint.Location.X.Length - 4f, (float)joint.Location.Y.Length + 10f), maxHeight);
e.Graphics.DrawString(joint.JointID.ToString(), new Font(FontFamily.GenericMonospace, 6f, FontStyle.Regular, GraphicsUnit.Point, 1, false), Brushes.Black, jointPoint);
}
//Draw the panels and links //TODO: (Trello: Improve math behind visualizer)
foreach (AnsMember member in objToDraw.AnsMembers)
{
List<AnsPanel> panels = member.Panels; //Drawing the panels
foreach (AnsPanel pan in panels)
{
pen.Color = Color.Red;
PointF p1 = this.ToCartesian(new PointF((float)pan.I.Location.X.Length, (float)pan.I.Location.Y.Length), maxHeight);
PointF p2 = this.ToCartesian(new PointF((float)pan.J.Location.X.Length, (float)pan.J.Location.Y.Length), maxHeight);
g.DrawEllipse(pen, p1.X - 2.5f, p1.Y - 2.5f, 5, 5);
g.DrawEllipse(pen, p2.X - 2.5f, p2.Y - 2.5f, 5, 5);
g.DrawEllipse(pen, p1.X - 3, p1.Y - 3.3f, 5, 5);
g.DrawEllipse(pen, p2.X - 3, p2.Y - 3.3f, 5, 5);
pen.Color = Color.Black;
g.DrawLine(pen, p1, p2);
}
List<AnsLink> links = member.Links; //Drawing the links
foreach (AnsLink link in links)
{
PointF p1 = this.ToCartesian(new PointF((float)link.I.Location.X.Length, (float)link.I.Location.Y.Length), maxHeight);
PointF p2 = this.ToCartesian(new PointF((float)link.J.Location.X.Length, (float)link.J.Location.Y.Length), maxHeight);
g.FillEllipse(Brushes.Green, p1.X - 1.5f, p1.Y - 1.5f, 3, 3);
g.FillEllipse(Brushes.Green, p2.X - 1.5f, p2.Y - 1.5f, 3, 3);
g.DrawLine(pen, p1, p2);
}
}
pictureBox1.Tag = entry.FileName;
}
if (isDraw && entry.TreComponentRead != null)
{
//Preparing to draw
Graphics g = e.Graphics;
g.ScaleTransform(2f, 2f);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.Bicubic;
TreComponent objToDraw = entry.TreComponentRead;
Pen pen = new Pen(Color.Black);
int maxWidth = 0, maxHeight = 0;
foreach (Member member in objToDraw.Members)
{
foreach (GeometryClassLibrary.Point p in member.Geometry.Vertices)
{
if (p.X.Inches > maxWidth)
{
maxWidth = (int)p.X.Inches;
}
if (p.Y.Inches > maxHeight)
{
maxHeight = (int)p.Y.Inches;
}
}
}
maxHeight += 5; maxWidth += 5;
maxHeight += 15; maxWidth += 5;
foreach (Member member in objToDraw.Members)
{
List<PointF> pointsToDraw = new List<PointF>();
foreach (GeometryClassLibrary.Point p in member.Geometry.Vertices)
{
pointsToDraw.Add(ToCartesian(new PointF((float)p.X.Inches, (float)p.Y.Inches), maxHeight));
PointF pointToDraw = this.ToCartesian(new PointF((float)p.X.Inches, (float)p.Y.Inches), maxHeight);
g.FillEllipse(Brushes.Red, pointToDraw.X - 1.5f, pointToDraw.Y - 1.5f, 3, 3);
pointsToDraw.Add(pointToDraw);
}
g.DrawPolygon(pen, pointsToDraw.ToArray());
//Getting center of member and labeling member
float totalX = 0, totalY = 0;
foreach (PointF p in pointsToDraw)
{
totalX += p.X;
totalY += p.Y;
}
float centerX = totalX / pointsToDraw.Count;
float centerY = totalY / pointsToDraw.Count - 10;
PointF midPoint = new PointF(centerX, centerY);
g.DrawString(member.Name, new Font(FontFamily.GenericMonospace, 6f, FontStyle.Regular, GraphicsUnit.Point, 1, false), Brushes.Black, midPoint);
}
pictureBox1.Tag = entry.FileName;
}
}

Graphic - DrawLine - draw line and move it

In my .net c# program i draw few lines using values from text boxes (i use DrawLine function). I want to be able to move one of this lines by clik on it and move this line with mouse - is it possible?
public class LineMover : Form
{
public LineMover()
{
this.DoubleBuffered = true;
this.Paint += new PaintEventHandler(LineMover_Paint);
this.MouseMove += new MouseEventHandler(LineMover_MouseMove);
this.MouseDown += new MouseEventHandler(LineMover_MouseDown);
this.MouseUp += new MouseEventHandler(LineMover_MouseUp);
this.Lines = new List<GraphLine>()
{
new GraphLine (10, 10, 100, 200),
new GraphLine (10, 150, 120, 40),
};
}
void LineMover_MouseUp(object sender, MouseEventArgs e)
{
if (Moving != null)
{
this.Capture = false;
Moving = null;
}
RefreshLineSelection(e.Location);
}
void LineMover_MouseDown(object sender, MouseEventArgs e)
{
RefreshLineSelection(e.Location);
if (this.SelectedLine != null && Moving == null)
{
this.Capture = true;
Moving = new MoveInfo
{
Line = this.SelectedLine,
StartLinePoint = SelectedLine.StartPoint,
EndLinePoint = SelectedLine.EndPoint,
StartMoveMousePoint = e.Location
};
}
RefreshLineSelection(e.Location);
}
void LineMover_Paint(object sender, PaintEventArgs e)
{
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
foreach (var line in Lines)
{
var color = line == SelectedLine ? Color.Red : Color.Black;
var pen = new Pen(color, 2);
e.Graphics.DrawLine(pen, line.StartPoint, line.EndPoint);
}
}
void LineMover_MouseMove(object sender, MouseEventArgs e)
{
if (Moving != null)
{
Moving.Line.StartPoint = new PointF(Moving.StartLinePoint.X + e.X - Moving.StartMoveMousePoint.X, Moving.StartLinePoint.Y + e.Y - Moving.StartMoveMousePoint.Y);
Moving.Line.EndPoint = new PointF(Moving.EndLinePoint.X + e.X - Moving.StartMoveMousePoint.X, Moving.EndLinePoint.Y + e.Y - Moving.StartMoveMousePoint.Y);
}
RefreshLineSelection(e.Location);
}
private void RefreshLineSelection(Point point)
{
var selectedLine = FindLineByPoint(Lines, point);
if (selectedLine != this.SelectedLine)
{
this.SelectedLine = selectedLine;
this.Invalidate();
}
if (Moving != null)
this.Invalidate();
this.Cursor =
Moving != null ? Cursors.Hand :
SelectedLine != null ? Cursors.SizeAll :
Cursors.Default;
}
public List<GraphLine> Lines = new List<GraphLine>();
GraphLine SelectedLine = null;
MoveInfo Moving = null;
static GraphLine FindLineByPoint(List<GraphLine> lines, Point p)
{
var size = 10;
var buffer = new Bitmap(size * 2, size * 2);
foreach (var line in lines)
{
//draw each line on small region around current point p and check pixel in point p
using (var g = Graphics.FromImage(buffer))
{
g.Clear(Color.Black);
g.DrawLine(new Pen(Color.Green, 3), line.StartPoint.X - p.X + size, line.StartPoint.Y - p.Y + size, line.EndPoint.X - p.X + size, line.EndPoint.Y - p.Y + size);
}
if (buffer.GetPixel(size, size).ToArgb() != Color.Black.ToArgb())
return line;
}
return null;
}
public static void Main()
{
Application.Run(new LineMover());
}
}
public class MoveInfo
{
public GraphLine Line;
public PointF StartLinePoint;
public PointF EndLinePoint;
public Point StartMoveMousePoint;
}
public class GraphLine
{
public GraphLine(float x1, float y1, float x2, float y2)
{
this.StartPoint = new PointF(x1, y1);
this.EndPoint = new PointF(x2, y2);
}
public PointF StartPoint;
public PointF EndPoint;
}

Categories