I created a .NET Framework 4.0 winforms project and added a "tab" as you can see in the following picture:
From the View Designer both tabs (Profile and Features) looks like they fit the rectangle window, but when I run the program, the "Profile" tab looks the same but the "Features" tab suddenly does not fit the rectangle anymore and looks like that:
I don't want to give the user the option to enlarge the window.
I am wondering why it looks like it fit the rectangle window but when I run the application, it does not.
This is the "Features" tab from the MainForm:
this.TabPage2.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.TabPage2.Controls.Add(this.listBoxShowFriendsBirthday);
this.TabPage2.Controls.Add(this.buttonShowFriendsBirthday);
this.TabPage2.Controls.Add(this.label1);
this.TabPage2.Controls.Add(this.monthCalendar1);
this.TabPage2.Controls.Add(this.postTextBox);
this.TabPage2.Controls.Add(this.pictureBoxEvent);
this.TabPage2.Controls.Add(this.listBoxFetchEvents);
this.TabPage2.Controls.Add(this.buttonFetchEvents);
this.TabPage2.Controls.Add(this.postButton);
this.TabPage2.Location = new System.Drawing.Point(8, 39);
this.TabPage2.Name = "tabPage2";
this.TabPage2.Padding = new System.Windows.Forms.Padding(3);
this.TabPage2.Size = new System.Drawing.Size(1976, 806);
this.TabPage2.TabIndex = 1;
this.TabPage2.Text = "Features";
this.TabPage2.UseVisualStyleBackColor = true;
I suggest you to use Dock and Anchor properties of controls. This way you can also allow to user resizing the window.
Dock:
Anchor:
See - How to: Anchor and Dock Child Controls in a FlowLayoutPanel Control
To prevent the user from rescaling their window:
c# how to prevent user from resizing my application window?
but aside from that, I'd suggest, (just like ikram said) to use docking, that way you're certain your lay-out will remain, no matter the resolution/screensize the user has.
I have a form application in C#. When I change the monitor's DPI, all the controls move.
I used the code this.AutoScaleMode = AutoScaleMode.Dpi, but it didn't avoid the problem.
Does anyone have an idea?
EDIT: As of .NET 4.7, windows forms has improved support for High DPI. Read more about it on learn.microsoft.com It only works for Win 10 Creators Update and higher though, so it might not be feasible to use this yet depending on your user base.
Difficult, but not impossible. Your best option is to move to WPF of course, but that might not be feasible.
I've spent A LOT of time with this problem. Here are some rules/guidelines to make it work correctly without a FlowLayoutPanel or TableLayoutPanel:
Always edit/design your apps in default 96 DPI (100%). If you design in 120DPI (125% f.ex) it will get really bad when you go back to 96 DPI to work with it later.
I've used AutoScaleMode.Font with success, I haven't tried AutoScaleMode.DPI much.
Make sure you use the default font size on all your containers (forms, panels, tabpage, usercontrols etc). 8,25 px. Preferrably it shouldn't be set in the .Designer.cs file at all for all containers so that it uses the default font from the container class.
All containers must use the same AutoScaleMode
Make sure all containers have the below line set in the Designer.cs file:
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); // for design in 96 DPI
If you need to set different font sizes on labels/textboxes etc. set them per control instead of setting the font on the container class because winforms uses the containers font setting to scale it's contents and having f.ex a panel with a different font size than it's containing form is guaranteed to make problems. It might work if the form and all containers on the form use the same font size, but I haven't tried it.
Use another machine or a virtual windows install (VMware, Virtual PC, VirtualBox) with a higher DPI setting to test your design immediatly. Just run the compiled .exe file from the /bin/Debug folder on the DEV machine.
I guarantee that if you follow these guidelines you will be ok, even when you have placed controls with specific anchors and don't use a flowpanel. We have an app built this way deployed on hundreds of machines with different DPI setups and we no longer have any complaints. All forms/containers/grids/buttons/textfield etc sizes are scaled correctly as is the font. Images work too, but they tend to get a little pixellated at high DPI.
EDIT: This link has a lot of good info, especially if you choose to use AutoScaleMode.DPI: link to related stackoverflow question
note: this will not fix the controls moving , when dpi change. this will only fix blurry text!!.
How to fix blurry Windows Forms in high-dpi settings:
Go the the Forms designer, then select your Form (by clicking at
its title bar)
Press F4 to open the Properties window,
then locate the AutoScaleMode property
Change it from Font (default) to Dpi.
Now, go to Program.cs (or the file where your Main method is located) and change it to look like:
namespace myApplication
{
static class Program
{
[STAThread]
static void Main()
{
// ***this line is added***
if (Environment.OSVersion.Version.Major >= 6)
SetProcessDPIAware();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
// ***also dllimport of that function***
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool SetProcessDPIAware();
}
}
Save and compile. Now your form should look crispy again.
source:
http://crsouza.com/2015/04/13/how-to-fix-blurry-windows-forms-windows-in-high-dpi-settings/
I finally found solution to problem of both Screen Orientation and DPI handling.
Microsoft has already provided a document explaining it but with a little flaw that will kill DPI handling completely.
Just follow solution provided in the document below under "Creating Separate Layout Code for Each Orientation"
http://msdn.microsoft.com/en-us/library/ms838174.aspx
Then IMPORTANT part!
Inside the code for Landscape() and Portrait() methods at the very end of each add these lines:
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
So, the code for these 2 methods would be like:
protected void Portrait()
{
this.SuspendLayout();
this.crawlTime.Location = new System.Drawing.Point(88, 216);
this.crawlTime.Size = new System.Drawing.Size(136, 16);
this.crawlTimeLabel.Location = new System.Drawing.Point(10, 216);
this.crawlTimeLabel.Size = new System.Drawing.Size(64, 16);
this.crawlStartTime.Location = new System.Drawing.Point(88, 200);
this.crawlStartTime.Size = new System.Drawing.Size(136, 16);
this.crawlStartedLabel.Location = new System.Drawing.Point(10, 200);
this.crawlStartedLabel.Size = new System.Drawing.Size(64, 16);
this.light1.Location = new System.Drawing.Point(208, 66);
this.light1.Size = new System.Drawing.Size(16, 16);
this.light0.Location = new System.Drawing.Point(192, 66);
this.light0.Size = new System.Drawing.Size(16, 16);
this.linkCount.Location = new System.Drawing.Point(88, 182);
this.linkCount.Size = new System.Drawing.Size(136, 16);
this.linkCountLabel.Location = new System.Drawing.Point(10, 182);
this.linkCountLabel.Size = new System.Drawing.Size(64, 16);
this.currentPageBox.Location = new System.Drawing.Point(10, 84);
this.currentPageBox.Size = new System.Drawing.Size(214, 90);
this.currentPageLabel.Location = new System.Drawing.Point(10, 68);
this.currentPageLabel.Size = new System.Drawing.Size(100, 16);
this.addressLabel.Location = new System.Drawing.Point(10, 4);
this.addressLabel.Size = new System.Drawing.Size(214, 16);
this.noProxyCheck.Location = new System.Drawing.Point(10, 48);
this.noProxyCheck.Size = new System.Drawing.Size(214, 20);
this.startButton.Location = new System.Drawing.Point(8, 240);
this.startButton.Size = new System.Drawing.Size(216, 20);
this.addressBox.Location = new System.Drawing.Point(10, 24);
this.addressBox.Size = new System.Drawing.Size(214, 22);
//note! USING JUST AUTOSCALEMODE WILL NOT SOLVE ISSUE. MUST USE BOTH!
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); //IMPORTANT
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; //IMPORTANT
this.ResumeLayout(false);
}
protected void Landscape()
{
this.SuspendLayout();
this.crawlTime.Location = new System.Drawing.Point(216, 136);
this.crawlTime.Size = new System.Drawing.Size(96, 16);
this.crawlTimeLabel.Location = new System.Drawing.Point(160, 136);
this.crawlTimeLabel.Size = new System.Drawing.Size(48, 16);
this.crawlStartTime.Location = new System.Drawing.Point(64, 120);
this.crawlStartTime.Size = new System.Drawing.Size(248, 16);
this.crawlStartedLabel.Location = new System.Drawing.Point(8, 120);
this.crawlStartedLabel.Size = new System.Drawing.Size(48, 16);
this.light1.Location = new System.Drawing.Point(296, 48);
this.light1.Size = new System.Drawing.Size(16, 16);
this.light0.Location = new System.Drawing.Point(280, 48);
this.light0.Size = new System.Drawing.Size(16, 16);
this.linkCount.Location = new System.Drawing.Point(80, 136);
this.linkCount.Size = new System.Drawing.Size(72, 16);
this.linkCountLabel.Location = new System.Drawing.Point(8, 136);
this.linkCountLabel.Size = new System.Drawing.Size(64, 16);
this.currentPageBox.Location = new System.Drawing.Point(10, 64);
this.currentPageBox.Size = new System.Drawing.Size(302, 48);
this.currentPageLabel.Location = new System.Drawing.Point(10, 48);
this.currentPageLabel.Size = new System.Drawing.Size(100, 16);
this.addressLabel.Location = new System.Drawing.Point(10, 4);
this.addressLabel.Size = new System.Drawing.Size(50, 16);
this.noProxyCheck.Location = new System.Drawing.Point(168, 16);
this.noProxyCheck.Size = new System.Drawing.Size(152, 24);
this.startButton.Location = new System.Drawing.Point(8, 160);
this.startButton.Size = new System.Drawing.Size(304, 20);
this.addressBox.Location = new System.Drawing.Point(10, 20);
this.addressBox.Size = new System.Drawing.Size(150, 22);
//note! USING JUST AUTOSCALEMODE WILL NOT SOLVE ISSUE. MUST USE BOTH!
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); //IMPORTANT
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; //IMPORTANT
this.ResumeLayout(false);
}
Works like charm for me.
It looks like this is a problem with Windows. Taking out these two lines fixed everything.
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
This is where I got the solution:
Anchor not work during changing text size to 125% # Telerik forums
It is really hard to design DPI aware applications in Windows Forms. You would have to use layout containers that resize properly when the DPI is changed (such as TableLayoutPanel or FlowLayoutPanel). All controls need resizing as well. The configuration of those containers can be a challenge.
For simple applications it can be done within a reasonable amount of time, but for big applications it is really alot of work.
From experience:
don't use DPI awareness with windows forms unless critical
to this end always set AutoScaleMode property to None on all forms and user controls in your app
The result: WYSIWYG type of interface when DPI settings change
I struggled with this for a while eventually I found a super simple solution for windows 10 and potentially other systems.
In your WinForms App.config file paste this:
<System.Windows.Forms.ApplicationConfigurationSection>
<add key="DpiAwareness" value="PerMonitorV2" />
</System.Windows.Forms.ApplicationConfigurationSection>
Then create a app.manifest file and paste or comment in this line:
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
After doing the above I was able to get great DPI results on my 4k screens.
Check out this and this for more information.
If you want your WinForms application to be DPI-Aware application, In addition to Trygve good answer, If you have big project you may want to scale your forms and their content automatically, You can do this by creating ScaleByDPI function:
ScaleByDPI function will receive a Control parameter that is usually a form, and than recursively iterate through all sub controls (if (control.HasChildren == true)), and scale location and sizes off your application controls & sizes and sizes of fonts to the OS configured DPI. You can try to implement it also for images, icons & graphics.
Special notes for ScaleByDPI function:
a. For all controls with default Font sizes, you will need to set their Font.Size to 8.25.
b. You can get devicePixelRatioX and devicePixelRatioY values by (control.CreateGraphics().DpiX / 96) and (control.CreateGraphics().DpiY / 96).
c. You will need scale Control.Size & Control.Location by algorithm that based on control.Dock & control.Anchor values. Be noticed that control.Dock may have 1 of 6 possible values and that control.Anchor may have 1 of 16 possible values.
d. this algorithm will need set values to next bool variables isDoSizeWidth, isDoSizeHeight, isDoLocationX, isDoLocationY, isDoRefactorSizeWidth, isDoRefactorSizeHeight, isDoRefactorLocationX, isDoRefactorLocationY, isDoClacLocationXBasedOnRight, isDoClacLocationYBasedOnBottom.
e. If your project uses a control library other then Microsoft controls, this controls may need a special treatment.
More info on above (d.) bool variables:
*Sometimes a group of controls (may be a buttons) need to be placed one after another on same vertical line, and their Anchor value include Right but not Left, or they need to be placed one after another on same horizontal line, and their Anchor value include Bottom but not Top, in this case you need to re-calculate controls Location values.
*In case of controls that Anchor contains Top & Bottom and\or Left & Right, you will need to re-factor controls Size & Location values.
Uses of ScaleByDPI function:
a. Add next command to the end off any Form constructor: ScaleByDPI(this);
b. Also when adding any control dynamically to a Form call to ScaleByDPI([ControlName]).
When you set Size or Location of any control dynamically after constructor ended, create and use one of next functions in order to get the scaled values of Size or Location: ScaleByDPI_X \ ScaleByDPI_Y \ ScaleByDPI_Size \ ScaleByDPI_Point
In order to mark your application as being DPI-aware, add the dpiAware element to your application's assembly manifest.
Set GraphicsUnit of all Control.Font to System.Drawing.GraphicsUnit.Point
In *.Designer.cs files of all containers, set AutoScaleMode value to System.Windows.Forms.AutoScaleMode.None
in controls like ComboBox & TextBox, changing Control.Size.Hieght have no affect. In this case changing Control.Font.Size will fix control's height.
If form StartPosition value is FormStartPosition.CenterScreen, you will need to recalculate the location of the window.
Since a Winform application form may content controls AND images, allowing the system to resize YOUR window is NOT a solution, but if you could manage to have one form per DPI resolution, with properly scaled images... And that's not a good idea, since as the screen size grow, the font size diminishes.
When using a different DPI resolution the system forces your form to redefine its control's size, location and font, BUT NOT IMAGES, the solution is to change the form's DPI at runtime, when loading, so that everything goes back to original size and location.
This is possible solution, which I've tested it with a card game application where I've gott some 80 image buttons, TabControls etc.
In each form form_Load event, add this code snippet:
Dim dpi As Graphics = Me.CreateGraphics
Select Case dpi.DpiX
Case 120
'-- Do nothing if your app has been desigbned with 120 dpi
Case Else
'-- I use 125 AND NOT 120 because 120 is 25% more than 96
Me.Font = New Font(Me.Font.FontFamily, Me.Font.Size * 125 / dpi.DpiX)
End Select
Besides, a quick trick for testing various resolutions on the same computer, without restarting:
From control panel, change the resolution.
Do not restart! Instead close your session and open a new one with same user.
There is another caveat: If you set a control's size and position at runtime, then you should apply the same DPI factor (eg. 125 / Dpi.Dpix) to the new coordinates. So you'd better set up a DPIFactor global variable from application.startup event.
Last but not least:
DO NOT open your application in Visual Studio from another resolution than the original one, or ALL YOUR CONTROLS will move and resize as you open each form, and there is no way back...
Hope this helps, happy programming.
I have a homework, where I need to create a winforms game using C#. I have the following components:
Panel subclass with custom paint event
Panel with default windows UI elements.
I want them to arrange like this:
Because I draw on the center panel manually, I want to set it's Width, and Height fixed, so the Form subclass, what will contain it, would show the whole panel.
I tried setting the size manually in the panel subclass:
Width = someFixedWidth;
Height = someFixedHeight;
Then adding it to the containing Form:
GamePanel panel = new GamePanel(...);
panel.Dock = DockStyle.Center;
this.AutoSize = true;
this.AutoSizeMode = AutoSizeMode.GrowAndShrink;
this.Controlls.Add(panel);
Using this, I thought, that the form will respect the size of the Panel, but it just shrinks the window to so small, that nothing is visible, only the title.
So my question is, how would I be able to set the size of the GamePanel manually, and then dock it in the center of the form, so that the Form will respect the size I set, and doesn't makes it smaller/bigger?
The Dock property is used to define the behavior of the component during resizing Container (Form) The way you did the screen is not centralized but is resized according to the screen changes, the ideal is to use a method to reposition the control and set its size. See this:
SuspendLayout();
Width = someFixedWidth;
Height = someFixedHeight;
panel.Size = new Size(panelWidth, panelHeight);
panel.Location = new Point( ClientSize.Width / 2 - panelWidth / 2, ClientSize.Height / 2 - panelHeight / 2);
panel.Anchor = AnchorStyles.None;
panel.Dock = DockStyle.None;
ResumeLayout();
In my case, I edited minimum height/width of the panel and it worked.
I tried to edit the code which related to design but it was not recommended to rewrite auto-generated code.
Thank you.
So I've written code to create a new Windows form from the parent that is fullscreen, black and opacity of 80% to create a dimming effect.
Then another form is shown above this to show progress.
The code is currently looking like this:
this.Enabled = false;
frmDimScreen.BackColor = Color.Black;
frmDimScreen.Opacity = .8;
frmDimScreen.FormBorderStyle = FormBorderStyle.None;
frmDimScreen.Size = new Size(splitContainerMain.Width - 4, splitContainerMain.Height - 2);
frmDimScreen.StartPosition = FormStartPosition.Manual;
frmDimScreen.ShowIcon = false;
frmDimScreen.ShowInTaskbar = false;
frmDimScreen.Show();
frmDimScreen.Location = new Point(splitContainerMain.Location.X + 2, ((splitContainerMain.Location.Y + tsMainTools.Height) - 4));
addImgIL.ShowIcon = false;
addImgIL.ShowInTaskbar = false;
addImgIL.TopMost = true;
addImgIL.Show();
addImgIL.Location = new Point((this.Width - addImgIL.Width) / 2, (this.Height - addImgIL.Height) / 2);
addImgIL.BringToFront();
Obvioulsy the main reason I'm questioning this is if the parent window loses focus, then the child forms get out of order and are not layered correctly. I know I can handle this via OnPaint, but looking for the best solution.
The question/problem, is this the best method to achieve what I am attempting? Should I be using forms or graphics to create the dimming? What's everyones experience with this?
Thanks in advance!
Another approach using only a single window would be to use an alpha blended image to draw the transparent dark background (you could even have a gradient) with a non-transparent custom drawn progress bar. See http://www.codeproject.com/KB/GDI-plus/perpxalpha_sharp.aspx .
In that project you can find a class that you can use to easily assign an alpha blended Bitmap to a form's background. The problem with this method is that you must custom draw everything on the form, including the progress bar or whatever other controls you need on it.
The effect that you are trying to achieve is kind of unusual for a windows application, that is why the best method is not very straightforward.
I assumed that the C# margin property had a meaning like in CSS - the spacing around the outside of the control. But Margin values seem to be ignored to matter what values I enter.
Then I read on the SDK:
Setting the Margin property on a
docked control has no effect on the
distance of the control from the the
edges of its container.
Given that I'm placing controls on forms, and perhaps docking them, what does the Margin property get me?
Like Philip Rieck said, the margin property is only respected by container controls that perform layout. Here's an example that makes it fairly clear how the TableLayoutPanel respects the Margin property:
using System.Drawing;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
TableLayoutPanel pnl = new TableLayoutPanel();
pnl.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50));
pnl.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 50));
pnl.Dock = DockStyle.Fill;
this.Controls.Add(pnl);
Button btn1 = new Button();
btn1.Text = "No margin";
btn1.Dock = DockStyle.Fill;
Button btn2 = new Button();
btn2.Margin = new Padding(25);
btn2.Text = "Margin";
btn2.Dock = DockStyle.Fill;
pnl.Controls.Add(btn1, 0, 0);
pnl.Controls.Add(btn2, 1, 0);
}
}
}
I believe the only .NET 2.0 built-in controls that respect this property are FlowLayoutPanel and TableLayoutPanel; hopefully third-party components respect it as well. It has basically no effect in other scenarios.
The margin property is used by whatever layout engine your control host (Panel, for example) is using, in whatever way that layout engine sees fit. However, it is best used for spacing just as you assume. Just read the documentation for that specific layout engine.
It can be very handy when using a FlowLayoutPanel or TableLayoutPanel, for example - to either reduce the default padding or space things out a bit. Obviously, if you write a custom layout provider, you can use Margin however you see fit.
Control.Margin property could be also useful at design time if you do not use layout container, but rather place controls manually.
It affects the distance between manually dragged controls at which snaplines appear.
E.g. for default margin value of 3 for text box you would have this snaplines:
And for margin of 10 - these (label has margin of 3 in both cases):
So if you have some strict guidelines for you UI then you just set the margins as you need and drag controls to the snaplines.