Make two form controls resize with window evenly - c#

I have a form that looks like this in designer, two grid views, exactly the same properties. It worked for a bit, but now when I resize it, only the right grid view expands horizontally, they both expand vertically. Also locking the form and controls doesn't stop me from resizing the Form which would be easiest solution.
What could cause this? The only relevant properties on the Grid Views are anchors for Top, Right, Left, Bottom on each. See code at bottom.
Here are some screenshots:
Here is the form in Designer:
And here is the form when I try to resize it:
As you can see the right half is wider, I also cannot resize it normally, as I try a diagonal resize it mainly grows vertically, a horizontal resize does the same thing. I've had the resizing issue forever, but the two gridviews were both resizing equally at first, I made no changes and they stopped. Am I missing something here? Why doesn't locking the form stop it from being resizable? I locked every control as well.
Just in case, here is the code for the grid views in the designer, first the right one:
// clientHistoryTableDataGridView
//
this.clientHistoryTableDataGridView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.clientHistoryTableDataGridView.AutoGenerateColumns = false;
this.clientHistoryTableDataGridView.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells;
this.clientHistoryTableDataGridView.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells;
this.clientHistoryTableDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.clientHistoryTableDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.dataGridViewTextBoxColumn4,
this.dataGridViewTextBoxColumn5,
this.dataGridViewTextBoxColumn6});
this.clientHistoryTableDataGridView.DataSource = this.clientHistoryTableBindingSource;
this.clientHistoryTableDataGridView.Location = new System.Drawing.Point(426, 52);
this.clientHistoryTableDataGridView.Name = "clientHistoryTableDataGridView";
this.clientHistoryTableDataGridView.RowHeadersVisible = false;
this.clientHistoryTableDataGridView.RowTemplate.Resizable = System.Windows.Forms.DataGridViewTriState.True;
this.clientHistoryTableDataGridView.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.clientHistoryTableDataGridView.Size = new System.Drawing.Size(430, 360);
this.clientHistoryTableDataGridView.TabIndex = 4;
this.clientHistoryTableDataGridView.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.clientHistoryTableDataGridView_CellContentClick);
and the left side:
// clientTableDataGridView
//
this.clientTableDataGridView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.clientTableDataGridView.AutoGenerateColumns = false;
this.clientTableDataGridView.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells;
this.clientTableDataGridView.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells;
this.clientTableDataGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
this.clientTableDataGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {
this.dataGridViewTextBoxColumn1,
this.dataGridViewTextBoxColumn2,
this.dataGridViewTextBoxColumn3});
this.clientTableDataGridView.DataSource = this.clientTableBindingSource;
this.clientTableDataGridView.Location = new System.Drawing.Point(1, 52);
this.clientTableDataGridView.Name = "clientTableDataGridView";
this.clientTableDataGridView.RowHeadersVisible = false;
this.clientTableDataGridView.RowTemplate.Resizable = System.Windows.Forms.DataGridViewTriState.True;
this.clientTableDataGridView.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.clientTableDataGridView.Size = new System.Drawing.Size(428, 360);
this.clientTableDataGridView.TabIndex = 3;
this.clientTableDataGridView.CellContentClick += new System.Windows.Forms.DataGridViewCellEventHandler(this.clientTableDataGridView_CellContentClick);
EDIT:
I fixed the resizing issues by using both answers together. And also disabling autosize on the main form and setting borderstyle to sizable.

possible solution:
add SplitContainer with anchor Left|Top|Right|Bottom
set SplitterDistance at half of SplitContainer width
put clientTableDataGridView in left panel and set Dock = Fill
put clientHistoryTableDataGridView in right panel and set Dock = Fill

You can resolve this problem in the following manner:
Set anchors for both gridview as Top | Bottom.
Override OnResize method of your form and in this method set width and location if both gridviews manually like this:
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
var width = ClientRectangle.Width / 2;
clientTableDataGridView.Left = 0;
clientTableDataGridView.Width = width;
clientHistoryTableDataGridView.Left = width;
clientHistoryTableDataGridView.Width = width;
}
And of course, instead of setting anchors in step 1 - you can completly manage gridviews size in OnResize. Here I've mentioned that step just for simplicity.

