I have this form that works perfectly fine on resolution 1920 x 1080 (mainly cause I made it using that resolution). I read information about controls from a database (labels and textboxs). This information tells me where to place the controls. Like I said, on 1920 x 1080 everything is placed correctly. When I go down resolutions the form is now bigger than the resolution so I added scroll bars. Issue is that the controls are being placed as if the form that is visable on the screen is all that there is. So if I were to place a textbox at location (4, 90) on the form on lower resolution it might place it at (100,90). Y coord is fine, X coord is not.
Although TableLayoutPanels looks very nice in this case it would be a complete hassle and a waste of time to basically completely redo what I have done already. If I were to have started fresh I probably would have used TableLayoutPanels. Instead what I did was, since the Form was bigger than the resolution, I turned my forms AutoScroll to true (which I said in the question). I then set the AutoScrollPosition to (0,0) on form_shown which makes the horizontal one go to the left and the vertical one to the top using...
this.AutoScrollPosition = new Point(0,0);
Since the controls information is based off of where they are on the screen I just force the form's top left to be located in the top left of the screen and not off. I also had to have an override for the ScrollToControl. Every time a control was placed it would reset back to the center and would mess the placement up. So I added this...
protected override Point ScrollToControl(Control activeControl)
{
return this.AutoScrollPosition;
}
Related
I am developing a WinForms application in C#. It uses a panel to draw an image of the Mandelbrot fractal. In the manual for the program and whenever I post about it somewhere, I recommend people to set their scaling setting to 100%, as otherwise the images won't look nice. This is because on other settings, the image is scaled up after drawing it, and it becomes blurry. All other controls are blurry too.
For example: my panel is 500x500. The scaling in my Windows is set to 125%. When I run the program, the panel is internally still 500x500, but it appears as 625x625, blurry. Instead, when the program is run, I want the panel to internally resize to 625x625, and appear as 625x625 too.
I have found the following solution: I found out about SetProcessDPIAware() (from here). Setting that makes the window not scale (it appears as if it was at the 100% scale setting), but the text does (and without becoming blurry). I can then, at the start of the program, calculate the appropriate multiplier (dpi = DpiX / 96) and give that to a huge method that includes commands like
xentrylabel.Location = new Point((int)(xentrylabel.Location.X * dpi),(int)(xentrylabel.Location.Y * dpi));
xentry.Location = new Point((int)(xentry.Location.X * dpi), (int)(xentry.Location.Y * dpi));
xentry.Size = new Size((int)(xentry.Width * dpi), (int)(xentry.Height * dpi));
One for every control property that might need to be updated. While writing this question, I got this idea and got started with it. However, I realised that this will need very many lines of code, so I wonder if there isn't a built-in way to do this. It seems like an option that many would like to go for, rather than their applications becoming blurry or hard to read on screens with high dpi.
Is this way of manually correcting positions and sizes the way to go, or is there a built-in way to scale the form for other DPI settings by actually scaling everything in the form, instead of scaling the end result?
Edit: From some comments it seems as if SetProcessDPIAware() alone should scale up everything. But in my experience, it doesn't. Here are some screenshots:
Application on 100% scale setting:
https://i.stack.imgur.com/pws9B.png
Application on 125% scale setting without SetProcessDPIAware():
https://i.stack.imgur.com/cE4Wx.png
Application on 125% scale setting with SetProcessDPIAware():
https://i.stack.imgur.com/oVA8W.png
We solved the issue of rendering controls for high DPI screens with scaling greater than 100% in Chem4Word by creating a WPF user control which is hosted in an ElementHost control, which is set to fill the form.
WinForm
+- ElementHost
+- WPFUserControl
Looking at your uploaded images, that's exactly the problem which is solved by using a WPF user control.
I am trying to get an accurate Form.Width on FormClosing().
I have a zoom() method I use to scale a picture in the Form. The form's width is then manually re-sized by me to fit. I then, set the MaximumSize's Width limit to the new width of the form. For our purpose, let's say the width and maxwidth are now 1386.
If I re-size the form in the Windows interface by dragging its edge to the left, the width is being reduced just fine. FormClosing reports the re-sized width accurately. Let's say 1107.
However, if go through the zoom() method I wrote, even though I drag to the left to the same position, FormClosing is reporting the ORIGINAL 1386 width. The only things I do to form size in the zoom() method are set width and maxwidth.
I am at a loss to handle this behavior. I need FormClosing to report the correct size. I think FormClosing is setting the Width to MaximumWidth when it fires. I think I read something about how the Form is set to Hidden by the time the FormClosing event is fired. Maybe being Hidden reverts its size to MaxWidth.
Any help would be much appreciated. I have been struggling with this for a few hours and can't find anything.
Thanks.
Here is the relevant code:
private void zoom()
{
// Re-size form width to slightly more than page width.
// This is percentage-based, meaning 100 = default size.
Size picSize = new Size
{
Width = (int)(originalRenderingSize.Width * integerUpDownZoom.Value / 100),
Height = (int)(originalRenderingSize.Height * integerUpDownZoom.Value / 100),
};
// some code here which I commented-out, and still, the problem exists.
// Set maximum to prevent ugly sizing.
this.MaximumSize = new Size(picSize.Width + 40, Screen.FromControl(this).WorkingArea.Height);
// The problem is this: When this method processes, as shown, a page is rendered and sized according to
// a user-provided zoom level represented by an integerUpDown control. Once rendered, the form should
// re-size its width to match the newly-scaled page's width. This works, too. So far, so good.
//
// But!!! If the user grabs the right side of the form and drags it to the left to reduce the width
// of the form (mind you, this is Windows OS doing this), upon Form.Form_Closing(), you can see a
// quick flash of the form fully-open and unscrolled. Then, in the FIRST line of Form_Closing(),
// the debugger reports the form's width as the fully-open and UNSCROLLED width.
//
// The goal is to set the width upon the conclusion of this zoom, but then, on closing
// get the width of the Form in order to persist it for use the next time the app is run.
//
// 2 options have been tried. Both cause Form_Closing to erroneously report the fully-open width.
// But if BOTH are commented-out, the re-sizing occurs and Form_Closing() reports properly the
// form's scrolled width.
// Bear in mind that Form_Closing is one of those things that can happen anytime or never. This means
// the bug is triggered by EITHER and ONLY the two options below.
// Option 1: Tried first. It sets the width fine.
//
// this.Width = picSize.Width + 40;
// Option 2: It also sets the width fine.
// I think this option causes width to change (it invokes width change, and somewhere in the
// OS's width change, the error occurs.
//this.MinimumSize = new Size(MaximumSize.Width - 1, this.Height);
//this.MinimumSize = new Size(0, 0);
}
Edit: I have new information which should help. The culprit is FormClosing(). Here's what I did: I re-sized the form in Windows until it occupied maybe 500 pixels in width. The horizontal scrollbar on my panel was showing. Then, in the FormClosing event, I added the line: Form.Show(). After that, I put a MessageBox.Show("Hi."). Sure enough, the Show() caused the form to repaint in its full, unscrolled width. It did not maintain its scroll state.
I think I have the solution and will post back after trying. Namely, I need to check Scroll values and operate on those values as well.
Edit 2: FormClosing() also re-sets HorizontalScroll.Value and VerticalScroll.Value to 0. This is a bummer! I wonder how to work around this. I also looked at e.Cancel, but there seems to be no way to take advantage of it, either. It seems e.Cancel just re-shows() the form in its unhidden state.
Sorry I cannot post the code. It would be pointless anyway. Think about it. The Windows OS handles the resizing. Not me. When the user re-sizes the form, it's all on Windows. If user re-sizes the form to 500 pixels wide, but FormClosing() reports it is 1386 pixels wide, no amount of my coding will show us anything new. This is a behavior issue. There is no code to show you how the Windows OS handles form re-sizing.
On FormClosing Event Use:
MessageBox.Show(this.Width.ToString());
You're using another Instance of your form's class, Form.Show() should not work in this event cause Dispose function is called and if you have referenced the same instance it will be disposed as well.
I have an application with some small windows on the screen. I would like to align them to each other, so when I move one close enough, it will automatically be align with the other. Helping me positioning and size them all.
How can I know the position of other windows when there isn't a parent window? Is it possible to know it even if they are different process (applications)?
I am not fully sure what you mean but the following trick is what I use for dynamic layout and it gives me full control over anything, you just need to play around with it and you can easily get the distance between two windows.
This code is copied from a windows phone app but it is easily understood.
width = Convert.ToInt32(Window.Current.Bounds.Width);//gets window width
height = Convert.ToInt32(Window.Current.Bounds.Height);// gets window height
double dist Math.Abs(Btn1.GetValue(Canvas.LeftProperty) - Btn2.GetValue(Canvas.LeftProperty) + Btn2.Width);
note first two lines are only in order to have the proportions right in every single movement and resize you do, you can keep it all proportional to the window size.
So my application runs in fixed size window and in full screen. The problem I'm facing is how to properly scale the current contents of the panel (which depend on the application use) when the window is resized. This is my current code:
private void Form1_ClientSizeChanged(object sender, EventArgs e)
{
System.Drawing.Drawing2D.Matrix transformMatrix = new System.Drawing.Drawing2D.Matrix();
float px = panel2.Width;
float py = panel2.Height;
panel2.Width = this.Width / 2;
panel2.Height = panel2.Width;
panel2.Location = new Point(this.Width - panel2.Width - 30, 30);
transformMatrix.Scale(panel2.Width / px, panel2.Height / py);
panel2.Region.Transform(transformMatrix);
//Rest of the code
}
But the drawn content doesn't scale, and if I use Invalidate() or Refresh() the drawn content gets cleared (the panel is redrawn empty). What am I missing?
.NET doesn't remember what's drawn on the panel, as simple as that. As soon as anything invalidates the windows bitmap buffer (causing a WM_PAINT), it's going to be repainted again. So, you have to draw what you want to draw using the Paint event (or overriding OnPaint).
However, there is another way that might be easier to implement - don't paint into a Panel. Instead, paint into a PictureBox (or rather, a Bitmap assigned to the Image property of the PictureBox). The Bitmap will be reused when invalidating (and redrawing) the picture box, so nothing will be lost. By using PictureBox.ScaleMode, you can define how you want the picture box to scale the bitmap, and it will do so as well as it can.
In any case, transforming the Region property doesn't do anything useful - you're simply changing the region, not doing anything to the drawing itself. To use 2D transformation matrices, you want to apply them on a Graphics object during the drawing (in Paint handler or OnPaint override) - drawing anything on the Graphics object will then transform everything you're trying to draw, which in your case means scaling the painting.
So you have to decide: do you want to just scale a stored bitmap with the painted image, or do you want to redraw it all from scratch (which also means you can pick any level of detail you can provide)?
I think that you're mistaking what the Region property is meant for. According to the MSDN docs (empasis mine, replace 'window' with 'control' when reading):
The window region is a collection of pixels within the window where the operating system permits drawing. The operating system does not display any portion of a window that lies outside of the window region. The coordinates of a control's region are relative to the upper-left corner of the control, not the client area of the control.
All that you're doing is changing the region that the OS will allow painting, which explains why you're not seeing anything. I think that you should be resizing the control when the form is resized, either through Anchor, or through my preference of Dock with several controls, or a panel like TableLayoutPanel where it will handle scaling and relative sizing for you.
Thank you for your answers, but I wrote my own function and logic that serves the purpose for this application. Basically the function checks for the state of the application variables, and calls the appropriate function that originally drew the content, and since those functions use the panel width and height as arguments they properly scale the drawn content and retain the drawing composition.
P.S. I'll accept Luaan's answers since it offers a valid alternative and is complete.
I'm trying to place a fixed tool window in the bottom right portion of my primary screen just above the start menu bar.
I'm using the following code
this.Top = Screen.PrimaryScreen.WorkingArea.Bottom - this.Height;
this.Left = Screen.PrimaryScreen.WorkingArea.Right - this.Width;
The form aligns the edge of the screen to the client rectangle so I see the non client bottom edge bleed into the start menu bar and the right non client edge bleed into the second monitor.
I need to get the full form width and height including all the non client borders
I notice it works when the form is SizableToolWindow but not with FixedToolWindow
Thanks everyone!
The Bounds property will give you a Rectangle from which you can get the full size.
The bounds of the control include the nonclient elements such as scroll bars, borders, title bars, and menus.
Documentation
Hans got this answer right but posted it as comment instead of an answer.
The answer is here:
https://social.msdn.microsoft.com/Forums/windows/en-US/af2608c1-1159-444f-bb21-b2cf0228f1c1/strange-issues-with-formborderstyle-on-vista-between-fixed-and-sizable-modes?forum=winforms
– Hans Passant May 19 at 21:07
Copied from site:
Strange issues with FormBorderStyle on Vista between fixed and sizable modes
Question:
The problem lies on all types of fixed border styles like FixedDialog, Fixed3D, FixedSingle and FixedToolWindow. It does not happen on the sizable ones. This problem, like I said, also happens only on Vista.
Let's say you have a form with any of the fixed border styles and set the starting location to 0,0. What you want here is for the form to be snapped to the top left corner of the screen. This works just fine if the form border style is one of the sizable options, if it's fixed, well, the form will be a little bit outside of the screen working area both to the left and top.
What's more strange about this is that the form location does not change, it sill is 0,0, but a few pixels of the form are drawn outside of the working screen area.
I tested this on XP and it didn't happen, the problem is Vista specific. On XP, the only difference was the border size that change a bit between any of the styles. But the form was always perfectly snapped to position 0,0.
If possible, without finding how many pixels are being drawn outside of the working area and then add that to the form location, is there a possible way to fix or workaround this?
Answer:
The problem is specific to Aero, it doesn't happen when you turn it off. It is one of those famous app-compat fixes. Windows actually lies to you when you P/Invoke GetWindowRect(). It gives window position and size, relative from the client area, that the window would have if it were running on an earlier version of Windows, like XP.
That makes sure that older programs still behave somewhat predictably, they'll have their all-important client area in the same location, even though they have much fatter borders. But gets in the way at times, like your case.
To fix that, you have to convince Vista that you are really aware how Vista works. That requires patching the .exe header. Start the Visual Studio Command Prompt, navigate to your build directory and type this command:
editbin /subsystem:windows,6.0 yourprogram.exe
Yes, the comment from Hans correctly told us why the form's .Height and .Width are not correct. I think the following should work to give you the real height and width, whether or not Aero is enabled.
(in the code for a Form)
[DllImport("dwmapi.dll", PreserveSig = false)]
public static extern bool DwmIsCompositionEnabled();
// When Aero is enabled, and our FormBorderStyle is FixedToolWindow,
// Windows will lie to us about our size and position.
public bool AeroIsMessingWithUs()
{
bool ret = false;
// check for other Fixed styles here if needed
if (FormBorderStyle == System.Windows.Forms.FormBorderStyle.FixedToolWindow)
{
if (Environment.OSVersion.Version.Major >= 6 && DwmIsCompositionEnabled())
{
// Aero is enabled
ret = true;
}
}
return ret;
}
public int MyWindowHeight()
{
int height = Height;
if (AeroIsMessingWithUs())
{
// there are actually 5 more pixels on the top and bottom
height += 10;
}
return height;
}
public int MyWindowWidth()
{
int width = Width;
if (AeroIsMessingWithUs())
{
// there are 5 more pixels on the left and right
width += 10;
}
return width;
}