I have three monitors in the following configuration:
Monitor 1 Monitor 2 Monitor 3
[1280 x 1024] [1200 x 1900] [1280 x 1024]
I use a form to outline a selected monitor (code below), which works great when I initialize or change between Monitors 1 and 3, but results in two rectangles drawn when Monitor 2 is selected :/
I tried to modify the code in many-many different ways, but nothing seems to work. I thought someone might be able to help me understand why two rectangles are being drawn (1280 x 1024 and 1200 x 1900) and how to correct.
Thank you for your time, regards and happy New Year.
P.S. If possible, please keep explanation simple as I am still learning.
public partial class ScreenArea : Form
{
private Pen _pen;
private int screenSelect;
public ScreenArea(int selectScreen = 0)
{
//xInitializeComponent();
TopMost = true;
ShowInTaskbar = true;
FormBorderStyle = FormBorderStyle.None;
BackColor = Color.LightGreen;
TransparencyKey = Color.LightGreen;
_pen = new Pen(Color.Aqua, 5);
Paint += new PaintEventHandler(ScreenArea_Paint);
ScreenSelect = screenSelect;
}
private void ScreenArea_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(_pen, 0, 0, Width, Height);
}
public int ScreenSelect
{
get
{
return screenSelect;
}
set
{
Rectangle screenBounds;
try
{
screenBounds = Screen.AllScreens[value].Bounds;
screenSelect = value;
}
catch (Exception)
{
screenBounds = Screen.AllScreens[screenSelect].Bounds;
}
this.Left = screenBounds.X;
this.Top = screenBounds.Y;
this.Width = screenBounds.Width;
this.Height = screenBounds.Height;
}
}
}
Related
I have a group of radio buttons on the form, which are shaped like a matrix(6x10). I want to draw a pattern or display any number by checking them (i want to use it like dot matrix led). I create radio buttons by software so i can create 60 radio buttons that 20 of them are checked,40 of them are not and draw my pattern but when i change the pattern, i cant draw the new one because if i check one, others become unchecked.
I never click on radio buttons everything works on code.
I need to check them separately so is there any way to check one radio button but avoid others to effect from that and let them remain their status?
this is how it looks
https://i.hizliresim.com/V9m0Vq.jpg
https://i.hizliresim.com/lqmd7l.jpg
when i rotate it, i want all to move towards the ground(bottom of the screen)
but only one of them falls.
Here's a quick "Dot" UserControl you can toggle on/off with its Checked() propperty:
public partial class Dot : UserControl
{
private bool _Checked = false;
public bool Checked
{
get
{
return _Checked;
}
set
{
_Checked = value;
this.Invalidate();
}
}
public Dot()
{
InitializeComponent();
this.DoubleBuffered = true;
this.SizeChanged += Dot_SizeChanged;
}
private void Dot_SizeChanged(object sender, EventArgs e)
{
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
int radius = (int)(Math.Min(this.ClientRectangle.Width, this.ClientRectangle.Height) / 2);
if (radius > 0)
{
double outerCircle = 0.95;
double innerCircle = 0.80;
Rectangle rc = new Rectangle(new Point(0, 0), new Size(1, 1));
rc.Inflate((int)(radius * outerCircle), (int)(radius * outerCircle));
Point center = new Point(this.ClientRectangle.Width / 2, this.ClientRectangle.Height / 2);
e.Graphics.TranslateTransform(center.X, center.Y);
e.Graphics.DrawEllipse(Pens.Black, rc);
if (this.Checked)
{
rc = new Rectangle(new Point(0, 0), new Size(1, 1));
rc.Inflate((int)(radius * innerCircle), (int)(radius * innerCircle));
e.Graphics.FillEllipse(Brushes.Black, rc);
}
}
}
}
I have a winform window. When I change the size of the screen, the screen immediately increases or decreases.
I would prefer the Resize behavior of the window will be like Split Container, as long as I drag the mouse I see only line that marks what will be the window size, and only in leaving the Resize operation will be made.
I saw several examples that show that by hiding the frame of the window, and then by clicking on the window itself paint frame.
I want that by clicking on the frame of the window(I don't want to hide the frame) and not on the window.
Is there any way to do this? (May override the behavior of the Resize in any way).
I'm pretty sure that you can't find any solution on the Internet. However I've tried a demo for this and it works pretty well.
In winforms and many other UI technologies, you can't render something outside the window itself. To get the effect we want, we have to render some indicative border outside or inside the window depending on how user resizes it. So looks like we're stuck?
BUT there is a kind of technique to do that (I call it layer technique). We need a transparent, non-focused layer to render the indicative border. This layer will have its Size and Location synchronized with the Size and Location (with just a little offset) of the main window. The layer will also be invisible by default and only shows when user resizes and hides when ending resizing.
That's pretty OK with the technique I mentioned. However how to prevent/discard the default resizing when user resizes the window? It's luckily that Win32 supports 2 messages for this to be done easily:
WM_RESIZING : is sent to the window when user starts and keeps resizing. The LParam holds the RECT structure of the current window when resizing. We read this info to render the indicative border correctly. Then we need to modify this RECT to the current Bounds of the window to discard the default resizing effect (the size and location are changed immediately).
WM_EXITSIZEMOVE : is sent to the window when the resizing or moving ends. We need to catch this message to assign the Size and Location of the window based on the Size and Location of the transparent layer and of course hide the layer then.
Now the problem is totally solvable. Here is the demo code I've made. Note that there is a very nasty unsolvable and incomprehensible bug here, it happens when you resize the Top-Left corner, the Size is updated correctly after releasing mouse but the Location is set with an offset. I've debugging but no luck. At some point the Top and Left jumps to unexpected values for no clear reason. However, resizing by all the sides (left, top, right, bottom) and other corners is OK. In fact, resizing by the Top-Left corner is hardly done by user so this solution is acceptable, I think.
//Must add using System.Runtime.InteropServices;
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
//Sizing border initialization
SizingBorderWidth = 3;
SizingBorderStyle = DashStyle.Custom;
SizingBorderColor = Color.Orange;
//layer initialization
layer.Owner = this;//especially this one.
layer.Width = Width + SizingBorderWidth * 2;
layer.Height = Height + SizingBorderWidth * 2;
//Paint the border when sizing
layer.Paint += (s, e) => {
using (Pen p = new Pen(SizingBorderColor) { Width = SizingBorderWidth }) {
if (Use3DSizingBorder) {
ControlPaint.DrawBorder3D(e.Graphics, sizingRect.Left, sizingRect.Top, sizingRect.Width, sizingRect.Height, Border3DStyle.Bump, Border3DSide.All);
}
else {
p.DashStyle = SizingBorderStyle;
p.LineJoin = LineJoin.Round;
if(p.DashStyle == DashStyle.Custom)
p.DashPattern = new float[] { 8f, 1f, 1f, 1f };//length of each dash from right to left
e.Graphics.DrawRectangle(p, sizingRect);
}
}
};
//Bind the Location of the main form and the layer form together
LocationChanged += (s, e) => {
Point p = Location;
p.Offset(-SizingBorderWidth, -SizingBorderWidth);
layer.Location = p;
};
//Set the intial Location of layer
Load += (s, e) =>{
Point p = Location;
p.Offset(-SizingBorderWidth, -SizingBorderWidth);
layer.Location = p;
};
}
//Set this to true to use 3D indicative/preview border
public bool Use3DSizingBorder { get; set; }
//Change the indicative/preview border thickness
public int SizingBorderWidth { get; set; }
//Change the indicative/preview border style
public DashStyle SizingBorderStyle { get; set; }
//Change the indicative/preview border color
public Color SizingBorderColor { get; set; }
//hold the current sizing Rectangle
Rectangle sizingRect;
bool startSizing;
bool suppressSizing;
//This is a Win32 RECT struct (don't use Rectangle)
public struct RECT
{
public int left, top, right, bottom;
}
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x214&&!suppressSizing)//WM_SIZING = 0x214
{
RECT rect = (RECT) m.GetLParam(typeof(RECT));
int w = rect.right - rect.left;
int h = rect.bottom - rect.top;
sizingRect = new Rectangle() {X = SizingBorderWidth/2, Y = SizingBorderWidth/2,
Width = w, Height = h};
layer.Left = rect.left-SizingBorderWidth;
layer.Top = rect.top-SizingBorderWidth;
layer.Width = w+2*SizingBorderWidth;
layer.Height = h+2*SizingBorderWidth;
if (!startSizing)
{
layer.Show();
startSizing = true;
}
layer.Invalidate();
//Keep the current position and size fixed
rect.right = Right;
rect.bottom = Bottom;
rect.top = Top;
rect.left = Left;
//---------------------------
Marshal.StructureToPtr(rect, m.LParam, true);
}
if (m.Msg == 0x232)//WM_EXITSIZEMOVE = 0x232
{
layer.Visible = false;
BeginInvoke((Action)(() => {
suppressSizing = true;
Left = layer.Left + SizingBorderWidth;
Top = layer.Top + SizingBorderWidth;
Width = layer.Width - 2 * SizingBorderWidth;
Height = layer.Height - SizingBorderWidth * 2;
suppressSizing = false;
}));
startSizing = false;
}
base.WndProc(ref m);
}
//Here is the layer I mentioned before.
NoActivationForm layer = new NoActivationForm();
}
public class NoActivationForm : Form {
public NoActivationForm() {
//The following initialization is very important
TransparencyKey = BackColor;
FormBorderStyle = FormBorderStyle.None;
ShowInTaskbar = false;
StartPosition = FormStartPosition.Manual;
//----------------------------------------------
}
protected override bool ShowWithoutActivation {
get { return true; }
}
}
Some screen shots:
EDIT: (This edit was suggested by Hodaya Shalom, the OP (weird :)
I found a solution to the left corner problem :
before the BeginInvoke I save the variables and in the invoke I put the local variable:
int _top = layer.Top + SizingBorderWidth;
int _left = layer.Left + SizingBorderWidth;
int _width = layer.Width - 2 * SizingBorderWidth;
int _height = layer.Height - SizingBorderWidth * 2;
BeginInvoke((Action)(() => {
suppressSizing = true;
Left = _left;
Top = _top;
Width =_width;
Height =_height;
suppressSizing = false;
}));
I'm making a simple game in winform (tic-tac-toe), and I'm having some problem to paint block control.
Here is the class I made, that represent a block in the game (without game logic, is only UI).
public class UI_Block : Control
{
private Rectangle block;
private SIGNS sign;
public SIGNS Sign
{
get {return sign;}
set
{
if (sign == SIGNS.EMPTY)
sign = value;
}
}
public UI_Block( ) {
sign = SIGNS.EMPTY;
}
public void SetBlockOnBoard(int x, int y)
{
this.Location = new Point( x , y );
this.Size = new Size(Parent.Width /3, Parent.Height / 3);
block = new Rectangle(this.Location, this.Size);
}
public void DrawSign(Graphics g)
{
Pen myPen = new Pen(Color.Red);
if (sign == SIGNS.O)
{
drawO(g,new Pen(Brushes.Black));
}
if (sign == SIGNS.X)
{
drawX(g, new Pen(Brushes.Red));
}
}
protected override void OnPaint(PaintEventArgs e)
{
DrawSign(e.Graphics);
base.OnPaint(e);
}
//Draw X
private void drawX(Graphics g, Pen myPen)
{
//draw first daignol
Point daignolStart = new Point { X = this.Location.X , Y = this.Location.Y };
Point daignolEnd = new Point { X = this.Size.Width , Y = this.Size.Height };
g.DrawLine(myPen, daignolStart, daignolEnd);
//draw second daignol
daignolStart = new Point { X = Size.Width , Y = this.Location.Y };
daignolEnd = new Point { X = Location.X, Y = Size.Height };
g.DrawLine(myPen, daignolEnd, daignolStart);
}
//Draw O
private void drawO(Graphics g, Pen myPen)
{
g.DrawEllipse(myPen, block);
}
}
I added them both to the winForm class and to see how it looks like when I paint them:
public partial class Form1 : Form
{
UI.UI_Block block;
UI.UI_Block blockX;
public Form1()
{
InitializeComponent();
block = new UI.UI_Block();
blockX = new UI.UI_Block();
Controls.Add(block);
Controls.Add(blockX);
}
protected override void OnLoad(EventArgs e)
{
block. SetBlockOnBoard(0, 0);
blockX.SetBlockOnBoard(0, block.Height);
block.Sign = SIGNS.X;
blockX.Sign = SIGNS.O;
base.OnLoad(e);
}
protected override void OnPaint(PaintEventArgs e)
{
//block.DrawSign(e.Graphics);
//block.DrawSign(e.Graphics);
base.OnPaint(e);
}
}
I tried few things, like not using the onPaint event and I still get the same result.
Here what I see when I run it:
Any idea why I can't paint both of them?
You are not drawing the contents of your control in it's visible area, so it is drawing fine but you can't see it.
Every control has it's own coordinate space (client coords), which starts at 0,0 regardless of where it is positioned within the parent control. You are placing the control in it's parent correctly by setting its Location, but then you are also using the Location to offset the graphics, so they are essentially offset twice.
(If you make your control bigger you'll be able to see the X being drawn further down the screen)
To fix this, do all your drawing in the client coordinate space of your control, i.e. draw in the area (0, 0, width, height)
(P.S. You could just draw all 9 tiles in the parent control, which is a more efficient approach than creating 9 child controls. But what you are doing will work fine)
I'm using this code:
private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
{
this.Show();
this.WindowState = FormWindowState.Normal;
//this.Location = new Point(form1_location_on_x, form1_location_on_y);
//this.StartPosition = FormStartPosition.CenterScreen;
Either the line
this.Location = new Point(form1_location_on_x, form1_location_on_y);
or the line
this.StartPosition = FormStartPosition.CenterScreen;
are working when I'm on my original screen resolution 1920x1080, but once I'm changing the resolution to 1024x768, the Form is on the right bottom corner not hidden I see it all but it's not in the center.
form1_location_on_x and on_y are:
form1_location_on_x = this.Location.X;
form1_location_on_y = this.Location.Y;
The question is what should I do to make it work on any other resolution like 1024x768 or any others? I tried many changes but nothing worked so far.
Size screenSize = Screen.PrimaryScreen.WorkingArea.Size;
Location = new Point(screenSize.Width / 2 - Width / 2, screenSize.Height / 2 - Height / 2);
Make sure that you set StartPosition = FormStartPosition.Manual;
Tested and working with 1920x1080 and 1024 x 768
You could calculate the top and left position of your form using this formula:
int formWidth = yourForm.Width;
int formHeight = yourForm.Height;
int screenH = (Screen.PrimaryScreen.WorkingArea.Top +
Screen.PrimaryScreen.WorkingArea.Height) / 2;
int screenW = (Screen.PrimaryScreen.WorkingArea.Left +
Screen.PrimaryScreen.WorkingArea.Width) / 2;
int top = screenH - formWidth / 2;
int left = screenW - formHeight / 2;
yourForm.Location = new Point(top, left);
Of course, these days, you have the problem of dual monitors.
I don't know if you want your form to appear always on the primary screen or you want the form appear in the current screen (the one where the form is currently displayed). In this second case you need to find where your form is displayed
private void CenterForm(Form yuorForm)
{
foreach(var s in Screen.AllScreens)
{
if(s.WorkingArea.Contains(yourForm.Location))
{
int screenH = s.WorkingArea.Height / 2;
int screenW = s.WorkingArea.Width / 2;
int top = (screenH + s.WorkingArea.Top) - formWidth / 2;
int left = (screenW + s.WorkingArea.Left) - formHeight / 2;
yourForm.Location = new Point(top, left);
break;
}
}
}
EDIT: Thanks to #alex I will complete the answer with the information on SystemEvents class
If you want to be notified by the system when the user suddenly change the resolution of your screen you could subscribe to the event SystemEvents.DisplaySettingsChanged (using Microsoft.Win32; needed)
SystemEvents.DisplaySettingsChanged += new EventHandler(SystemEvents_DisplaySettingsChanged);
and then handle the reposition of your form in the event
// This method is called when the display settings change.
void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
{
RecenterForm(yourForm);
}
try using one of these after the resize:
this.CenterToScreen();
or
this.CenterToParent();
You can use StartPosition property of Form objects. It determines the position of a form. Set it's value to CenterScreen if you want your form to open in the center of the screen
This behavior in C# is bizarre. I have the following class to allow me to effectively 'draw' on the desktop:
class drawOnDesktop {
public static Form dodF = new Form();
public static Graphics formGraphics;
public drawOnDesktop() {
formGraphics = dodF.CreateGraphics();
dodF.BackColor = Color.LightGreen;
dodF.TransparencyKey = Color.LightGreen;
dodF.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
dodF.Location = new Point(0,0);
dodF.StartPosition = FormStartPosition.Manual;
//dodF.FormBorderStyle = FormBorderStyle.None;
dodF.WindowState = FormWindowState.Maximized;
dodF.MinimizeBox = false;
dodF.MaximizeBox = false;
dodF.ControlBox = false;
//dodF.TopMost = true; //For development in case something goes wrong
dodF.BringToFront();
dodF.Show();
}
public static void drawCircle(Point location) {
formGraphics.FillEllipse(Brushes.Black, location.X, location.Y, 10, 10);
}
}
And I call it like this, from my main form:
drawOnDesktop dod = new drawOnDesktop();
drawOnDesktop.drawCircle(new Point(100,100));
If you run that code, you'll get a small black circle in the top left corner of your screen. The problem is that you can see the form's border. Now, try commenting out the FormBorderStyle line. The black dot will appear for a fraction of a second, and disappear. Why!? As you can see, I've set a lot of properties on this form, and still it refuses to work. Is it getting repainted over by the OS?
I don't need to worry about mouse events or things like that - the dots being placed on the screen are completely programmatic, and not from the user. As well, if I set dodF.ShowInTaskbar = false, the entire program crashes.
How can I fix this code so the dot appears and stays until I formGraphics.Clear(Color.Black)?
Don't keep a copy of the graphics around, that is just asking for trouble. As others have stated, you should use the paint event to draw on the screen:
class drawOnDesktop
{
public Form dodF = new Form();
List<Point> circles = new List<Point>();
public drawOnDesktop()
{
dodF.BackColor = Color.LightGreen;
dodF.TransparencyKey = Color.LightGreen;
dodF.Size = new Size(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
dodF.Location = new Point(0, 0);
dodF.StartPosition = FormStartPosition.Manual;
dodF.FormBorderStyle = FormBorderStyle.None;
dodF.WindowState = FormWindowState.Maximized;
dodF.MinimizeBox = false;
dodF.MaximizeBox = false;
dodF.ControlBox = false;
dodF.TopMost = true; //For development in case something goes wrong
dodF.BringToFront();
dodF.Paint += dodF_Paint;
dodF.Show();
}
void dodF_Paint(object sender, PaintEventArgs e)
{
using (Graphics g = dodF.CreateGraphics())
{
foreach(Point location in circles)
g.FillEllipse(Brushes.Black, location.X, location.Y, 10, 10);
}
}
public void drawCircle(Point location)
{
circles.Add(location);
}
}
You can call it the same way, but now every time the form repaints, it will redraw the circles.