I would like to create a control the floats (potentially) outside the bounds of it's containing form. Is this possible? How may I do it?
This would function much like Context Menu's only I need to be able to add other controls to it such as buttons and images.
You want a Form with it's FormBorderStyle set to None, if you want it to behave like a context menu then you'll need to tie showing it to the appropriate event handler in your main form. Simple example below of setting the location and calling show from a mouse click event handler.
MyForm form = new MyForm();
form.Location = PointToScreen(new Point(e.X, e.Y));
form.Show();
It is possible, the TopLevel property controls this. However, the designer doesn't support them well, hard to keep control over controls that are also top-level windows at design time.
Beyond components like ToolTip and ContextMenuStrip, there is exactly one class that is top-level by design, the Form class. Set its FormBorderStyle to None and ControlBox to False to create a basic top-level window that you can use and populate with other controls.
Take a look at the DockPanel Suite source and adopt the technique.
here is u can made for all Control floating Style
private void Panel_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
Panel.Left += e.X - PanelMouseDownLocation.X;
Panel.Top += e.Y - PanelMouseDownLocation.Y;
}
}
private void Panel_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) PanelMouseDownLocation = e.Location;
}
public Point PanelMouseDownLocation { get; set; }
It would need to be a separate window (much like a context menu actually is) -- you could wrap it as a control, that displays a modeless form (which would even give you the option for non rectangular windows if you really wanted to). As you could create the window from a non-visible control from the parent form, you can maintain a reference to the child for handling inter-form communication.
Have your UserControl override CreateParams. For example:
[DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]
public static extern IntPtr GetDesktopWindow();
protected override CreateParams CreateParams
{
get
{
var cp = base.CreateParams;
cp.ExStyle &= 0x00080000; // WS_EX_LAYERED
cp.Style = 0x40000000 | 0x4000000; // WS_CHILD | WS_CLIPSIBLINGS
cp.Parent = GetDesktopWindow();
return cp;
}
}
This may have unintended effects (including not working well with Designer). I am choosing to follow one of the above patterns, but I thought it was worth mentioning here. Lookup CreateParams to see its purpose. (This option was gleaned from this page.)
This worked for me
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Windows.Forms;
using LollipopUIControls.UIManagers;
namespace Gamasis.Apps.Controls
{
public class FloatingButton : Button
{
public FloatingButton()
{
SetStyle((ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.ResizeRedraw | ControlStyles.SupportsTransparentBackColor | ControlStyles.UserPaint), true);
DoubleBuffered = true;
Size = new Size(50, 50);
BackColor = Color.Transparent;
SF.Alignment = StringAlignment.Center;
SF.LineAlignment = StringAlignment.Center;
AnimationTimer.Tick += new EventHandler(AnimationTick);
}
#region Variables
Timer AnimationTimer = new Timer { Interval = 1 };
FontManager font = new FontManager();
StringFormat SF = new StringFormat();
Rectangle StringRectangle;
bool Focus = false;
int margintop = 0, marginleft = 0, marginright = 0, marginBottom = 0;
int xx;
int yy;
float SizeAnimation = 0;
float SizeIncNum;
string fontcolor = "#FAFAFA";
string Backcolor = "#039BE5";
Color EnabledBGColor;
Color EnabledBorderColor;
Color StringColor;
Color DisabledBGColor = ColorTranslator.FromHtml("#B0BEC5");
Color DisabledStringColor = ColorTranslator.FromHtml("#FAFAFA");
Color NonColor = ColorTranslator.FromHtml("#e3e5e7");
Image bGImage = null;
#endregion
#region Properties
[Category("Custom")]
public string BGColor
{
get { return Backcolor; }
set
{
Backcolor = value;
Invalidate();
}
}
[Category("Custom")]
public string FontColor
{
get { return fontcolor; }
set
{
fontcolor = value;
Invalidate();
}
}
[Browsable(false)]
public Font Font
{
get { return base.Font; }
set { base.Font = value; }
}
[Browsable(false)]
public Color ForeColor
{
get { return base.ForeColor; }
set { base.ForeColor = value; }
}
[Category("Custom")]
public Image BGImage
{
get { return bGImage; }
set { bGImage = value; }
}
ImageSizeLevel bGimgSize = ImageSizeLevel.peque2;
public ImageSizeLevel BGimgSize
{
get { return bGimgSize; }
set { bGimgSize = value; }
}
#endregion
#region Events
protected override void OnMouseEnter(EventArgs e)
{
base.OnMouseEnter(e);
EnabledBGColor = Color.FromArgb(30, ColorTranslator.FromHtml(BGColor));//StringColor);
EnabledBorderColor = Color.FromArgb(20, ColorTranslator.FromHtml(BGColor));//StringColor);
Refresh();
}
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
EnabledBGColor = ColorTranslator.FromHtml(BGColor);
EnabledBorderColor = ColorTranslator.FromHtml(BGColor);
Refresh();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
EnabledBGColor = Color.FromArgb(30, StringColor);
Refresh();
xx = e.X;
yy = e.Y;
Focus = true;
AnimationTimer.Start();
Invalidate();
}
protected override void OnMouseUp(MouseEventArgs e)
{
base.OnMouseUp(e);
Focus = false;
AnimationTimer.Start();
Invalidate();
}
protected override void OnTextChanged(System.EventArgs e)
{
base.OnTextChanged(e);
Invalidate();
}
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
//StringRectangle = new Rectangle(3, 0, Width - 6, Height - 6);
}
#endregion
protected override void OnResize(System.EventArgs e)
{
base.OnResize(e);
//SizeIncNum = Width / 34;
SizeIncNum = Width / 10;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var G = e.Graphics;
#region Default rectangle
//G.SmoothingMode = SmoothingMode.HighQuality | SmoothingMode.AntiAlias;
//G.Clear(Parent.BackColor);
//StringColor = ColorTranslator.FromHtml(fontcolor);
//var BG = DrawHelper.CreateRoundRect(1, 1, Width - 3, Height - 3, 1);
//Region region = new Region(BG);
//G.FillPath(new SolidBrush(Enabled ? EnabledBGColor : Color.White), BG);
//G.DrawPath(new Pen(Enabled ? EnabledBorderColor : Color.White), BG);
//G.SetClip(region, CombineMode.Replace);
////The Ripple Effect
//G.FillEllipse(new SolidBrush(Color.FromArgb(30, StringColor)), xx - (SizeAnimation / 2), yy - (SizeAnimation / 2), SizeAnimation, SizeAnimation);
//G.DrawString(Text, font.Roboto_Medium10, new SolidBrush(Enabled ? StringColor : DisabledStringColor), R, SF);
#endregion
#region Circle
//G.SmoothingMode = SmoothingMode.AntiAlias;
//G.Clear(BackColor);
//GraphicsPath bgbtn = new GraphicsPath();
//bgbtn.AddEllipse(0, 0, Width - 5, Height - 5);
//GraphicsPath bgShadow = new GraphicsPath();
//bgShadow.AddEllipse(0, 0, Width - 2, Height - 2);
//G.FillPath(new SolidBrush(NonColor), bgShadow);
//G.DrawPath(new Pen(NonColor), bgShadow);
//G.FillPath(new SolidBrush(Color.DeepSkyBlue), bgbtn);
//G.DrawPath(new Pen(Color.DeepSkyBlue), bgbtn);
#endregion
///----------------------------
G.SmoothingMode = SmoothingMode.AntiAlias;
G.Clear(Parent.BackColor);
StringColor = ColorTranslator.FromHtml(fontcolor);
//var BG = DrawHelper.CreateRoundRect(1, 1, Width - 3, Height - 3, 1);
//Círculo principal
GraphicsPath bgbtn = new GraphicsPath();
bgbtn.AddEllipse(2, 0, Width - 6, Height - 6);
//Círculo para la sombra
GraphicsPath bgShadow = new GraphicsPath();
bgShadow.AddEllipse(2, 4, Width - 6, Height - 6);
// se dibuja la sombra
G.FillPath(new SolidBrush(NonColor), bgShadow);
G.DrawPath(new Pen(NonColor), bgShadow);
//sedibuja el círculo principal sobre la sombra
G.FillPath(new SolidBrush(Enabled ? ColorTranslator.FromHtml(BGColor) : DisabledBGColor), bgbtn);
G.DrawPath(new Pen(Enabled ? ColorTranslator.FromHtml(BGColor) : DisabledBGColor), bgbtn);
// Se da a la región forma de círculo/elipse
Region region = new Region(bgbtn);//BG);
G.SetClip(region, CombineMode.Replace);
//The Ripple Effect
if (Enabled)
G.FillEllipse(new SolidBrush(Color.FromArgb(30, EnabledBGColor)), xx - (SizeAnimation / 2), yy - (SizeAnimation / 2), SizeAnimation, SizeAnimation);
StringRectangle = new Rectangle((int)bgbtn.GetBounds().Location.X, (int)bgbtn.GetBounds().Location.Y,
(int)bgbtn.GetBounds().Size.Width, (int)bgbtn.GetBounds().Size.Height);
G.DrawString(Text, font.Roboto_Medium15, new SolidBrush(Enabled ? StringColor : DisabledStringColor), StringRectangle, SF);
if (bGImage != null)
{
float imgX = 0, imgY = 0;
imgY = (bgbtn.GetBounds().Size.Height - (int)bGimgSize) / 2;
imgX = ((bgbtn.GetBounds().Size.Width - (int)bGimgSize) + 2) / 2;
G.DrawImage(bGImage, imgX, imgY, (float)bGimgSize, (float)bGimgSize);
}
}
protected void AnimationTick(object sender, EventArgs e)
{
if (Focus)
{
if (SizeAnimation < Width + 250)
{
SizeAnimation += SizeIncNum;
this.Invalidate();
}
}
else
{
if (SizeAnimation > 0)
{
SizeAnimation = 0;
this.Invalidate();
}
}
}
public enum ImageSizeLevel
{
peque = 12, peque1 = 24, peque2 = 32,
maso = 48, maso1 = 56, maso2 = 64,
grande = 72, grande1 = 86, grande2 = 96,
monstruo = 128, monstruo1 = 256, monstruo2 = 512
}
}
}
Related
I am trying to add a close button on the tab pages of TabControl and
change the color of the close button from light gray to black when
mouse hovers over it. However, the color never changes.
The DrawEventArgsCustom class is created to indicate that the mouse is
hovering over the close button. When it's true, the statement to
change the color is executed but color never changes.
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
try
{
Rectangle r = e.Bounds;
r = this.tabControl1.GetTabRect(e.Index);
r.Offset(2, 2);
Brush TitleBrush = new SolidBrush(Color.Black);
Brush CloseBrush = new SolidBrush(Color.Gray);
Brush CloseBrushSelected = new SolidBrush(Color.Black);
Font f = this.Font;
string title = this.tabControl1.TabPages[e.Index].Text;
e.Graphics.DrawString(title, f, TitleBrush, new PointF(r.X, r.Y));
if (e is DrawEventArgsCustom)
{
if (((DrawEventArgsCustom)e) != null && ((DrawEventArgsCustom)e).HoverTrue == true)
e.Graphics.DrawString("x", f, CloseBrushSelected, new PointF
(r.X + (this.tabControl1.GetTabRect(e.Index).Width - _imageLocation.X), _imageLocation.Y));
}
e.Graphics.DrawString("x", f, CloseBrush, new PointF
(r.X + (this.tabControl1.GetTabRect(e.Index).Width - _imageLocation.X), _imageLocation.Y));
}
catch (Exception ex)
{
}
}
private void tabControl1_MouseMove(object sender, MouseEventArgs e)
{
Rectangle mouseRect = new Rectangle(e.X, e.Y, 1, 1);
Graphics graphics = CreateGraphics();
for (int i = 0; i < tabControl1.TabCount; i++)
{
if (tabControl1.GetTabRect(i).IntersectsWith(mouseRect))
{
tabControl1_DrawItem(this, new DrawEventArgsCustom(hoverTrue: true, graphics, this.Font, mouseRect, i, DrawItemState.Focus));
}
}
}
class DrawEventArgsCustom : DrawItemEventArgs
{
public DrawEventArgsCustom(bool hoverTrue, Graphics graphics, Font font, Rectangle rect, int index, DrawItemState drawItemState)
: base(graphics, font, rect, index, drawItemState)
{
this.HoverTrue = hoverTrue;
this.Graph = graphics;
this.Fnt = font;
this.Rect = rect;
this.ind = index;
this.drawItemSt = drawItemState;
}
public bool HoverTrue { get; private set; }
public Graphics Graph { get; private set; }
public Font Fnt { get; private set; }
public Rectangle Rect { get; private set; }
public int ind { get; private set; }
public DrawItemState drawItemSt { get; private set; }
}
No need to create new Graphics objects like that, you should do all the drawings in the DrawItem event. For example in this context:
//a class level variable.
private int HoverIndex = -1;
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
var g = e.Graphics;
var tp = tabControl1.TabPages[e.Index];
var rt = e.Bounds;
var rx = new Rectangle(rt.Right - 20, (rt.Y + (rt.Height - 12)) / 2 + 1, 12, 12);
if ((e.State & DrawItemState.Selected) != DrawItemState.Selected)
{
rx.Offset(0, 2);
}
rt.Inflate(-rx.Width, 0);
rt.Offset(-(rx.Width / 2), 0);
using (Font f = new Font("Marlett", 8f))
using (StringFormat sf = new StringFormat()
{
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center,
Trimming = StringTrimming.EllipsisCharacter,
FormatFlags = StringFormatFlags.NoWrap,
})
{
g.DrawString(tp.Text, tp.Font ?? Font, Brushes.Black, rt, sf);
g.DrawString("r", f, HoverIndex == e.Index ? Brushes.Black : Brushes.LightGray, rx, sf);
}
tp.Tag = rx;
}
Note that, now the Tag property of each TabPage control holds a rectangle for the x button.
In the MouseMove event iterate through the TabPages, cast the x rectangle from the Tag property, check if the x rectangle contains the current e.Location, and call Invalidate(); method of the TabControl to update the drawing:
private void tabControl1_MouseMove(object sender, MouseEventArgs e)
{
for (int i = 0; i < tabControl1.TabCount; i++)
{
var rx =(Rectangle)tabControl1.TabPages[i].Tag;
if (rx.Contains(e.Location))
{
//To avoid the redundant calls.
if (HoverIndex != i)
{
HoverIndex = i;
tabControl1.Invalidate();
}
return;
}
}
//To avoid the redundant calls.
if (HoverIndex != -1)
{
HoverIndex = -1;
tabControl1.Invalidate();
}
}
In the MouseLeave event invalidate if necessary:
private void tabControl1_MouseLeave(object sender, EventArgs e)
{
if (HoverIndex != -1)
{
HoverIndex = -1;
tabControl1.Invalidate();
}
}
And to close/dispose a page, handle the MouseUp event:
private void tabControl1_MouseUp(object sender, MouseEventArgs e)
{
for(int i = 0; i < tabControl1.TabCount; i++)
{
var rx = (Rectangle)tabControl1.TabPages[i].Tag;
if (rx.Contains(rx.Location)) //changed e.Location to rx.Location
{
tabControl1.TabPages[i].Dispose();
return;
}
}
}
Related Posts
TabControl with Close and Add Button
I am working on a project wherein I need to add a Control with the shape of a Circle with some text in the middle.
My problem is the circle is too small, when I resize it, it overlaps other controls. I want to draw the circle same width as the square.
Otherwise. how can I make the Control's background transparent?
I am using the code below:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (Bitmap bitmap = new Bitmap(this.Width, this.Height))
{
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.Clear(this.BackColor);
using (SolidBrush brush = new SolidBrush(this._FillColor))
{
graphics.FillEllipse(brush, 0x18 - 6, 0x18 - 6, (this.Width - 0x30) + 12, (this.Height - 0x30) + 12);
}
Brush FontColor = new SolidBrush(this.ForeColor);
SizeF MS = graphics.MeasureString(Convert.ToString(Convert.ToInt32((100 / _Maximum) * _Value)), Font);
graphics.DrawString(Convert.ToString(Convert.ToInt32((100 / _Maximum) * _Value)), Font, FontColor, Convert.ToInt32((Width / 2 - MS.Width / 2) + 2), Convert.ToInt32((Height / 2 - MS.Height / 2) + 3));
bitmap.MakeTransparent(this.BackColor);
e.Graphics.DrawImage(bitmap, 0, 0);
graphics.Dispose();
bitmap.Dispose();
}
}
}
This is a Custom Control derived from Control, which can be made translucent.
The interface is a colored circle which can contain a couple of numbers.
The Control exposes these custom properties:
Opacity: The level of opacity of the control BackGround [0, 255]
InnerPadding: The distance between the inner rectangle, which defines the circle bounds and the control bounds.
FontPadding: The distance between the Text and the Inner rectangle.
Transparency is obtained overriding CreateParams, then setting ExStyle |= WS_EX_TRANSPARENT;
The Control.SetStyle() method is used to modify the control behavior, adding these ControlStyles:
▶ ControlStyles.Opaque: prevents the painting of a Control's BackGround, so it's not managed by the System. Combined with CreateParams to set the Control's Extended Style to WS_EX_TRANSPARENT, the Control becomes completely transparent.
▶ ControlStyles.SupportsTransparentBackColor the control accepts Alpha values for it's BackGround color. Without also setting ControlStyles.UserPaint it won't be used to simulate transparency. We're doing that ourselves with other means.
To see it at work, create a new Class file, substitute all the code inside with this code preserving the NameSpace and build the Project/Solution.
The new Custom Control will appear in the ToolBox. Drop it on a Form. Modify its custom properties as needed.
A visual representation of the control:
Note and disclaimer:
This is a prototype Control, the custom Designer is missing (cannot post that here, too much code, also connected to a framework).
As presented here, it can be used to completely overlap other Controls in a Form or other containers. Partial overlapping is not handled in this simplified implementation.
The Font is hard-coded to Segoe UI, since this Font has a base-line that simplifies the position of the text in the middle of the circular area.
Other Fonts have a different base-line, which requires more complex handling.
See: TextBox with dotted lines for typing for the base math.
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Text;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
[DesignerCategory("Code")]
public class RoundCenterLabel : Label, INotifyPropertyChanged, ISupportInitialize
{
private const int WS_EX_TRANSPARENT = 0x00000020;
private bool IsInitializing = false;
private Point MouseDownLocation = Point.Empty;
private readonly int fontPadding = 4;
private Font m_CustomFont = null;
private Color m_BackGroundColor;
private int m_InnerPadding = 0;
private int m_FontPadding = 25;
private int m_Opacity = 128;
public event PropertyChangedEventHandler PropertyChanged;
public RoundCenterLabel() => InitializeComponent();
private void InitializeComponent()
{
SetStyle(ControlStyles.Opaque |
ControlStyles.SupportsTransparentBackColor |
ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
m_CustomFont = new Font("Segoe UI", 50, FontStyle.Regular, GraphicsUnit.Pixel);
BackColor = Color.LimeGreen;
ForeColor = Color.White;
}
protected override CreateParams CreateParams {
get {
var cp = base.CreateParams;
cp.ExStyle |= WS_EX_TRANSPARENT;
return cp;
}
}
public new Font Font
{
get => m_CustomFont;
set {
m_CustomFont = value;
if (IsInitializing) return;
FontAdapter(value, DeviceDpi);
NotifyPropertyChanged();
}
}
public override string Text {
get => base.Text;
set { base.Text = value;
NotifyPropertyChanged();
}
}
public int InnerPadding {
get => m_InnerPadding;
set {
if (IsInitializing) return;
m_InnerPadding = ValidateRange(value, 0, ClientRectangle.Height - 10);
NotifyPropertyChanged(); }
}
public int FontPadding {
get => m_FontPadding;
set {
if (IsInitializing) return;
m_FontPadding = ValidateRange(value, 0, ClientRectangle.Height - 10);
NotifyPropertyChanged();
}
}
public int Opacity {
get => m_Opacity;
set { m_Opacity = ValidateRange(value, 0, 255);
UpdateBackColor(m_BackGroundColor);
NotifyPropertyChanged();
}
}
public override Color BackColor {
get => m_BackGroundColor;
set { UpdateBackColor(value);
NotifyPropertyChanged();
}
}
protected override void OnLayout(LayoutEventArgs e)
{
base.OnLayout(e);
base.AutoSize = false;
}
private void NotifyPropertyChanged([CallerMemberName] string PropertyName = null)
{
InvalidateParent();
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
MouseDownLocation = e.Location;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.Button == MouseButtons.Left) {
var loc = new Point(Left + (e.X - MouseDownLocation.X), Top + (e.Y - MouseDownLocation.Y));
InvalidateParent();
BeginInvoke(new Action(() => Location = loc));
}
}
private void InvalidateParent()
{
Parent?.Invalidate(Bounds, true);
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
using (var format = new StringFormat(StringFormatFlags.LineLimit | StringFormatFlags.NoWrap, CultureInfo.CurrentUICulture.LCID))
{
format.LineAlignment = StringAlignment.Center;
format.Alignment = StringAlignment.Center;
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
using (var circleBrush = new SolidBrush(m_BackGroundColor))
using (var foreBrush = new SolidBrush(ForeColor))
{
FontAdapter(m_CustomFont, e.Graphics.DpiY);
RectangleF rect = InnerRectangle();
e.Graphics.FillEllipse(circleBrush, rect);
e.Graphics.DrawString(Text, m_CustomFont, foreBrush, rect, format);
};
};
}
public void BeginInit() => IsInitializing = true;
public void EndInit()
{
IsInitializing = false;
Font = new Font("Segoe UI", 50, FontStyle.Regular, GraphicsUnit.Pixel);
FontPadding = m_FontPadding;
InnerPadding = m_InnerPadding;
}
private RectangleF InnerRectangle()
{
(float Min, _) = GetMinMax(ClientRectangle.Height, ClientRectangle.Width);
var size = new SizeF(Min - (m_InnerPadding / 2), Min - (m_InnerPadding / 2));
var position = new PointF((ClientRectangle.Width - size.Width) / 2,
(ClientRectangle.Height - size.Height) / 2);
return new RectangleF(position, size);
}
private void FontAdapter(Font font, float dpi)
{
RectangleF rect = InnerRectangle();
float fontSize = ValidateRange(
(int)(rect.Height - m_FontPadding), 6,
(int)(rect.Height - m_FontPadding)) / (dpi / 72.0F) - fontPadding;
m_CustomFont.Dispose();
m_CustomFont = new Font(font.FontFamily, fontSize, font.Style, GraphicsUnit.Pixel);
}
private void UpdateBackColor(Color color)
{
m_BackGroundColor = Color.FromArgb(m_Opacity, Color.FromArgb(color.R, color.G, color.B));
base.BackColor = m_BackGroundColor;
}
private int ValidateRange(int Value, int Min, int Max)
=> Math.Max(Math.Min(Value, Max), Min); // (Value < Min) ? Min : ((Value > Max) ? Max : Value);
private (float, float) GetMinMax(float Value1, float Value2)
=> (Math.Min(Value1, Value2), Math.Max(Value1, Value2));
}
I have a panel that has a collection of custom drawn controls. I know how to programmically scroll the panel but the problem is how the controls draw when scrolling. when scrolling right the control shows as normal but when scrolling left it is not looking correctly. This just the start of the complete application so I just have some basic testing in place right now. Need to find a way for the control to draw correctly when scrolling.
Form code:
public partial class Form1 : Form
{
Panel pn;
private int location = 0;
public Form1()
{
InitializeComponent();
pn = new Panel()
{
Width = this.ClientRectangle.Width - 20,
Height = 120,
BackColor = Color.Black,
Left = 5,
Top = 20
};
pn.AutoScroll = false;
pn.VerticalScroll.Maximum = 100;
pn.HorizontalScroll.Maximum = this.ClientRectangle.Width - 100;
pn.VerticalScroll.Visible = false;
pn.HorizontalScroll.Visible = false;
pn.AutoScrollPosition = new Point(0, 0);
pn.AutoScroll = true;
this.KeyPreview = true;
this.KeyDown += new KeyEventHandler(keyPress);
for(int i = 0; i<10;i++)
{
CustomControl1 cc = null;
if (i % 2 != 0)
cc = new CustomControl1()
{
isOdd = true,
Width = 100,
Height = 100,
Left = (100*i)+5,
Top = 0
};
else
cc = new CustomControl1()
{
isOdd = false,
Width = 100,
Height = 100,
Left = (100 * i) + 5,
Top = 0
};
pn.Controls.Add(cc);
}
this.Controls.Add(pn);
}
private void keyPress(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.A:
if(location - 20 >0)
{
location -= 20;
pn.HorizontalScroll.Value = location;
}
else
{
location = 0;
pn.AutoScrollPosition = new Point(location, 0);
}
break;
case Keys.D:
if(location +20 < pn.HorizontalScroll.Maximum)
{
location += 20;
pn.HorizontalScroll.Value = location;
}
else
{
location = pn.VerticalScroll.Maximum;
pn.AutoScrollPosition = new Point(location, 0);
}
break;
}
foreach(Control c in pn.Controls)
{
c.Invalidate();
}
}
}
Control Code:
public partial class CustomControl1 : Control
{
public bool isOdd { get; set; }
public CustomControl1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
Graphics gr = pe.Graphics;
Rectangle rc = new Rectangle(pe.ClipRectangle.X,pe.ClipRectangle.Y,100,100);
Brush br = new SolidBrush(Color.Red);
if (isOdd)
br = new SolidBrush(Color.Yellow);
gr.FillEllipse(br, rc);
base.OnPaint(pe);
}
protected override void OnPaintBackground(PaintEventArgs pe)
{
base.OnPaintBackground(pe);
}
}
Don't clip your drawing:
//Rectangle rc = new Rectangle(pe.ClipRectangle.X, pe.ClipRectangle.Y, 100, 100);
Rectangle rc = new Rectangle(0, 0, 100, 100);
This is the code:
private void hsMagnfier_OnMouseDown(object sender)
{
int x = mLastCursorPosition.X;
int y = mLastCursorPosition.Y;
MagnifierForm magnifier = new MagnifierForm(mConfiguration, System.Windows.Forms.Cursor.Position);//mLastCursorPosition);
magnifier.Show();
}
This code above is in a Form which I can drag over the screen.
Then when I click on an icon it's doing the magnifier.Show(); and the magnifier form is shown up where the mouse current position is.
But if I click on it again so now the position of the new form the magnifier is in my Form1 center. And not where the mouse current position as in the first time.
This is the MagnifierForm code maybe first time it's in the current mouse position but in the next time/s it's in the center of Form1 ?
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.IO;
using System.Drawing.Imaging;
namespace ScreenVideoRecorder
{
public partial class MagnifierForm : Form
{
public MagnifierForm(Configuration configuration, Point startPoint)
{
InitializeComponent();
//--- My Init ---
mConfiguration = configuration;
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = mConfiguration.ShowInTaskbar;
TopMost = mConfiguration.TopMostWindow;
Width = mConfiguration.MagnifierWidth;
Height = mConfiguration.MagnifierHeight;
// Make the window (the form) circular
GraphicsPath gp = new GraphicsPath();
gp.AddEllipse(ClientRectangle);
Region = new Region(gp);
mImageMagnifier = Properties.Resources.magnifierGlass;
mTimer = new Timer();
mTimer.Enabled = true;
mTimer.Interval = 20;
mTimer.Tick += new EventHandler(HandleTimer);
mScreenImage = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
Screen.PrimaryScreen.Bounds.Height);
mStartPoint = startPoint;
mTargetPoint = startPoint;
if (mConfiguration.ShowInTaskbar)
ShowInTaskbar = true;
else
ShowInTaskbar = false;
}
protected override void OnShown(EventArgs e)
{
RepositionAndShow();
}
private delegate void RepositionAndShowDelegate();
private void RepositionAndShow()
{
if (InvokeRequired)
{
Invoke(new RepositionAndShowDelegate(RepositionAndShow));
}
else
{
// Capture the screen image now!
Graphics g = Graphics.FromImage(mScreenImage);
g.CopyFromScreen(0, 0, 0, 0, new Size(mScreenImage.Width, mScreenImage.Height));
g.Dispose();
if (mConfiguration.HideMouseCursor)
Cursor.Hide();
else
Cursor = Cursors.Cross;
Capture = true;
if (mConfiguration.RememberLastPoint)
{
mCurrentPoint = mLastMagnifierPosition;
Cursor.Position = mLastMagnifierPosition;
Left = (int)mCurrentPoint.X - Width / 2;
Top = (int)mCurrentPoint.Y - Height / 2;
}
else
{
mCurrentPoint = Cursor.Position;
}
Show();
}
}
void HandleTimer(object sender, EventArgs e)
{
float dx = mConfiguration.SpeedFactor * (mTargetPoint.X - mCurrentPoint.X);
float dy = mConfiguration.SpeedFactor * (mTargetPoint.Y - mCurrentPoint.Y);
if (mFirstTime)
{
mFirstTime = false;
mCurrentPoint.X = mTargetPoint.X;
mCurrentPoint.Y = mTargetPoint.Y;
Left = (int)mCurrentPoint.X - Width / 2;
Top = (int)mCurrentPoint.Y - Height / 2;
return;
}
mCurrentPoint.X += dx;
mCurrentPoint.Y += dy;
if (Math.Abs(dx) < 1 && Math.Abs(dy) < 1)
{
mTimer.Enabled = false;
}
else
{
// Update location
Left = (int)mCurrentPoint.X - Width / 2;
Top = (int)mCurrentPoint.Y - Height / 2;
mLastMagnifierPosition = new Point((int)mCurrentPoint.X, (int)mCurrentPoint.Y);
}
Refresh();
}
protected override void OnMouseDown(MouseEventArgs e)
{
mOffset = new Point(Width / 2 - e.X, Height / 2 - e.Y);
mCurrentPoint = PointToScreen(new Point(e.X + mOffset.X, e.Y + mOffset.Y));
mTargetPoint = mCurrentPoint;
mTimer.Enabled = true;
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (mConfiguration.CloseOnMouseUp)
{
Close();
mScreenImage.Dispose();
}
Cursor.Show();
Cursor.Position = mStartPoint;
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mTargetPoint = PointToScreen(new Point(e.X + mOffset.X, e.Y + mOffset.Y));
mTimer.Enabled = true;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
if (mConfiguration.DoubleBuffered)
{
// Do not paint background (required for double buffering)!
}
else
{
base.OnPaintBackground(e);
}
}
protected override void OnPaint(PaintEventArgs e)
{
if (mBufferImage == null)
{
mBufferImage = new Bitmap(Width, Height);
}
Graphics bufferGrf = Graphics.FromImage(mBufferImage);
Graphics g;
if (mConfiguration.DoubleBuffered)
{
g = bufferGrf;
}
else
{
g = e.Graphics;
}
if (mScreenImage != null)
{
Rectangle dest = new Rectangle(0, 0, Width, Height);
int w = (int)(Width / mConfiguration.ZoomFactor);
int h = (int)(Height / mConfiguration.ZoomFactor);
int x = Left - w / 2 + Width / 2;
int y = Top - h / 2 + Height / 2;
g.DrawImage(
mScreenImage,
dest,
x, y,
w, h,
GraphicsUnit.Pixel);
}
if (mImageMagnifier != null)
{
g.DrawImage(mImageMagnifier, 0, 0, Width, Height);
}
if (mConfiguration.DoubleBuffered)
{
e.Graphics.DrawImage(mBufferImage, 0, 0, Width, Height);
}
}
//--- Data Members ---
#region Data Members
private Timer mTimer;
private Configuration mConfiguration;
private Image mImageMagnifier;
private Image mBufferImage = null;
private Image mScreenImage = null;
private Point mStartPoint;
private PointF mTargetPoint;
private PointF mCurrentPoint;
private Point mOffset;
private bool mFirstTime = true;
private static Point mLastMagnifierPosition = Cursor.Position;
#endregion
}
}
The first time the new Form the magnifier is shown up where my mouse cursour is.
The next time i click on it's showing the magnifier form in the center of Form1 and not where the mouse cursour is.
Why is that ? When i clikc on the icon again it's still doing the
System.Windows.Forms.Cursor.Position
Again. Strange.
Consider you have two forms - Master and Child
If you are calling Child from Master on MouseUp event(for example), write the code in MouseUp event of Master form
ChildForm obj=new ChildForm();
obj.pntLocation = new Point(Cursor.Position.X, Cursor.Position.Y);
obj.ShowDialog();
Declare a variable inside the Child for location
public Point pntLocation;
Now set location inside the Form_Load of Child
this.Location = pntLocation;
Ok found that the part that doing it is here in the Magnifier form:
mConfiguration.RememberLastPoint = false;
if (mConfiguration.RememberLastPoint)
{
mCurrentPoint = mLastMagnifierPosition;
Cursor.Position = mLastMagnifierPosition;
Left = (int)mCurrentPoint.X - Width / 2;
Top = (int)mCurrentPoint.Y - Height / 2;
}
else
{
mCurrentPoint = Cursor.Position;
}
So I added for now the line: mConfiguration.RememberLastPoint = false; which did the job for now.
I have a class that extends TabControl, basically to have one with the tabs down the left hand side instead of along the top. To do I have set it to be custom drawn.
The problem is when this is put onto a form via the designer, it easily makes the designer loose track of itself and just give up and display nothing. To fix it I have to close the designer and re-open it and then everything is fine until I hit debug, or I go to a code window for a while and come back, where it gives up again.
Is there anything I can do to help visual studio a bit? As while its not throwing errors, it is getting a little tedious now. I'm using visual studio 2008.
Here is the code that extends the tab control, if anyone sees any issues that could be causing this it'd be very appreciated.
public class VerticalTabControl : TabControl
{
private Color tabColour1 = Color.AliceBlue;
public Color TabColour1
{
get { return tabColour1; }
set { tabColour1 = value; this.Refresh(); }
}
private Color tabColour2 = Color.White;
public Color TabColour2
{
get { return tabColour2; }
set { tabColour2 = value; this.Refresh(); }
}
private Color selectedTabColor1 = Color.AliceBlue;
public Color SelectedTabColor1
{
get { return selectedTabColor1; }
set { selectedTabColor1 = value; this.Refresh(); }
}
private Color selectedTabColor2 = Color.White;
public Color SelectedTabColor2
{
get { return selectedTabColor2; }
set { selectedTabColor2 = value; this.Refresh(); }
}
private Color backgroundColour = Color.White;
public Color BackgroundColour
{
get { return backgroundColour; }
set { backgroundColour = value; this.Refresh(); }
}
private Color tabTextColour = Color.Black;
public Color TabTextColour
{
get { return tabTextColour; }
set { tabTextColour = value; this.Refresh(); }
}
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
this.Parent.Resize += new EventHandler(Parent_Resize);
}
void Parent_Resize(object sender, EventArgs e)
{
this.Refresh();
}
public VerticalTabControl()
: base()
{
this.Alignment = TabAlignment.Left;
this.SizeMode = TabSizeMode.Fixed;
this.ItemSize = new Size(50, 120);
this.DrawMode = TabDrawMode.OwnerDrawFixed;
this.DrawItem += new DrawItemEventHandler(VerticalTabControl_DrawItem);
}
void VerticalTabControl_DrawItem(object sender, DrawItemEventArgs e)
{
Graphics g = e.Graphics;
TabControl ctrl = sender as TabControl;
String sText = ctrl.TabPages[e.Index].Text;
Rectangle r = new Rectangle(e.Bounds.Left, e.Bounds.Top, e.Bounds.Width, e.Bounds.Height);
if (e.Index == ctrl.SelectedIndex)
{
using (LinearGradientBrush gb = new LinearGradientBrush(r, this.selectedTabColor1, this.selectedTabColor2, LinearGradientMode.Horizontal))
{
e.Graphics.FillRectangle(gb, r);
}
}
else
{
using (LinearGradientBrush gb = new LinearGradientBrush(r, this.tabColour1, this.tabColour2, LinearGradientMode.Horizontal))
{
e.Graphics.FillRectangle(gb, r);
}
}
// Set up the page and the various pieces.
TabPage page = ctrl.TabPages[e.Index];
// Set up the offset for an icon, the bounding rectangle and image size and then fill the background.
int iconOffset = 0;
Rectangle tabBackgroundRect = e.Bounds;
// If we have images, process them.
if (this.ImageList != null)
{
// Get sice and image.
Size size = this.ImageList.ImageSize;
Image icon = null;
if (page.ImageIndex > -1)
icon = this.ImageList.Images[page.ImageIndex];
else if (page.ImageKey != "")
icon = this.ImageList.Images[page.ImageKey];
// If there is an image, use it.
if (icon != null)
{
Point startPoint = new Point(tabBackgroundRect.X + 6,
tabBackgroundRect.Y + 2 + ((tabBackgroundRect.Height - size.Height) / 2));
e.Graphics.DrawImage(icon, new Rectangle(startPoint, size));
iconOffset = size.Width + 4;
}
}
// Draw out the label.
SizeF sizeText = g.MeasureString(sText, ctrl.Font);
int iX = e.Bounds.Left + 6 + iconOffset;
int iY = e.Bounds.Top + (e.Bounds.Height / 2) - (int)(sizeText.Height / 2);
using (Brush ForeBrush = new SolidBrush(tabTextColour))
{
g.DrawString(sText, ctrl.Font, ForeBrush, iX, iY);
}
Rectangle rec = ctrl.GetTabRect(ctrl.TabPages.Count - 1);
Rectangle recF = new Rectangle(0, rec.Bottom, this.ItemSize.Height, ctrl.Height - rec.Bottom);
using (SolidBrush bb = new SolidBrush(backgroundColour))
{
g.FillRectangle(bb, recF);
}
}
}
turns out this was the offending bit of code
protected override void OnParentChanged(EventArgs e)
{
base.OnParentChanged(e);
this.Parent.Resize += new EventHandler(Parent_Resize);
}
'this.Parent' was sometimes null and so an exception would have been thrown which would have caused the designer to fail.
Have now fixed this and also tidied up the event hooking so I didn't leave hooks everywhere. Seems to work fine now. Thanks for your help whoever answered this and deleted their answer for some reason.