Related

How to change control's size and add new control while form is minimized

I have a windows form that has 2 panels inside it, one takes up left half of the form, one right. Now when a certain event occurs in my program (I receive a message from server for example), I want to add a new control (say a third panel) between the two existing, so I need to make make them smaller and move them to the sides of my form.
This can happen while the form is minimized and this is where my problem is. Panels Size returns [0,0] when the form is minimized so I cant use it for calculations.
So my first question is, how can I get "original" size of controls while the form is minimized?
And then, even if I somehow calculated the new Size (say I have 400px wide form with 2x 200px panels and I want the new 3rd panel to be 200px wide, so the old panels will become 100px wide), and applied it:
leftPanel.Size = new Size(100, 100);
then after the form is restored from minimized state to normal state, the panel will be way bigger than specified 100x100. Seems like it will restore to the forms ClientSize + the newly specified size
Therefore my question: how can I add and resize controls to form while the form is minimized?
Sample procedures to resize Panels hosted in a Form and adapt the Layout when a new Panel is inserted in the middle of the two existing.
The WindowState of the hosting Form Forms is not relevant (it can be minimized, maximized or in normal state).
► Using the first method, if the Form is maximized, the Panels will retain the initial Height.
► Using the second method, as it is now, the Panels' Height will be set to the Form's ClientSize.Height. It can of course be changed, setting the TableLayoutPanel Row(s) to an Absolute height instead of AutoSize.
Using the Docking feature alone:
Set the Form AutoSizeMode = Dpi
Add two Panels to the Form (e.g., panelLeft and panelRight)
Set the Width of both Panels to 200
Set panelLeft to Dock = DockStyle.Left and panelRight to Dock = DockStyle.Right
Right-click the Panel on the left and select SendToBack (!important)
Adjust the Form Size: it should be: (418, 138). Not important, just for a visual confirmation
In the Form constructor set this.ClientSize = new Size(400, 100);
Add a new public method to the Form:
public void AdjustPanelsWidth(int newWidth)
{
this.panelLeft.Width = newWidth;
this.panelRight.Width = newWidth;
}
When you need to add a new Panel in the middle of the two existing Panels:
(someForm represents the current instance of the minimized Form)
int newSize = 100;
someForm.AdjustPanelsWidth(newSize);
var p = new Panel() {
Size = new Size(newSize * 2, 100),
Dock = DockStyle.Fill
};
p.BringToFront();
someForm.Controls.Add(p);
Using a TableLayoutPanel:
Add a TableLayoutPanel to the Form
Set it to Dock = DockStyle.Top
Edit Columns and Rows to have 3 Columns and 1 Row
Set the Columns Styles in the TLP Designer as:
Columns:
(0) Percent 50%
(1) AutoSize
(2) Percent 50%
Row:
(0) AutoSize
Closing the TLP Designer, it should appear to have just two Columns: since the central one is auto-sized and it has no content, its Width is currently 0.
Add two Panels to the Form (not to the TableLayoutPanel directly)
Set the Size of the Panels = (200, 100)
Drag one Panel inside the left Column of the TLP and the other to the Column on the right
! Verify, in VS Property panel, that the Column property of Panel on the Left is Column 0
The same for the Panel on the Right: the Colum property must be Column 2
If the Column is wrong, edit it manually.
Select both Panels and set both to Dock = DockStyle.Fill
(now you should see the TLP completely filled by the Panels, both occupying 50% of the TLP Size)
Adjust the Form size as before (still not actually important)
In the Form constructor set this.ClientSize = new Size(400, 100); (as before)
Add a public method to the Form:
public void AddControl(Control control)
{
// Add a Control to Column 1 - Row 0
this.tableLayoutPanel1.Controls.Add(control, 1, 0);
panel.Dock = DockStyle.Fill;
}
To add a new Panel in the middle Column:
var p = new Panel() {
Size = new Size(200, 100),
BackColor = Color.Red,
Margin = new Padding(0)
};
someForm.AddControl(p);
Structure of a Form that implements the TableLAyoutPanel method described:
ClockMinimize() => Minimizes the Clock size, squeezing it between two other Panels
ClockShow() => Enlarges the Clock to overlap the other Panels, which will resize to completely fill the Form's ClientArea:
using System.Drawing;
using System.Windows.Forms;
public partial class frmClock : Form
{
public frmClock() => InitializeComponent();
private int m_ClientHeight = 0;
public void ClockShow()
{
this.panClock.Parent = this;
this.panClock.Size = new Size(360, 80);
this.panClock.Location = new Point(20, 10);
// Adjust the Clock Font Size here
this.panClock.BringToFront();
}
public void ClockMinimize()
{
this.panClock.Size = new Size(200, 40);
tableLayoutPanel1.Controls.Add(this.panClock, 1, 0);
this.panClock.Margin = new Padding(0, (m_ClientHeight - this.panClock.Height) / 2, 0, 0);
// Adjust the Clock Font Size here
AdjustPanelsWidth(panClock.Width / 2);
}
public void AdjustPanelsWidth(int newWidth)
{
this.panLeft.Width = newWidth;
this.panRight.Width = newWidth;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.MinimumSize = this.Size;
m_ClientHeight = this.ClientSize.Height;
}
protected override void OnClientSizeChanged(EventArgs e)
{
base.OnClientSizeChanged(e);
if (this.ClientSize.Height > 0) {
m_ClientHeight = this.ClientSize.Height;
}
}
}
I have come up with this workaround: I'm postponing resizing/moving until the form returns from minimized state, using async/await.
Instead of my original function:
public void changeControlPositionAndSize() {
//calculate new size and location based on size and location of neighboring controls
myPanel.Location = ...
myPanel.Size = ...
}
I'm now using:
public async Task changeControlPositionAndSize()
{
while (WindowState == FormWindowState.Minimized)
{
await Task.Delay(2000);
}
//calculate new size and location based on size and location of neighboring controls
myPanel.Location = ...
myPanel.Size = ...
}

