The best way to draw a line in windows form C# - c#

I just want to know the best way to draw a line in windows form without using GDI+,but use control from toolbox.

You can use Visual Basic Power Pack Line Shape class - http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.powerpacks.lineshape.aspx
Another solution using Label control
Add a Label control to your form.
Set Label Text to empty.
Set BorderStyle to Fixed3D.
Set AutoSize to false.
Set Height to 2 (most easily accomplished by typing 2 into the Size/Height field under Label Properties).
Source : Draw horizontal divider in winforms

Might not be the best choice but should help
Label lbl = new Label();
lbl.Text = "_________________________________";
this.Controls.Add(lbl);

From: http://www.csharp-examples.net/separator-line/
// separator bevel line
Label labelSeperator = new Label();
labelSeperator.AutoSize = false;
labelSeperator.Height = 2;
labelSeperator.BorderStyle = BorderStyle.Fixed3D;
this.Controls.Add(labelSeperator);

You can use my control:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class AmenLine : Control
{
bool SzChInternaly;
public enum EDIR : int
{
Horizontal = 0,
Vertical = 1,
Slash = 2,
BKSlash = 3
}
public enum eStyle : int
{
rect = 0,
circ = 1
}
private eStyle _DrawStyle;
public eStyle DrawStyle {
get { return _DrawStyle; }
set {
_DrawStyle = value;
BuildMe(_Direc);
}
}
private EDIR _Direc = EDIR.Horizontal;
public EDIR Direction {
get { return _Direc; }
set {
if ((value == EDIR.Horizontal && _Direc == EDIR.Vertical) | (value == EDIR.Vertical && _Direc == EDIR.Horizontal)) {
SzChInternaly = true;
this.Size = new Size(this.Height, this.Width);
}
_Direc = value;
BuildMe(value);
}
}
private void BuildMe(EDIR BodyDir)
{
try {
SzChInternaly = true;
Drawing2D.GraphicsPath gr = new Drawing2D.GraphicsPath();
switch (BodyDir) {
case EDIR.Horizontal:
this.Size = new Size(this.Width, _BorderWidth + 10);
gr.AddRectangle(new Rectangle(0, (-_BorderWidth + this.Height) / 2, this.Width, _BorderWidth));
break;
case EDIR.Vertical:
this.Size = new Size(_BorderWidth + 10, this.Height);
gr.AddRectangle(new Rectangle((-_BorderWidth + this.Width) / 2, 0, _BorderWidth, this.Height));
break;
case EDIR.Slash:
try {
// CType(_BorderWidth * Height / Width, Integer)
for (i = 0; i <= Convert.ToInt32(Math.Pow(Math.Pow(Width, 2) + Math.Pow(Height, 2), 0.5)); i += _BorderWidth) {
switch (_DrawStyle) {
case eStyle.circ:
gr.AddPie(i, Convert.ToInt32(Height * i / Width), _BorderWidth, _BorderWidth, 0, 360);
break;
case eStyle.rect:
gr.AddRectangle(new Rectangle(i, Convert.ToInt32(Height * i / Width), _BorderWidth, _BorderWidth));
break;
}
}
} catch {
}
break;
case EDIR.BKSlash:
try {
// CType(_BorderWidth * Height / Width, Integer)
for (i = 0; i <= Convert.ToInt32(Math.Pow(Math.Pow(this.Height, 2) + Math.Pow(this.Width, 2), 0.5)); i += _BorderWidth) {
switch (_DrawStyle) {
case eStyle.circ:
gr.AddPie(Width - 1 - i, Convert.ToInt32(Height * i / Width), _BorderWidth, _BorderWidth, 0, 360);
break;
case eStyle.rect:
gr.AddRectangle(new Rectangle(Width - 1 - i, Convert.ToInt32(Height * i / Width), _BorderWidth, _BorderWidth));
break;
}
}
} catch {
}
break;
}
this.Region = new Region(gr);
SzChInternaly = false;
} catch {
}
}
private int _BorderWidth = 1;
public int BorderWidth {
get { return _BorderWidth; }
set {
_BorderWidth = value;
BuildMe(_Direc);
}
}
public AmenLine()
{
this.BackColor = Color.Black;
BuildMe(_Direc);
}
protected override void OnSizeChanged(System.EventArgs e)
{
base.OnSizeChanged(e);
if (SzChInternaly == false)
BuildMe(_Direc);
}
}

You can draw line runtime by below code:
Pen pen = new Pen(Color.FromArgb(255, 0, 0, 0));
e.Graphics.DrawLine(pen, 20, 10, 300, 100);

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();
}
}

Xamarin Forms PanGesture has inconsequent coordinates

