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
Related
namespace ConwayGameofLife
{
public partial class GameController : Window
{
public int row;
public int col;
public SolidColorBrush brush;
public Rectangle[,] rectangle;
public GridCell cell;
public string[,] cellPosition;
public int cellColumn;
public int cellRow;
public GameController()
{
InitializeComponent();
brush = new SolidColorBrush(Colors.Gray);
}
private void GridSlider_Button(object sender, RoutedPropertyChangedEventArgs<double> e)
{
row = (int)gridSlider.Value;
col = (int)gridSlider.Value;
resizeGrid.DataContext = gridSlider;
}
private void BuildGrid_Button(object sender, RoutedEventArgs e)
{
gameUniformGrid.Children.Clear();
gameUniformGrid.Rows = row;
gameUniformGrid.Columns = col;
rectangle = new Rectangle[row, col];
for (int x = 0; x < row; x++)
{
for (int y = 0; y < col; y++)
{
rectangle[y, x] = new Rectangle { Stroke = Brushes.Black, Fill = brush };
gameUniformGrid.Children.Add(rectangle[y, x]);
}
}
//for (int i = 0; i < rectangle.Length; i++)
//{
// rectangle[i] = new Rectangle { Stroke = Brushes.Black, Fill = brush };
// gameUniformGrid.Children.Add(rectangle[i]);
//}
}
On the ToggleGrid function, I wanted to set cellColumn and cellRow variable to the value of the column and row of where the clicked rectangle is on a uniform grid and be able to change the rectangle's color using the index value of those variables. I did some research on ways I can go about this and couldn't find anything useful.
private void ToggleGrid(object sender, MouseButtonEventArgs e) //changes color
{
if (e.OriginalSource is Shape s)
{
cellColumn = (int)GetBoundingBox(s, gameUniformGrid).X;
cellRow = (int)GetBoundingBox(s, gameUniformGrid).Y;
rectangle[cellColumn, cellRow].Fill = new SolidColorBrush(Colors.Green);
//s.Fill = new SolidColorBrush(Colors.Green);
//cellPosition[y, x] = "Dead";
}
MessageBox.Show($"Column Clicked: {cellColumn}, Row Clicked: {cellRow}");
}
private static Rect GetBoundingBox(FrameworkElement element, FrameworkElement parent)
{
GeneralTransform transform = element.TransformToAncestor(parent);
Point topLeft = transform.Transform(new Point(0, 0));
Point bottomRight = transform.Transform(new Point(element.ActualWidth, element.ActualHeight));
return new Rect(topLeft, bottomRight);
}
}
though rectangles are placed into UniformGrid, it is possible to assign Grid.Row and Grid.Column properties for them:
rectangle[x, y] = new Rectangle { Stroke = Brushes.Black, Fill = brush };
rectangle[x, y].SetValue(Grid.RowProperty, x);
rectangle[x, y].SetValue(Grid.ColumnProperty, y);
gameUniformGrid.Children.Add(rectangle[x, y]);
then read them:
if (e.OriginalSource is Shape s)
{
cellColumn = (int)s.GetValue(Grid.ColumnProperty);
cellRow = (int)s.GetValue(Grid.RowProperty);
rectangle[cellRow, cellColumn].Fill = Brushes.Green;
}
So I have some code that creates a highlight effect on top of a picture box with gdi32.dll and I am wondering if there is a simpler way to do it with System.Drawing.Graphics? Basically with the gdi32.dll, I have to capture a screen shot after drawing, post that to my picturebox and then I am able to draw more stuff and change the color of the pen that I use. If I just try to change the pen thickness and color and draw on the screen again, if changes what I already drew.
Now I have a version of this that uses System.Drawing.Graphics and lots of math with FillPolygon but if I draw over an area I already drew on, it just makes the area I drew on darker. It does not do this with gdi32.dll which justr shades as long as you have not already shaded the area with the mouse. Any suggestions?
public partial class Form9 : Form
{
private bool is_mouse_down { get; set; } // Will check if the mouse is down or not.
private Color Pen_Color = new Color();
private int Pen_Type { get; set; }
private int Thickness { get; set; }
private bool Start { get; set; }
List<Point> Points = new List<Point>();
public Form9()
{
InitializeComponent();
pictureBox1.Dock = DockStyle.Fill;
Pen_Color = Color.Blue;
Pen_Type = 13; // Type = 9 for highlighter, Type = 13 for solid.
Thickness = 2;
Start = false;
pictureBox1.MouseDown += pictureBox1_MouseDown;
pictureBox1.MouseUp += pictureBox1_MouseUp;
pictureBox1.MouseMove += pictureBox1_MouseMove;
pictureBox1.Paint += pictureBox1_OnPaint;
}
private void DrawHighlight(Graphics g, Point[] usePoints, int brushSize, int penType, Color brushColor)
{
int useColor = System.Drawing.ColorTranslator.ToWin32(brushColor);
IntPtr pen = GetImage.GDI32.CreatePen(GetImage.GDI32.PS_SOLID, brushSize, (uint)useColor);
IntPtr hDC = g.GetHdc();
IntPtr xDC = GetImage.GDI32.SelectObject(hDC, pen);
GetImage.GDI32.SetROP2(hDC, penType);//GetImage.GDI32.R2_MASKPEN);
for (int i = 1; i <= usePoints.Length - 1; i++)
{
Point p1 = usePoints[i - 1];
Point p2 = usePoints[i];
GetImage.GDI32.MoveToEx(hDC, p1.X, p1.Y, IntPtr.Zero);
GetImage.GDI32.LineTo(hDC, p2.X, p2.Y);
}
GetImage.GDI32.SetROP2(hDC, GetImage.GDI32.R2_COPYPEN);
GetImage.GDI32.SelectObject(hDC, xDC);
GetImage.GDI32.DeleteObject(pen);
g.ReleaseHdc(hDC);
}
private void pictureBox1_OnPaint(object sender, PaintEventArgs e)
{
if (Start)
{
base.OnPaint(e);
if (is_mouse_down)
{
DrawHighlight(e.Graphics, Points.ToArray(), Thickness, Pen_Type, Pen_Color);
}
}
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
Points.Clear();
Start = true;
is_mouse_down = true;
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
is_mouse_down = false;
using (Image img = CaptureScreen())
{
try
{
if (System.IO.File.Exists(Program.ProgramPath + #"\Temp\marked.bmp"))
{
System.IO.File.Delete(Program.ProgramPath + #"\Temp\marked.bmp");
}
}
catch (Exception Ex)
{
MessageBox.Show("File Delete Error" + Environment.NewLine + Convert.ToString(Ex));
}
try
{
img.Save(Program.ProgramPath + #"\Temp\marked.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
}
catch (Exception Ex)
{
MessageBox.Show("Unable to save Screenshot" + Environment.NewLine + Convert.ToString(Ex));
}
}
if (System.IO.File.Exists(Program.ProgramPath + #"\Temp\marked.bmp"))
{
using (FileStream fs = new System.IO.FileStream(Program.ProgramPath + #"\Temp\marked.bmp", System.IO.FileMode.Open, System.IO.FileAccess.Read, FileShare.Read))
{
pictureBox1.Image = Image.FromStream(fs);
}
}
pictureBox1.Invalidate(); // Refreshes picturebox image.
}
public Image CaptureScreen()
{
GetImage gi = new GetImage();
return gi.CaptureWindow(GetImage.User32.GetDesktopWindow());
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (is_mouse_down == true) // Check to see if the mouse button is down while moving over the form.
{
Points.Add(new Point(e.X, e.Y));
pictureBox1.Invalidate(); // Refreshes picturebox image.
}
}
Here are a couple photos of what I am talking about:
Using System.Drawing.Graphics:
Using gdi32.dll:
UPDATE
After testing some of your code...I got a some strange stuff.
This is a way to draw multiple independent stroke of semi-transparent color without piling up the alpha:
It uses tow lists, one for the strokes and one for the current stroke:
List<List<Point>> strokes = new List<List<Point>>();
List<Point> currentStroke = new List<Point>();
They are filled in the usual way
private void canvas_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button.HasFlag(MouseButtons.Left))
{
currentStroke.Add(e.Location);
if (currentStroke.Count == 1)
currentStroke.Add(new Point(currentStroke[0].X + 1,
currentStroke[0].Y));
canvasInvalidate();
}
}
private void canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button.HasFlag(MouseButtons.Left))
{
currentStroke.Add(e.Location);
canvas.Invalidate();
}
}
private void canvas_MouseUp(object sender, MouseEventArgs e)
{
if (currentStroke.Count > 1)
{
strokes.Add(currentStroke.ToList());
currentStroke.Clear();
}
canvas.Invalidate();
}
In this version we avoid overlay effects of overlapping strokes by drawing all pixels in only one call. All the pixels are painted by creating a GraphicsPath from the strokes and and filling it:
private void canvas_Paint(object sender, PaintEventArgs e)
{
if (strokes.Count > 0 || currentStroke.Count > 0)
{
GraphicsPath gp = new GraphicsPath();
gp.FillMode = FillMode.Winding;
if (currentStroke.Count > 0)
{
gp.AddCurve(currentStroke.ToArray());
gp.CloseFigure();
}
foreach (var stroke in strokes)
{
gp.AddCurve(stroke.ToArray());
gp.CloseFigure();
}
using (SolidBrush b = new SolidBrush(Color.FromArgb(77, 177, 99, 22)))
{
e.Graphics.FillPath(b, gp);
}
}
}
Note that you should take care not to move back onto the current stroke while drawing it or else the croosing path parts will create holes!
A Clear or Save Button are simple, the former Clears the two list and invalidates, the latter would use DrawToBitmap to save the control..
Note: To avoid flicker do make sure the canval Panel is DoubleBuffered!
Update:
Here is another way that uses a Pen to draw the overlay. To avoid the piling up of alpha and changed color values (depending on the PixelFormat) it uses a fast function to modify all set pixels in the overlay to have the same overlay color:
The stroke collection code is the same. The Paint is reduced to calling a function to create an overlay bitmap and drawing it:
private void canvas_Paint(object sender, PaintEventArgs e)
{
using (Bitmap bmp = new Bitmap(canvas.ClientSize.Width,
canvas.ClientSize.Height, PixelFormat.Format32bppPArgb))
{
PaintToBitmap(bmp);
e.Graphics.DrawImage(bmp, 0, 0);
}
The first function does the drawing, pretty much like before, but with simple pen strokes:
private void PaintToBitmap(Bitmap bmp)
{
Color overlayColor = Color.FromArgb(77, 22, 99, 99);
using (Graphics g = Graphics.FromImage(bmp))
using (Pen p = new Pen(overlayColor, 15f))
{
p.MiterLimit = p.Width / 2;
p.EndCap = LineCap.Round;
p.StartCap = LineCap.Round;
p.LineJoin = LineJoin.Round;
g.SmoothingMode = SmoothingMode.AntiAlias;
if (currentStroke.Count > 0)
{
g.DrawCurve(p, currentStroke.ToArray());
}
foreach (var stroke in strokes)
g.DrawCurve(p, stroke.ToArray());
}
SetAlphaOverlay(bmp, overlayColor);
}
It also calls the function that 'flattens' all set pixels to the overlay color:
void SetAlphaOverlay(Bitmap bmp, Color col)
{
Size s = bmp.Size;
PixelFormat fmt = bmp.PixelFormat;
Rectangle rect = new Rectangle(Point.Empty, s);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, fmt);
int size1 = bmpData.Stride * bmpData.Height;
byte[] data = new byte[size1];
System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, data, 0, size1);
for (int y = 0; y < s.Height; y++)
{
for (int x = 0; x < s.Width; x++)
{
int index = y * bmpData.Stride + x * 4;
if (data[index + 0] + data[index + 1] + data[index + 2] > 0)
{
data[index + 0] = col.B;
data[index + 1] = col.G;
data[index + 2] = col.R;
data[index + 3] = col.A;
}
}
}
System.Runtime.InteropServices.Marshal.Copy(data, 0, bmpData.Scan0, data.Length);
bmp.UnlockBits(bmpData);
}
It uses LockBits, so it is pretty fast..
Here it is in action:
Update 2:
Just for the fun of it here is an extension of only a few lines that adds the option of drawing filled curves:
The fill mode is stored in a cheapo hack by having the 1st element twice. These are the changes:
In the MouseDown:
currentStroke.Add(e.Location);
if (cbx_Fill.Checked)
currentStroke.Add(e.Location);
And in the PaintToBitmap:
g.SmoothingMode = SmoothingMode.AntiAlias;
if (currentStroke.Count > 0)
{
if (cbx_Fill.Checked)
g.FillClosedCurve(b, currentStroke.ToArray());
else
g.DrawCurve(p, currentStroke.ToArray());
}
foreach (var stroke in strokes)
if (stroke[0]==stroke[1])
g.FillClosedCurve(b, stroke.ToArray());
else
g.DrawCurve(p, stroke.ToArray());
And one more demo:
I am creating dynamic text fields and storing Position and font etc details in an Arraylist. So for example if I click 3 times on Form I am generating 3 random numbers and showing it on clicked position on form. I have one selector button when it is clicked then adding more text functionality is disabled.
Now after when selector button is clicked and if I click ( MouseDown event) on any text then it should move along mouse until MouseUp event is not fired and place on new drop position.
After hours of struggling and searching I found This Solution, So I saved the position and checked with IsPointOnLine method but still its not draggable.
Thank you for any help.
Update: managed to get foreach on place but its still not dragging the selected element.
Form1.Class
public partial class Form1 : Form
{
private Point mouseDownPosition = new Point(0, 0);
private Point mouseMovePosition = new Point(0, 0);
private int mousePressdDown;
IList drawnItemsList = new List<DrawingData>();
private bool dragging;
private bool isSelectorClicked; // if selector button is clicked
DrawingData draggingText;
Random rnd;
int clicked = 0;
public Form1()
{
InitializeComponent();
this.rnd = new Random();
this.isSelectorClicked = false;
dragging = false;
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
mouseMovePosition = e.Location;
if (e.Button == MouseButtons.Left)
mousePressdDown = 1;
if (isSelectorClicked)
{
foreach (DrawingData a in drawnItemsList)
{
if (a.IsPointOnLine(e.Location, 5))
{
draggingText = a;
dragging = true;
}
}
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mouseDownPosition = e.Location;
if (dragging)
{
draggingText.cur = mouseDownPosition;
this.Invalidate();
}
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (mousePressdDown == 1 && isSelectorClicked == false)
{
label1.Text = "X: " + mouseMovePosition.X.ToString();
label2.Text = "Y: " + mouseMovePosition.Y.ToString();
this.Invalidate();
DrawingData a = new DrawingData(mouseMovePosition, mouseDownPosition, rnd.Next().ToString(), FontStyle.Bold, 30, "Candara", Brushes.Blue);
drawnItemsList.Add(a);
this.clicked++;
}
mousePressdDown = 0;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
if(drawnItemsList.Count != 0)
{
foreach (DrawingData a in drawnItemsList)
{
draw(e.Graphics, a);
}
if (mousePressdDown != 0)
{
draw(e.Graphics, mouseDownPosition, mouseMovePosition, FontStyle.Bold, 30, "Candara", Brushes.Blue);
}
}
}
private void draw(Graphics e, Point mold, Point mcur, FontStyle fontWeight, int fontSize, string fontName, Brush fontColor)
{
Pen p = new Pen(Color.Black, 2);
using (Font useFont = new Font(fontName, fontSize, fontWeight))
{
string header2 = rnd.Next().ToString();
RectangleF header2Rect = new RectangleF();
int moldX = mold.X - 5;
int moldY = mold.Y;
header2Rect.Location = new Point(moldX, moldY);
header2Rect.Size = new Size(600, ((int)e.MeasureString(header2, useFont, 600, StringFormat.GenericTypographic).Height));
e.DrawString(header2, useFont, fontColor, header2Rect);
}
}
private void draw(Graphics e, DrawingData a)
{
Pen p = new Pen(Color.Black, 2);
using (Font useFont = new Font(a.FontName, a.FontSize, a.FontWeight))
{
string header2 = rnd.Next().ToString();
RectangleF header2Rect = new RectangleF();
int moldX = a.old.X - 5;
int moldY = a.old.Y;
header2Rect.Location = new Point(moldX, moldY);
header2Rect.Size = new Size(600, ((int)e.MeasureString(header2, useFont, 600, StringFormat.GenericTypographic).Height));
e.DrawString(a.Rand, useFont, a.FontColor, header2Rect);
}
}
private void Select_button_Click(object sender, EventArgs e)
{
this.isSelectorClicked = true;
}
private void WriteNewText_button_Click(object sender, EventArgs e)
{
this.isSelectorClicked = false;
}
}
DrawingData.Class
[Serializable]
public class DrawingData
{
private Point Start; // mouseDown position
private Point End; // mouseUp poslition
private string randValue; // random data value
private FontStyle fontWeight;
private int fontSize;
private string fontName;
private Brush fontColor;
public DrawingData()
{
Start = new Point(0, 0);
End = new Point(0, 0);
randValue = String.Empty;
fontWeight = FontStyle.Bold;
}
public DrawingData(Point old, Point cur, string rand, FontStyle fs, int fSize, string fName, Brush fColor)
{
Start = old;
End = cur;
randValue = rand;
fontWeight = fs;
fontSize = fSize;
fontName = fName;
fontColor = fColor;
}
public float slope
{
get
{
return (((float)End.Y - (float)Start.Y) / ((float)End.X - (float)Start.X));
}
}
public float YIntercept
{
get
{
return Start.Y - slope * Start.X;
}
}
public bool IsPointOnLine(Point p, int cushion)
{
float temp = (slope * p.X + YIntercept);
if (temp >= (p.Y - cushion) && temp <= (p.Y + cushion))
{
return true;
}
else
{
return false;
}
}
public FontStyle FontWeight
{
get
{
return fontWeight;
}
set
{
fontWeight = value;
}
}
public int FontSize
{
get
{
return fontSize;
}
set
{
fontSize = value;
}
}
public string FontName
{
get
{
return fontName;
}
set
{
fontName = value;
}
}
public Brush FontColor
{
get
{
return fontColor;
}
set
{
fontColor = value;
}
}
public Point old
{
get
{
return Start;
}
set
{
Start = value;
}
}
public Point cur
{
get
{
return End;
}
set
{
End = value;
}
}
public string Rand
{
get
{
return randValue;
}
set
{
randValue = value;
}
}
}
SOLVED: I was missing these lines in my mouse move method:
m.Start = new Point(deltaStart.X + e.Location.X, deltaStart.Y + e.Location.Y);
m.End = new Point(deltaEnd.X + e.Location.X, deltaEnd.Y + e.Location.Y);
You should move stuff in Form1_MouseMove instead of Form1_MouseUp.
Find the element you want to move in the Arraylist and assign it new position.
Redraw the element.
Also you should use IList<DrawingData> instead of ArrayList
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
}
}
}
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.