How to overlap WPF Images

Is there a way overlap say three images to have them overlap. I am have a case where I bring the top element to the top but it is showing the base layer underneath instead of the second.
Here is what I mean:
Should be this:
The code for it:
// Bottom Box
this.BottomBox.BackColor = Color.Transparent;
this.BottomBox.BackgroundImage = Resource.BottomBox;
this.BottomBox.Name = "BottomBox";
// Middle Box
this.Middle.BackColor = Color.Transparent;
this.Middle.BackgroundImage = Resource.MiddleBox;
this.Middle.Parent = BottomBox;
this.Middle.Name = "Middle";
// Top Box
this.TopBox.BackkColor = Color.Transparent;
this.TopBox.BackgroundImage = Resource.TopBox;
this.TopBox.Parent = MiddleBox;
this.TopBox.Name = "TopBox";
The easiest way to overlap controls/views (images, etc) in WPF is to put them inside a Canvas or a Grid...
In a Canvas, you can alter there relative positions using the attached properties Canvas.Top and Canvas.Left.
In a Grid, you can alter their relative positions using margins...
You can do this in the Constructor of the views immediately below the InitializeComponent method.
Sample Code:
// Bottom Box
this.BottomBox.BackColor = Color.Transparent;
this.BottomBox.BackgroundImage = Resource.BottomBox;
this.BottomBox.Name = "BottomBox";
// Middle Box
this.Middle.BackColor = Color.Transparent;
this.Middle.BackgroundImage = Resource.MiddleBox;
this.Middle.Parent = BottomBox;
this.Middle.Name = "Middle";
// Top Box
this.TopBox.BackkColor = Color.Transparent;
this.TopBox.BackgroundImage = Resource.TopBox;
this.TopBox.Parent = MiddleBox;
this.TopBox.Name = "TopBox";
var canvas = new Canvas();
canvas.Children.Add(this.BottomBox);
canvas.Children.Add(this.TopBox);
canvas.Children.Add(this.Middle);
Canvas.SetLeft(this.BottomBox, 50);
Canvas.SetTop(this.BottomBox, 100);
Canvas.SetLeft(this.Middle, 50);
Canvas.SetTop(this.Middle, 50);
Canvas.SetLeft(this.TopBox, 50);
Canvas.SetTop(this.TopBox, 0);
//put canvas as the main element to display in the view
Try it out and leave a comment if you have any further issue.