I have a problem. I created an image within a AbsoluteLayout with a panGestureRecognizer, but when I use that to move the image in the app, it jumps back and forth. Very annoying. Here is the code I am using
private void AddImageToPreview(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
var image = (Image)e.NewItems[e.NewItems.Count - 1];
PanGestureRecognizer panGesture = new PanGestureRecognizer();
panGesture.PanUpdated += PanUpdated;
image.GestureRecognizers.Add(panGesture);
MyLayout.Children.Add(image);
}
}
void PanUpdated(object sender, PanUpdatedEventArgs args)
{
Image image = (Image)sender;
if (args.StatusType.Equals(GestureStatus.Running))
{
x = args.TotalX;
y = args.TotalY;
image.TranslateTo(x, y, 1);
}
}
Can someone help me improve this, so this drag/drop system works smooth and fast!
I have a control purely made in Xamarin.Forms that you can use for the above mentioned usage:
using System;
using Xamarin.Forms;
using FFImageLoading.Forms;
public class ZoomImage : CachedImage
{
private const double MIN_SCALE = 1;
private const double MAX_SCALE = 4;
private const double OVERSHOOT = 0.15;
private double StartScale, LastScale;
private double StartX, StartY;
public ZoomImage()
{
var pinch = new PinchGestureRecognizer();
pinch.PinchUpdated += OnPinchUpdated;
GestureRecognizers.Add(pinch);
var pan = new PanGestureRecognizer();
pan.PanUpdated += OnPanUpdated;
GestureRecognizers.Add(pan);
var tap = new TapGestureRecognizer { NumberOfTapsRequired = 2 };
tap.Tapped += OnTapped;
GestureRecognizers.Add(tap);
Scale = MIN_SCALE;
TranslationX = TranslationY = 0;
AnchorX = AnchorY = 0;
}
protected override SizeRequest OnMeasure(double widthConstraint, double heightConstraint)
{
Scale = MIN_SCALE;
TranslationX = TranslationY = 0;
AnchorX = AnchorY = 0;
return base.OnMeasure(widthConstraint, heightConstraint);
}
private void OnTapped(object sender, EventArgs e)
{
if (Scale > MIN_SCALE)
{
this.ScaleTo(MIN_SCALE, 250, Easing.CubicInOut);
this.TranslateTo(0, 0, 250, Easing.CubicInOut);
}
else
{
AnchorX = AnchorY = 0.5; //TODO tapped position
this.ScaleTo(MAX_SCALE, 250, Easing.CubicInOut);
}
}
private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
{
switch (e.StatusType)
{
case GestureStatus.Started:
StartX = (1 - AnchorX) * Width;
StartY = (1 - AnchorY) * Height;
break;
case GestureStatus.Running:
AnchorX = Clamp(1 - (StartX + e.TotalX) / Width, 0, 1);
AnchorY = Clamp(1 - (StartY + e.TotalY) / Height, 0, 1);
break;
}
}
private void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
{
switch (e.Status)
{
case GestureStatus.Started:
LastScale = e.Scale;
StartScale = Scale;
AnchorX = e.ScaleOrigin.X;
AnchorY = e.ScaleOrigin.Y;
break;
case GestureStatus.Running:
if (e.Scale < 0 || Math.Abs(LastScale - e.Scale) > (LastScale * 1.3) - LastScale)
{ return; }
LastScale = e.Scale;
var current = Scale + (e.Scale - 1) * StartScale;
Scale = Clamp(current, MIN_SCALE * (1 - OVERSHOOT), MAX_SCALE * (1 + OVERSHOOT));
break;
case GestureStatus.Completed:
if (Scale > MAX_SCALE)
this.ScaleTo(MAX_SCALE, 250, Easing.SpringOut);
else if (Scale < MIN_SCALE)
this.ScaleTo(MIN_SCALE, 250, Easing.SpringOut);
break;
}
}
private T Clamp<T>(T value, T minimum, T maximum) where T: IComparable
{
if (value.CompareTo(minimum) < 0)
return minimum;
else if (value.CompareTo(maximum) > 0)
return maximum;
else
return value;
}
}
What this does:
Pinch zoom, Pan and Swipe movements together with double tap centre zoom and un-zoom
Note: I have used FFimageLoading's CachedImage because I needed to cache the data in case you do not intend this replace CachedImage with Xamarin.Forms.Image

How do you make a tab control look like it does in Visual Studio