Button image sides cropped

I'm trying to fit image to button perfectly.
But the image is cropped on its right and bottom faces, see attached print screen:
I edited the button as follows:
var l_oStopImage = Image.FromFile(#"C:\Users\AmitL\Downloads\Button-2-stop-icon72p.png");
var l_oStopPic = new Bitmap(l_oStopImage , new Size(btnStopOperation.Width, btnStopOperation.Height));
btnStopOperation.Image = l_oStopPic ;
btnStopOperation.ImageAlign = System.Drawing.ContentAlignment.MiddleCenter;
btnStopOperation.TabStop = false;
btnStopOperation.FlatStyle = FlatStyle.Flat;
btnStopOperation.FlatAppearance.BorderSize = 0;
I also tried to edit the BackgroundImageLayout but none of the ImageLayouts fixed the problem..
Any suggestions?
Thanks in advance
1https://msdn.microsoft.com/en-us/library/system.windows.forms.imagelayout(v=vs.110).aspx
You should use stretch, I suggest in designtime (this is not java where you have to add elements by code):
this.buttonOk.BackColor = System.Drawing.SystemColors.MenuHighlight;
this.buttonOk.BackgroundImage = ((System.Drawing.Image)(resources.GetObject("buttonOk.BackgroundImage")));
this.buttonOk.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch;
this.buttonOk.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.buttonOk.Location = new System.Drawing.Point(475, 15);
this.buttonOk.Name = "buttonOk";
this.buttonOk.Size = new System.Drawing.Size(50, 50);
this.buttonOk.TabIndex = 11;
this.buttonOk.UseVisualStyleBackColor = false;
this.buttonOk.Click += new System.EventHandler(this.buttonOk_Click);
And it will work, done it many times before
I got this code from my own working Form1.Designer.cs but because of that: please use the Visual Studio designer and don't try to write all this code / logic in your constructor or something.
The problem is because you are showing an image with the same size as your button.
When you want an image fit in your button, the width and height of image should be at least 1 point less than your button size. (or in other word, you can set your button width and height 1 point more than the image size).
So you can change your code to this:
var l_oStopPic = new Bitmap(l_oStopImage ,
new Size(btnStopOperation.Width-1, btnStopOperation.Height-1));

WinForms Button: Autosize Maximumsize

I want to add Buttons to a FlowLayoutPanel. The Buttons might contain longer texts with spaces between the words. The Buttons are Autosize=true and AutoSizeMode = AutoSizeMode.GrowAndShrink. Further more I set the MaximumSize property to (maxwidth,0). maxwidth is the width of the panel. So the button does not grow too wide.
What I see is, that the widht of the Button is limited by the MaximumSize property, but when text wrapping occurs, the Button's height doesn't autosize to the height of the wrapped text. Is there a solution to that problem?
I also tried this manually sizing the button like this:
using (Graphics cg = this.CreateGraphics()) {
SizeF size = cg.MeasureString(button.Text, button.Font, 200);
button.Width = (int)size.Width+20;
button.Height = (int)size.Height+20;
button.Text = someLongTextWithSpaces;
}
But please note that I added 20 to the calculated size. It's working, but is there a proper way to determin this additional size? Maybe 2x Padding + ?????
A few hours later...
I came to this version which seems to work quite fine.
using (Graphics cg = this.CreateGraphics()) {
var fmt = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.WordBreak;
var prop = new Size(tableLayoutPanel1.Width - 20, 0);
var size = TextRenderer.MeasureText(button.Text, button.Font, prop, fmt);
int border = button.Height - button.Font.Height;
button.Width = (int)size.Width + border;
button.Height = (int)size.Height + border;
button.Text = someLongTextWithSpaces;
}
It seems that the initial button height is borders + the height the font. So I calculated the border subtracting button.Height-button.font.Height.
According to Hans, I now use the TextRenderer.MeasureText. I tested it without enabling VisualStyles and it worked fine. Any comments on that?
There is a proper way, but it isn't exactly very subtle. Reverse-engineering it from the ButtonRenderer class source code, the Winforms class that draws the button text, you must use the TextRenderer class to measure the text. And you must use the VisualStyleRenderer.GetBackgroundContentRectangle() method to obtain the effective drawing bounds. Note that it is smaller than the button's Size because of the border and a margin that depends on the selected visual style.
Non-trivial problems are mapping a calculated content rectangle back to the outer button size and dealing with old machines that don't have visual styles enabled. Sample code that appeared to arrive at the correct size:
private static void SetButtonSize(Graphics gr, Button button) {
VisualStyleElement ButtonElement = VisualStyleElement.Button.PushButton.Normal;
var visualStyleRenderer = new VisualStyleRenderer(ButtonElement.ClassName, ButtonElement.Part, 0);
var bounds = visualStyleRenderer.GetBackgroundContentRectangle(gr, button.Bounds);
var margin = button.Height - bounds.Height;
var fmt = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter | TextFormatFlags.WordBreak;
var prop = new Size(bounds.Width, 0);
var size = TextRenderer.MeasureText(button.Text, button.Font, prop, fmt);
button.ClientSize = new Size(button.ClientSize.Width, size.Height - margin);
}
protected override void OnLoad(EventArgs e) {
using (var gr = this.CreateGraphics()) {
SetButtonSize(gr, this.button1);
}
base.OnLoad(e);
}
Not extensively tested for corner cases, can't say I recommend this.
It seems that the initial button height is borders + the height the font. So I calculated the border subtracting button.Height-button.font.Height. (See the last block of my original post)
This also works with VisualStyles enabled/disabled.
You should control the line breaks by adding newline characters in the text. Automatic text wrapping won't work with spaces alone:
button1.Text = "123232131232\r\nfgfdgfdgdfgdfgdf\r\nASDSADSDASD";
Or :
button1.Text = "123232131232" + Environment.NewLine +
"fgfdgfdgdfgdfgdf" + Environment.NewLine + "ASDSADSDASD";
If you'd rather get the automatic wrapping you could try to use TextMeasure to determine the height needed for the text and then set the button's height accordingly but that may need some extra attention..
But I suggest to consider using Labels instead. For a Label the wrapping works out of the box.. Huge Buttons with varying sizes are non-standard UI elements.

WinForms Anchor Control changes Location Origin?

I have been porting my C# / .NET 2.0 project over to Mono for use on other platforms, but this seems to have brought up a problem in the NATIVE WinForms implementation.
I have isolated the problem to the relationship between a Control's (specifically, a Button) Anchor property, and it's Location property's Y-component. When the AnchorStyle property is set to Top, the origin of the Location property is the ClientArea of the form (excluding the Title Bar). Changing the Anchor to Bottom, however changes the origin to the Top-Left corner of the entire Window (including the Title Bar).
Here's a small Form class which illustrates the difference:
public class RawCodeForm : Form
{
public RawCodeForm()
{
Button b = new Button();
b.Text = "Test";
b.Location = new Point( 10, 10 );
b.Size = new Size( 75, 23 );
b.Anchor = AnchorStyles.Left | AnchorStyles.Top;
//b.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;
this.Controls.Add( b );
this.Size = new Size( 100, 200 );
this.Location = new Point( 100, 100 );
this.Show();
}
}
Try swapping the b.Anchor lines to see the change is position.
Is this a documented bug, or am I missing another property that needs to be set?
Edit: Thanks for pointing out that the Form starts as Size(300,300). I had assumed it was (0,0) until set.
Outside of the simple test form above, the problem now looks to be that the FormBorderStyle being changed later is causing the form to resize. My guess is that under Mono (or the host OS), the FormBorderStyle being changed resizes the ClientArea smaller, where-as the ClientSize area stays the same size in native WinForms.
This is because you change the size of the form after having added the button. Change it before
this.Size = new Size(100, 200);
this.Location = new Point(100, 100);
Button b = new Button();
b.Text = "Test";
b.Location = new Point(10, 10);
b.Size = new Size(75, 23);
//b.Anchor = AnchorStyles.Left | AnchorStyles.Top;
b.Anchor = AnchorStyles.Left | AnchorStyles.Bottom;
this.Controls.Add(b);
this.Show();
The button just follows the change of the lower border when anchored to the bottom.

Categories