I'm trying to make a UI, with elements that resemble Visual Studio. For example, I want my horizontal and vertical tabs, to look like this:
You can't in a Winform. So, I was like ... They probably added that in WPF. Nope.
Edit: Code rewrite and posted below
Not wanting to do it by hand, which I knew I had to, I spent a few minutes and created this:
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.Windows.Forms;
using static VisualStudioLikeControls.misc;
namespace VisualStudioLikeControls
{
//TODO:
// ADD a multi-tab select system
[
ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch),
DefaultProperty("TabPages"), DefaultEvent("SelectedIndexChanged")
]
public partial class VSTabControl : TabControl
{
[Category("Custom"), Description("The color used for the components background."), DefaultValue(typeof(Color), "0, 0, 0")]
public Color Background { get; set; } = Color.FromArgb(0, 0, 0);
[Category("Custom"), Description("The color used for the components tab background. Only fully shows, if the tabs background color is set to transparent."), DefaultValue(typeof(Color), "0, 0, 0")]
public Color BackgroundTab { get; set; } = Color.FromArgb(0, 0, 0);
[Category("Custom"), Description("The color used for the components border."), DefaultValue(typeof(Color), "0, 122, 204")]
public Color Border { get; set; } = Color.FromArgb(0, 122, 204);
[Category("Custom"), Description("Indicates whether or not you the components Tabpages Headers have border edges."), DefaultValue(true)]
public bool BorderEdges { get; set; } = true;
private int _BorderSize = 1;
[Category("Custom"), Description("The size of the components border."), DefaultValue(1)]
public int BorderSize
{
get => _BorderSize;
set => _BorderSize = value.LimitToRange(1, 2); //Two is the largest it can be. Anymore won't be seen.
}
[Category("Custom"), Description("The color used for the components Active tabs color."), DefaultValue(typeof(Color), "0, 122, 204")]
public Color ActiveTab { get; set; } = Color.FromArgb(0, 122, 204);
[Category("Custom"), Description("The color used for the components Alternative tab indicator color.\nUsage : Set the Tag property of the desired TabPage(s) to 1."), DefaultValue(typeof(Color), "OrangeRed")]
public Color AlternativeTab { get; set; } = Color.OrangeRed;
[Category("Custom"), Description("The color used for the components InActive tabs color."), DefaultValue(typeof(Color), "0, 0, 0")]
public Color InActiveTab { get; set; } = Color.FromArgb(0, 0, 0);
[Category("Custom"), Description("The color used for the components Active Tab Indicator."), DefaultValue(typeof(Color), "0, 122, 204")]
public Color ActiveIndicator { get; set; } = Color.FromArgb(0, 122, 204);
[Category("Custom"), Description("The color used for the components InActive Tab Indicator."), DefaultValue(typeof(Color), "0, 122, 204")]
public Color InActiveIndicator { get; set; } = Color.FromArgb(0, 122, 204);
[Category("Custom"), Description("The size of the components Indicator."), DefaultValue(6)]
public int IndicatorSize { get; set; } = 6; //Might become obsolete
[Category("Custom"), Description("The color used for the components Divider."), DefaultValue(typeof(Color), "0, 122, 204")]
public Color Divider { get; set; } = Color.FromArgb(0, 122, 204);
private int _DividerSize = 2;
[Category("Custom"), Description("The size of the components Divider."), DefaultValue(2)]
public int DividerSize
{
get => _DividerSize;
set => _DividerSize = value.LimitToRange(1, 2);
}
[Category("Custom"), Description("The color used for the components Active tabs text."), DefaultValue(typeof(Color), "255, 255, 255")]
public Color ActiveText { get; set; } = Color.FromArgb(255, 255, 255);
[Category("Custom"), Description("The color used for the components InActive tabs text."), DefaultValue(typeof(Color), "255, 255, 255")]
public Color InActiveText { get; set; } = Color.FromArgb(255, 255, 255);
[Category("Custom"), Description("Indicates whether or not you can rearrange the components Tabpages. CLICK on the Tabpages HEADER with the LEFT mousebutton and HOLD DOWN the <SHIFT> KEY, to drag it LEFT or RIGHT."), DefaultValue(false)]
public bool CanDrag { get; set; }
StringFormat format = new StringFormat { Alignment = StringAlignment.Center };
private Bitmap bitDrag = default(Bitmap);
private bool bDrag, bMouseDown, bShiftKey;
private Point ptPreviousLocation, ptMaxDrag;
private int DraggedIndex = -1;
protected override CreateParams CreateParams
{
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED to stop flicker
return cp;
}
}
public VSTabControl()
{
InitializeComponent();
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer |
ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.CacheText, true);
Alignment = TabAlignment.Top;
Margin = new Padding(0);
Padding = new Point(0, 0);
Font = new Font("Leelawadee UI", Font.Size);
}
private void SetDragState() => bDrag = (CanDrag && bMouseDown && bShiftKey);
protected override void OnMouseDown(MouseEventArgs e)
{
bMouseDown = true;
SetDragState();
Rectangle rectDrag = GetTabRect(SelectedIndex);
ptPreviousLocation = new Point(rectDrag.X, rectDrag.Y);
rectDrag.Width += 1; rectDrag.Height += 1;
Bitmap src = new Bitmap(Width, Height);
DrawToBitmap(src, ClientRectangle);
using (Graphics g = Graphics.FromImage(bitDrag = new Bitmap(rectDrag.Width, rectDrag.Height)))
{
g.DrawImage(src, new Rectangle(0, 0, bitDrag.Width, bitDrag.Height), rectDrag, GraphicsUnit.Pixel);
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (bDrag)
{
if (Alignment == TabAlignment.Top || Alignment == TabAlignment.Bottom)
{
ptPreviousLocation = new Point(((e.X < 0) ? 0 : (e.X > ptMaxDrag.X) ? ptMaxDrag.X : e.X), (Alignment == TabAlignment.Top ? BorderSize : ptMaxDrag.Y));
}
if (Alignment == TabAlignment.Right || Alignment == TabAlignment.Left)
{
ptPreviousLocation = new Point(ptMaxDrag.X, ((e.Y < 0) ? 0 : (e.Y > ptMaxDrag.Y) ? ptMaxDrag.Y : e.Y));
}
for (int i = 0; i < TabCount; i++)
{
if (GetTabRect(i).Contains(PointToClient(Cursor.Position))) { DraggedIndex = i; break; }
}
Invalidate();
}
}
protected override void OnMouseUp(MouseEventArgs e)
{
void SwapTabPages(TabPage inDestTab)
{
int SourceIndex = TabPages.IndexOf(SelectedTab);
int DestinationIndex = TabPages.IndexOf(inDestTab);
TabPages[DestinationIndex] = SelectedTab;
TabPages[SourceIndex] = inDestTab;
if (SelectedIndex == SourceIndex) { SelectedIndex = DestinationIndex; }
else if (SelectedIndex == DestinationIndex) { SelectedIndex = SourceIndex; }
}
bDrag = bMouseDown = false;
if (DraggedIndex > -1)
{
SwapTabPages(TabPages[DraggedIndex]);
DraggedIndex = -1;
}
SetDragState();
Invalidate();
}
protected override void OnKeyDown(KeyEventArgs ke) { bShiftKey = ke.Shift; SetDragState(); }
protected override void OnKeyUp(KeyEventArgs e) { bDrag = bShiftKey = false; SetDragState(); }
protected override void OnPaint(PaintEventArgs e)
{
if (DesignMode) return;
e.Graphics.Clear(Background); //Background
Rectangle container = new Rectangle(0, 0, Width - (BorderSize % 2), Height - (BorderSize % 2));
Rectangle containerHead = default(Rectangle);
if (TabCount > 0)
{
using (SolidBrush brushBackgroundTab = new SolidBrush(BackgroundTab))
using (SolidBrush brushDivider = new SolidBrush(Divider))
{
//Draws the background of the tab control
{
e.Graphics.FillRectangle(brushBackgroundTab, DisplayRectangle);
}
//Divider Line
{
Rectangle rectDivider = GetTabRect(SelectedIndex);
if (Alignment == TabAlignment.Top || Alignment == TabAlignment.Bottom)
{
e.Graphics.FillRectangle(brushDivider,
0,
(((Alignment == TabAlignment.Top) ? (TabPages[SelectedIndex].Top - DividerSize - (DividerSize % 2)) :
(TabPages[SelectedIndex].Bottom + (DividerSize % 2)))),
(Width - BorderSize), DividerSize
);
}
if (Alignment == TabAlignment.Right || Alignment == TabAlignment.Left)
{
e.Graphics.FillRectangle(brushDivider,
((Alignment == TabAlignment.Right) ? (TabPages[SelectedIndex].Right + (DividerSize % 2)) : TabPages[SelectedIndex].Left - DividerSize - (DividerSize % 2)),
BorderSize,
DividerSize,
(Height - (BorderSize * 2))
);
}
}
}
}
using (Pen penActive = new Pen(ActiveTab))
using (Pen penBorder = new Pen(Border, BorderSize))
using (SolidBrush brushActive = new SolidBrush(ActiveTab))
using (SolidBrush brushInActive = new SolidBrush(InActiveTab))
using (SolidBrush brushAlternative = new SolidBrush(AlternativeTab))
using (SolidBrush brushActiveIndicator = new SolidBrush(ControlPaint.Light(ActiveIndicator, .75f)))
using (SolidBrush brushInActiveIndicator = new SolidBrush(InActiveIndicator))
using (SolidBrush brushActiveText = new SolidBrush(ActiveText))
using (SolidBrush brushInActiveText = new SolidBrush(InActiveText))
using (SolidBrush brushDrag = new SolidBrush(ControlPaint.Dark(ActiveTab, .5f)))
{
penBorder.Alignment = penActive.Alignment = PenAlignment.Inset; //Only way to set the proper alignment, that I know of
e.Graphics.DrawRectangle(penBorder, container); //Border
if (TabCount > 0)
{
ptMaxDrag = new Point(0, 0);
for (int i = 0; i < TabCount; i++)
{
containerHead = GetTabRect(i);
e.Graphics.FillRectangle((SelectedIndex == i) ? (bDrag ? brushDrag : brushActive) : brushInActive, containerHead); //Header Background
//Header Edges
if (BorderEdges && (i == SelectedIndex))
{
Point ptA = new Point(0, 0); Point ptB = new Point(0, 0);
Point ptC = new Point(0, 0); Point ptD = new Point(0, 0);
ptA.X = ptB.X = ptD.X = containerHead.X;
ptA.Y = ptB.Y = ptC.Y = containerHead.Y;
ptA.Y = ptC.Y = ptD.Y = containerHead.Y + containerHead.Height - 1;
if (Alignment == TabAlignment.Top || Alignment == TabAlignment.Bottom)
{
ptD.X = ptC.X = containerHead.X + containerHead.Width;
ptC.Y = containerHead.Y;
if (Alignment == TabAlignment.Bottom)
{
Swap(ref ptA, ref ptB); Swap(ref ptC, ref ptD);
}
}
if (Alignment == TabAlignment.Right || Alignment == TabAlignment.Left)
{
ptA.Y = containerHead.Y;
ptB.X = ptC.X = containerHead.X + containerHead.Width - 1;
if (Alignment == TabAlignment.Left)
{
Swap(ref ptA, ref ptC); Swap(ref ptB, ref ptD);
}
}
e.Graphics.DrawLine(new Pen(ControlPaint.Light(brushActive.Color)), ptA, ptB);
e.Graphics.DrawLine(new Pen(ControlPaint.Light(brushActive.Color)), ptB, ptC);
e.Graphics.DrawLine(new Pen(ControlPaint.Dark(brushActive.Color)), ptC, ptD);
}
//Indicator
{
Rectangle rectDivider = default(Rectangle);
if (Alignment == TabAlignment.Top || Alignment == TabAlignment.Bottom)
{
rectDivider = new Rectangle(containerHead.X, containerHead.Y + ((Alignment == TabAlignment.Top) ? containerHead.Height : -DividerSize), containerHead.Width, DividerSize);
}
if (Alignment == TabAlignment.Right || Alignment == TabAlignment.Left)
{
rectDivider = new Rectangle(containerHead.X - ((Alignment == TabAlignment.Right) ? DividerSize : -containerHead.Width), containerHead.Y, DividerSize, containerHead.Height); //Indicator
}
e.Graphics.FillRectangle(((TagToInt(TabPages[i]) == 1) ? brushAlternative : ((i == SelectedIndex) ? brushActiveIndicator : brushInActiveIndicator)), rectDivider);
}
//Text
if (!(bDrag && i == SelectedIndex))
{
int angle = 0;
{
if (Alignment == TabAlignment.Right) angle = 90;
if (Alignment == TabAlignment.Left) angle = 270;
}
float w, h;
w = h = 0f;
if (Alignment == TabAlignment.Top || Alignment == TabAlignment.Bottom) { w = containerHead.X + (containerHead.Width / 2); }
if (Alignment == TabAlignment.Right || Alignment == TabAlignment.Left) { w = containerHead.X; h = containerHead.Y + (containerHead.Height / 2); }
if (Alignment == TabAlignment.Top || Alignment == TabAlignment.Bottom) { h = containerHead.Y + ((Alignment == TabAlignment.Top) ? IndicatorSize : 0) + ((containerHead.Height - IndicatorSize) / 2); }
if (Alignment == TabAlignment.Right || Alignment == TabAlignment.Left) { w += (((Alignment == TabAlignment.Right) ? 0 : IndicatorSize) + ((containerHead.Width - IndicatorSize) / 2)); }
e.Graphics.TranslateTransform(w, h);
{
Size textSize = e.Graphics.MeasureString(TabPages[i].Text, Font).ToSize();
e.Graphics.RotateTransform(angle);
e.Graphics.DrawString(TabPages[i].Text, Font, ((SelectedIndex == i) ? brushActiveText : brushInActiveText), new PointF((-textSize.Width / 2f), (-textSize.Height / 2f)));
}
e.Graphics.ResetTransform();
}
if (bMouseDown)
{
if (Alignment == TabAlignment.Top || Alignment == TabAlignment.Bottom) { if (i > 0) { ptMaxDrag.X += GetTabRect(i).Width; } }
if (Alignment == TabAlignment.Top) { ptMaxDrag.Y = BorderSize; }
if (Alignment == TabAlignment.Bottom) { ptMaxDrag.Y = containerHead.Y; };
if (Alignment == TabAlignment.Right || Alignment == TabAlignment.Left) { ptMaxDrag.X = containerHead.X; if (i > 0) { ptMaxDrag.Y += containerHead.Height; } }
}
if (bDrag && (bitDrag != null)) { e.Graphics.DrawImage(bitDrag, new Point(ptPreviousLocation.X, ptPreviousLocation.Y)); }
}
}
}
}
}
}
Helper class
using System;
using System.Windows.Forms;
namespace VisualStudioLikeControls
{
static class misc
{
/// <summary> Essentially ... clamp </summary>
public static int LimitToRange(this int value, int inclusiveMinimum, int inclusiveMaximum)
{
if (value < inclusiveMinimum) { return inclusiveMinimum; }
if (value > inclusiveMaximum) { return inclusiveMaximum; }
return value;
}
/// <summary> Swap function that uses <Generics>. </summary>
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b; b = temp;
}
/// <summary> Converts the <Tag> property of a control to an integer. </summary>
public static int TagToInt(object inObject) => Convert.ToInt32((inObject as Control).Tag);
}
}
Progress, so far:

Show Transparent Loading Spinner above other Controls

I am working in a spinner control. I want the control to support transparent backcolor. When the arc is drawn, there is a blank space in the middle, I want that space to be truly transparent, so that I could put another control behind it and it would not be covered by the spinner.
I tried overriding the CreateParams void.
Also I set the style to support TransparentColor.
Tried overriding OnPaintBackground void, but I cannot achieve the real transparent backcolor.
So, what can you suggest me to do?
To make a transparent layer, you should override painting of the control and draw the control in this order, First draw all controls in the same container which are under your control (based on z-index) on a bitmap.
Then draw that bitmap on graphics of your control.
At last draw content of your control.
Also the BackColor of your control should be Color.Transparent.
Also as another option to make transparent layer, you can exclude some regions from your control when drawing.
In the following samples I used the first technique and created 2 controls. A spinning circles transparent control. and a transparent picturebox control.
In both samples I used a delay between loading rows to showing a spinner make sense.
Sample 1 - Using a SpinningCircles Control
SpinningCircles control draws circles and supports transparency. The control doesn't animate at design-time, but it animates at run-time. Also it doesn't consume resources when it is not visible.
Sample 2 - Using a TransparentPictureBox Control and a transparent animated gif
TransparentPictureBox control supports transparency, so I used an animated gif as its image and as you can see, the gif is showing correctly.
Sample 1 Code - SpinningCircles
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
public class SpinningCircles : Control
{
int increment = 1;
int radius = 4;
int n = 8;
int next = 0;
Timer timer;
public SpinningCircles()
{
timer = new Timer();
this.Size = new Size(100, 100);
timer.Tick += (s, e) => this.Invalidate();
if (!DesignMode)
timer.Enabled = true;
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw | ControlStyles.UserPaint |
ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
}
protected override void OnPaint(PaintEventArgs e)
{
if (Parent != null && this.BackColor == Color.Transparent)
{
using (var bmp = new Bitmap(Parent.Width, Parent.Height))
{
Parent.Controls.Cast<Control>()
.Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
.Where(c => c.Bounds.IntersectsWith(this.Bounds))
.OrderByDescending(c => Parent.Controls.GetChildIndex(c))
.ToList()
.ForEach(c => c.DrawToBitmap(bmp, c.Bounds));
e.Graphics.DrawImage(bmp, -Left, -Top);
}
}
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
int length = Math.Min(Width, Height);
PointF center = new PointF(length / 2, length / 2);
int bigRadius = length / 2 - radius - (n - 1) * increment;
float unitAngle = 360 / n;
if (!DesignMode)
next++;
next = next >= n ? 0 : next;
int a = 0;
for (int i = next; i < next + n; i++)
{
int factor = i % n;
float c1X = center.X + (float)(bigRadius * Math.Cos(unitAngle * factor * Math.PI / 180));
float c1Y = center.Y + (float)(bigRadius * Math.Sin(unitAngle * factor * Math.PI / 180));
int currRad = radius + a * increment;
PointF c1 = new PointF(c1X - currRad, c1Y - currRad);
e.Graphics.FillEllipse(Brushes.Black, c1.X, c1.Y, 2 * currRad, 2 * currRad);
using (Pen pen = new Pen(Color.White, 2))
e.Graphics.DrawEllipse(pen, c1.X, c1.Y, 2 * currRad, 2 * currRad);
a++;
}
}
protected override void OnVisibleChanged(EventArgs e)
{
timer.Enabled = Visible;
base.OnVisibleChanged(e);
}
}
Sample 2 Code - TransparentPictureBox Code
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
class TransparentPictureBox : PictureBox
{
public TransparentPictureBox()
{
this.BackColor = Color.Transparent;
}
protected override void OnPaint(PaintEventArgs e)
{
if (Parent != null && this.BackColor == Color.Transparent)
{
using (var bmp = new Bitmap(Parent.Width, Parent.Height))
{
Parent.Controls.Cast<Control>()
.Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
.Where(c => c.Bounds.IntersectsWith(this.Bounds))
.OrderByDescending(c => Parent.Controls.GetChildIndex(c))
.ToList()
.ForEach(c => c.DrawToBitmap(bmp, c.Bounds));
e.Graphics.DrawImage(bmp, -Left, -Top);
}
}
base.OnPaint(e);
}
}
I slightly changed Reza's code (SpinningCircles) to add a semi-transparent background. I wanted to share it with you.
(NOTE: The spinner length was fixed to 100 and should be added as a component property)
public partial class WorkingPanel : UserControl
{
#region Constants
private static readonly Int32 kSpinnerLength = 100;
#endregion
#region Fields
private Int32 increment = 1;
private Int32 radius = 4;
private Int32 n = 8;
private Int32 next = 0;
private Timer timer = null;
#endregion
#region Constructor
public WorkingPanel()
{
this.Size = new Size(100, 100);
timer = new Timer();
timer.Tick += (s, e) => this.Invalidate();
if (!DesignMode)
timer.Enabled = true;
SetStyle(ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw | ControlStyles.UserPaint |
ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.Transparent;
}
#endregion
#region Methods (Protected - Override)
protected override void OnPaint(PaintEventArgs e)
{
if (null != Parent && (this.BackColor.A != 255 || this.BackColor == Color.Transparent))
{
using (var bmp = new Bitmap(Parent.Width, Parent.Height))
{
Parent.Controls.Cast<Control>()
.Where(c => Parent.Controls.GetChildIndex(c) > Parent.Controls.GetChildIndex(this))
.Where(c => c.Bounds.IntersectsWith(this.Bounds))
.OrderByDescending(c => Parent.Controls.GetChildIndex(c))
.ToList()
.ForEach(c => c.DrawToBitmap(bmp, c.Bounds));
e.Graphics.DrawImage(bmp, -Left, -Top);
if (this.BackColor != Color.Transparent)
e.Graphics.FillRectangle(new SolidBrush(this.BackColor), new Rectangle(0, 0, Width, Height));
}
}
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
Int32 length = kSpinnerLength;
PointF center = new PointF(Width / 2, Height / 2);
Int32 bigRadius = length / 2 - radius - (n - 1) * increment;
float unitAngle = 360 / n;
if (!DesignMode)
next++;
next = next >= n ? 0 : next;
Int32 a = 0;
for (Int32 i = next; i < next + n; i++)
{
Int32 factor = i % n;
float c1X = center.X + (float)(bigRadius * Math.Cos(unitAngle * factor * Math.PI / 180));
float c1Y = center.Y + (float)(bigRadius * Math.Sin(unitAngle * factor * Math.PI / 180));
Int32 currRad = radius + a * increment;
PointF c1 = new PointF(c1X - currRad, c1Y - currRad);
e.Graphics.FillEllipse(Brushes.White, c1.X, c1.Y, 2 * currRad, 2 * currRad);
using (Pen pen = new Pen(Color.White, 2))
e.Graphics.DrawEllipse(pen, c1.X, c1.Y, 2 * currRad, 2 * currRad);
a++;
}
}
protected override void OnVisibleChanged(EventArgs e)
{
timer.Enabled = Visible;
base.OnVisibleChanged(e);
}
#endregion
}
Modification from CrApHeR's code to add Designer Properties
How to use
Add this class to your project
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Windows.Forms;
[DesignerCategory("Code")]
public class Spinner : Control
{
private int next = 0;
private Timer timer = new Timer();
public Spinner()
{
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
Size = new Size(100, 100);
BackColor = Color.Transparent;
timer.Tick += (s, e) => Invalidate();
if (!DesignMode)
{
timer.Enabled = true;
}
}
[Browsable(true)]
[Category("Appearance")]
public int NodeCount { get; set; } = 8;
[Browsable(true)]
[Category("Appearance")]
public int NodeRadius { get; set; } = 4;
[Browsable(true)]
[Category("Appearance")]
public float NodeResizeRatio { get; set; } = 1.0f;
[Browsable(true)]
[Category("Appearance")]
public Color NodeFillColor { get; set; } = Color.Black;
[Browsable(true)]
[Category("Appearance")]
public Color NodeBorderColor { get; set; } = Color.White;
[Browsable(true)]
[Category("Appearance")]
public int NodeBorderSize { get; set; } = 2;
[Browsable(true)]
[Category("Appearance")]
public int SpinnerRadius { get; set; } = 100;
protected override void OnPaint(PaintEventArgs e)
{
if (null != Parent && (BackColor.A != 255 || BackColor == Color.Transparent))
{
using (Bitmap bmp = new Bitmap(Parent.Width, Parent.Height))
{
foreach (Control control in GetIntersectingControls(Parent))
{
control.DrawToBitmap(bmp, control.Bounds);
}
e.Graphics.DrawImage(bmp, -Left, -Top);
if (BackColor != Color.Transparent)
{
using (Brush brush = new SolidBrush(BackColor))
{
e.Graphics.FillRectangle(brush, 0, 0, Width, Height);
}
}
}
}
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
PointF center = new PointF(Width / 2, Height / 2);
int bigRadius = (int)(SpinnerRadius / 2 - NodeRadius - (NodeCount - 1) * NodeResizeRatio);
float unitAngle = 360 / NodeCount;
if (!DesignMode)
{
next++;
}
next = next >= NodeCount ? 0 : next;
for (int i = next, a = 0; i < next + NodeCount; i++, a++)
{
int factor = i % NodeCount;
float c1X = center.X + (float)(bigRadius * Math.Cos(unitAngle * factor * Math.PI / 180));
float c1Y = center.Y + (float)(bigRadius * Math.Sin(unitAngle * factor * Math.PI / 180));
int currRad = (int)(NodeRadius + a * NodeResizeRatio);
PointF c1 = new PointF(c1X - currRad, c1Y - currRad);
using (Brush brush = new SolidBrush(NodeFillColor))
{
e.Graphics.FillEllipse(brush, c1.X, c1.Y, 2 * currRad, 2 * currRad);
}
using (Pen pen = new Pen(Color.White, NodeBorderSize))
{
e.Graphics.DrawEllipse(pen, c1.X, c1.Y, 2 * currRad, 2 * currRad);
}
}
}
protected override void OnVisibleChanged(EventArgs e)
{
timer.Enabled = Visible;
base.OnVisibleChanged(e);
}
private IOrderedEnumerable<Control> GetIntersectingControls(Control parent)
{
return parent.Controls.Cast<Control>()
.Where(c => parent.Controls.GetChildIndex(c) > parent.Controls.GetChildIndex(this))
.Where(c => c.Bounds.IntersectsWith(Bounds))
.OrderByDescending(c => parent.Controls.GetChildIndex(c));
}
}
Drag the Spinner control from the Toolbox to your Form
Alternatively, you can add it at runtime with this code
Spinner spinner = new Spinner();
spinner.Location = new Point(50, 50);
Controls.Add(spinner);
Then, you can change its properties from the Designer Properties category Appearance
The main properties you may want to customize are:
NodeCount (default: 8)
NodeRadius (default: 4)
NodeResizeRatio (default: 1.0f)
NodeFillColor (default: Black)
NodeBorderColor (default: White)
NodeBorderSize (default: 2)
SpinnerRadius (default: 100)
BackColor (default: Transparent) (supports alpha transparency)
BackColor = Color.FromArgb(50, Color.Blue.R, Color.Blue.G, Color.Blue.B))

Getting exception "Parameter is not valid" when minimizing the form to the taskbar

It happen only when I resize the form when the program is running.
I click the "-" and the program is minimised to the taskbar, then I see the error/exception message.
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
if (!_addingLines)
SplitText(this.Text);
if (_backBmpBU != null)
this._backBmp = MakeBackBmp(_backBmpBU);
if (this.BitmapModus)
{
UpdateBitmap();
}
}
private void UpdateBitmap()
{
if (_lines != null && _lines.Length > 0)
{
SizeF sz = new SizeF(0, 0);
float lineOrigHeight = sz.Height;
using (Graphics g = this.CreateGraphics())
{
sz = g.MeasureString("Teststring", this.Font);
if (this._additionalSpaceBetweenLines > 0)
sz = new SizeF(sz.Width, sz.Height + this._additionalSpaceBetweenLines);
}
this._textHeight = sz.Height * _lines.Length;
if (_bmp != null)
{
_bmp.Dispose();
_bmp = null;
}
try
{
if (this._textHeight > MAXHEIGHT)
throw new Exception("Text too long, for BitmapMode.");
_bmp = new Bitmap(this.ClientSize.Width, (int)Math.Ceiling(this._textHeight));
using (Graphics g = Graphics.FromImage(_bmp))
{
//set it to value you like...
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.None;
using (SolidBrush b = new SolidBrush(this.ForeColor))
{
for (int i = 0; i < _lines.Length; i++)
{
SolidBrush bb = b;
if (TrimText)
_lines[i] = _lines[i].Trim();
sz = g.MeasureString(_lines[i], this.Font);
lineOrigHeight = sz.Height;
if (this._additionalSpaceBetweenLines > 0)
sz = new SizeF(sz.Width, sz.Height + this._additionalSpaceBetweenLines);
_posX = 0;
if (this.TextLayoutCentered)
_posX = (this.ClientSize.Width - sz.Width) / 2.0F;
bool drect = false;
bool colw = false;
int indx = -1;
int length = 0;
string textToFind = "";
Color fc = this.ForeColor;
Color bc = Color.Transparent;
Color rc = Color.Transparent;
if (Words != null && Words.Count > 0)
{
for (int ii = 0; ii < Words.Count; ii++)
{
if (_lines[i].Contains(Words[ii].WordOrText))
{
bb = new SolidBrush(Words[ii].ForeColor);
if (Words[ii].DrawRect)
drect = true;
if (Words[ii].ColorOnlyThisWord)
colw = true;
indx = _lines[i].IndexOf(Words[ii].WordOrText);
length = Words[ii].WordOrText.Length;
textToFind = Words[ii].WordOrText;
fc = Words[ii].ForeColor;
bc = Words[ii].BackColor;
rc = Words[ii].RectColor;
drect = Words[ii].DrawRect;
}
}
}
if (colw)
{
//reset b and create a new color brush
if (bb.Equals(b) == false)
bb.Dispose();
bb = b;
string ftext = _lines[i];
float cPosX = _posX;
using (SolidBrush bbb = new SolidBrush(fc))
{
while (indx > -1)
{
if (indx > 0)
g.DrawString(ftext.Substring(0, indx), this.Font, bb, new PointF(cPosX, sz.Height * i + _additionalSpaceBetweenLines / 2F));
cPosX += g.MeasureString(ftext.Substring(0, indx), this.Font).Width;
SizeF sfWord = g.MeasureString(ftext.Substring(indx, length), this.Font);
if (bc.ToArgb().Equals(Color.Transparent.ToArgb()) == false)
{
using (SolidBrush bbbb = new SolidBrush(bc))
g.FillRectangle(bbbb, cPosX, sz.Height * i + _additionalSpaceBetweenLines / 2F, sfWord.Width, sfWord.Height);
}
g.DrawString(ftext.Substring(indx, length), this.Font, bbb, new PointF(cPosX, sz.Height * i + _additionalSpaceBetweenLines / 2F));
cPosX += sfWord.Width;
ftext = ftext.Substring(indx + length);
if (textToFind.Length > 0)
indx = ftext.IndexOf(textToFind);
else
indx = -1;
}
if (ftext.Length > 0)
g.DrawString(ftext, this.Font, bb, new PointF(cPosX, sz.Height * i + _additionalSpaceBetweenLines / 2F));
}
}
else
{
if (bc.ToArgb().Equals(Color.Transparent.ToArgb()) == false)
{
using (SolidBrush bbbb = new SolidBrush(bc))
g.FillRectangle(bbbb, _posX, sz.Height * i + _additionalSpaceBetweenLines / 2F, sz.Width, lineOrigHeight);
}
g.DrawString(_lines[i], this.Font, bb, new PointF(_posX, sz.Height * i + _additionalSpaceBetweenLines / 2F));
}
if (drect)
{
if (rc.ToArgb().Equals(Color.Transparent.ToArgb()) == false)
using (Pen p = new Pen(rc))
g.DrawRectangle(p, _posX, sz.Height * i + _additionalSpaceBetweenLines / 2F, sz.Width, lineOrigHeight);
}
if (bb.Equals(b) == false)
bb.Dispose();
if (DrawRect)
{
using (Pen p = new Pen(this.ForeColor))
{
if (DrawRectAroundText)
g.DrawRectangle(p, _posX, sz.Height * i + _additionalSpaceBetweenLines / 2F, sz.Width, lineOrigHeight);
else
g.DrawRectangle(p, 0, sz.Height * i + _additionalSpaceBetweenLines / 2F, this.ClientSize.Width - 1, lineOrigHeight);
}
}
}
}
}
}
catch (Exception ex)
{
if (_bmp != null)
{
_bmp.Dispose();
_bmp = null;
}
this.BitmapModus = false;
//MessageBox.Show(ex.Message + " switching to Dynamic-Draw-Mode.");
OnSwitchModeOnError();
}
}
}
When the program is running and i minimize the form to the taskbar im getting exception:
Parameter is not valid
The full exception message:
System.ArgumentException was caught
HResult=-2147024809
Message=Parameter is not valid.
Source=System.Drawing
StackTrace:
at System.Drawing.Bitmap..ctor(Int32 width, Int32 height, PixelFormat format)
at System.Drawing.Bitmap..ctor(Int32 width, Int32 height)
at ScrollLabelTest.ScrollLabel.UpdateBitmap() in e:\scrolllabel\ScrollLabel\ScrollLabel\ScrollLabel.cs:line 601
InnerException:
I had the same problem, with a huge list of errors in the Just-In-Time-Debugger. After several hours of searching I found my own way. Try this, no more exceptions!!
private void picMinimize_Click(object sender, EventArgs e)
{
try
{
panelUC.Visible = false; //change visible status of your form, etc.
this.WindowState = FormWindowState.Minimized; //minimize
minimizedFlag = true; //set a global flag
}
catch (Exception)
{
}
}
private void mainForm_Resize(object sender, EventArgs e)
{
//check if form is minimized, and you know that this method is only called if and only if the form get a change in size, meaning somebody clicked in the taskbar on your application
if (minimizedFlag == true)
{
panelUC.Visible = true; //make your panel visible again! thats it
minimizedFlag = false; //set flag back
}
}
Thumb up!

Categories