I'm trying to get an UserControl (which has a grid on it) on a Windows Form to resize.
The below code is what I have in the Form. The behavior I'm getting is that the control is resized when I make it big. But it does not shrink. What am I doing wrong (or) What am I missing?
private void AdjustGrid()
{
ZoomControl.Location = new Point(5, 5);
ZoomControl.Size = new Size(this.Width - 15, this.Height - 75);
}
void zoomform_Resize(object sender, EventArgs e)
{
AdjustGrid();
}
Now the user control has the following code:
//Resize the grid that the UserControl has on it
private void NameValuePropertyBag_Resize(object sender, EventArgs e)
{
grdNameValueProperties.Location = new Point(4,4);
grdNameValueProperties.Size = new Size(this.Width - 8, this.Height - 8);
}
I tried
grdNameValueProperties.Size.Width = this.Width - 8;
grdNameValueProperties.Size.Height = this.Height -8;
It gives me "Cannot modify expression because it is not a variable" error... What am I missing?
Additional Info:
I'm using SetParent() Windows call to move/zoom an UserControl to another Form (ZoomForm).
Anchor doesn't seem to work for controls moved with SetParent()... More precisely, it may be working but I have repainting problems.
I got Anchor/Dock pair to working without repaint issues [I removed the resize event wireup and adjusted Dock to Fill]
The ZoomForm initally has no controls. The Usercontrol is added to the ParentForm dynamically.
Currently, I'm able to make the zoom form bigger with the above code but not smaller.
grdNameValueProperties.Size.Width = this.Width - 8;
grdNameValueProperties.Size.Height = this.Height - 8;
That code gives the error because Size is a value type, not a reference type. Reading this may help explain the difference between value types and reference types.
As recursive commented, you should just use the Anchor property.
The error occurse because the Size property exposes a struct and not a reference type. The Size property returns a copy of the size object of the control. Writing to the properties Width and Hight of this copy makes no sense because it is just a temporary copy and not backed by memory anywhere.
You can't directly change the Size.Width property on a UserControl, because the Size property is a value type, so changing its width would essentially be overwriting the entire Size property. Instead, controls in WinForms provide their own Width and Height properties, so this code should work:
grdNameValueProperties.Width = this.Width - 8;
grdNameValueProperties.Height = this.Height - 8;
Having said that, I agree with #recursive's comment that you should probably just use the UserControl's Anchor property to make it "automatically" resize.
Currently, I'm able to make the zoom form bigger with the above code but not smaller.
Some controls have a MinSize (or similar) property on them. Do you have any of those set such that you can't resize smaller?
For the first portion -
First off, I'd recommend using the Anchor property on UserControl instead of trying to size this yourself. It works very simply, and very reliably, for handling window resizing.
If you want to do this, you should probably look at chaining off this.ClientSize instead of this.Height and this.Width. You're probably setting your control too large, and that's unachoring the panel or other thing you're sitting on, which causes all sorts of issues.
The second part is due to the fact that gridNameValue Properties.Size.Width is a member of a struct.
When you call gridNameValueProperties.Size, you're returning a Size struct, then trying to set the Width on the returned struct (not the original). This is why you need to set the entire Size valuetype in one shot. Creating a new Size() and setting it to gridNameValueProperties.Size is the only way to make that work.
Related
I have a large WPF application. I'm looking to make the UI multithreaded. As part of that process, I'm moving some application-level control and style resources to the Window level. (I know from experience that I can't use DynamicResource and resolve at the application level unless I'm on the application thread.) Anyway, I moved a bunch of control resources. The application works find with one nasty problem: all of my animations on FrameworkElement Height and Width broke. They all fail because the control is of width or height NaN. These animations all work when the control templates are registered at the application level. All of my controls where I animate height or width have appropriate default height or width values that are not NaN. Why would the resource location affect this?
Use ActualWidth and ActualHeight. Nan means that these propeties are not set yet.
https://stackoverflow.com/a/607849/3955716
This is late, but in case someone else get into this.
the DoubleAnimation is a helper class that allows for a double to go from a value to another in a smooth fashion.
As you don't specify a Width explicitly for your grid, the default value is NaN.
Thus the DoubleAnimation is trying to go from NaN to Whatever the target value. This cannot be done for obvious reasons.
If you set the Width of the grid, it should work properly.
A workaround would be to set the grid width after it loads in the constructor :
public Grid()
{
InitializeComponent();
this.Loaded += (s, _) => this.Width = this.ActualWidth;
}
Hope this help.
I'm trying to optimize populating and scrolling a FlowLayoutPanel, but I've had issues with similar controls before, where if they have too many controls inside, it takes a really long while for the container to populate and get ready for use (and the scroller gets shorter and shorter, you might be familiar with that).
I've read that you can use a pool of just the controls within visible boundaries of the container rectangle and simulate scrolling by repopulating them with corresponding contents, as if they would be without this optimization. So you scroll as usual but the population doesn't take nearly as long. But how do I implement that for a general case?
I'm using custom controls to populate the FlowLayoutPanel container, so I'm looking for a generic enough solution that can be applied to both my control and the standard .Net controls.
Display and scrolling performance are good reasons to try a virtual paging, although they can be overcome by replacing the Controls.Add with a Controls.AddRange call and a double-buffered container..
..but there is another: Any Winforms control is limited to 32k pixels in its display dimensions. Even if you make it larger nothing will be displayed beyond this limit.
Here is a quick list of things to do, when implementing virtual paging:
Use a double-buffered FlowLayoutPanel subclass to simplify the layout and make it flicker-free.
Turn off AutoSize and AutoScroll
Add a VScrollBar to the right of the FLP and keep its Height the same as the FLP's
Calculate the Height (plus Margins) of your UserControl. I assume you add your control wrapped up in a UC, to make things easier.
Calculate the paging numbers
Create a List<yourUserControlClass> theUCs
Now create your UCs but add them only to the list theUCs
Code a scrollTo(int ucIndex) function, which clears the FLP's controls and adds the right range from the list.
code KeyPreview for the FLP to allow scrolling with the keyboard.
Setting the right values for the VScrollBar's properties, i.e. Minimum, Maximum, Value, SmallChange, LargeChange is a little tricky and setting the page size must be done whenever the FLP is resized or elements are added to or removed from the list.
In my test the setting up and the scrolling results were instantaneous. Only complete UCs are visible from the top, which is fine with me. I have added 1000 UCs with a bitmap in a Panel, a Label and a CheckedListBox.
Here is how I calculate the setting for Maximum:
float pageSize = flowLayoutPanel2.ClientSize.Height /
(uc1.Height + uc1.Margin.Top + uc1.Margin.Bottom);
vScrollBar1.Maximum = (int)( 1f * theUCs.Count / (pageSize)) + 9;
The extra 9 is a workaround for the quirky offset of a ScrollBar's theoretical and actual Maximum values.
In the ValueChanged event I wrote:
private void vScrollBar1_ValueChanged(object sender, EventArgs e)
{
int pageSize = flowLayoutPanel1.ClientSize.Height / theUCs.First().Height;
int v = Math.Min(theUCs.Count, vScrollBar1.Value);
flowLayoutPanel1.SuspendLayout();
flowLayoutPanel1.Controls.Clear();
flowLayoutPanel1.Controls.AddRange(theUCs.Skip( (v- 1) * pageSize)
.Take(pageSize + 1).ToArray());
flowLayoutPanel1.ResumeLayout();
}
This scrolls to a certain item:
void scrollTo(int item)
{
int pageSize = flowLayoutPanel1.ClientSize.Height / theUCs.First().Height;
int p = item / pageSize + 1;
vScrollBar1.Value = p;
}
For even smoother scrolling use a DoubleBuffered subclass:
class DrawFLP : FlowLayoutPanel
{
public DrawFLP() { DoubleBuffered = true; }
}
This is probably a bit rough at the edges, but I hope it'll get you on the right track.
I want to layout controls during runtime (dynamically created). For the purpose of this question, let's restrict to a Button control. I want to set the control's properties (such as Text) and then determine the minimum size for the control for it to display properly; the size that setting AutoSize = true would give. In C# example code, with GetAutoSizeSize being this minimum size:
Button button = new Button();
this.Controls.Add(button);
button.Text = "Example";
button.Size = GetAutoSizeSize(button);
button.Location = /* Some calculation based on button.Size */
Possible solution: AutoSize
One can set button.AutoSize = true and button.AutoSizeMode = AutoSizeNode.GrowAndShrink. After that, the button.Size can be fetched, after which AutoSize can be turned off and the control size can be changed.
Potential issues:
It looks odd and I can't help but feel that this could easily break, but maybe I am wrong?
Possible solution: GetPreferredSize
button.GetPreferredSize can be used to get a size that the control wants to be.
Problems with this:
Its usage is internal and/or meant for flow layout.
GetPreferredSize takes a suggested size as a parameter, so one needs to guess at what would be appropriate.
The size returned is wrong, in that it returns the 'comfy' size of a control, which can be much larger than the minimum size that AutoSize gives.
EDIT: From the comments and some trial-and-error, I was able to conclude that the problems I originally listed with the AutoSize-method were due to needing both the control to be added to the control collection first and AutoSizeMode set to GrowAndShrink.
I would like to know if there is a function (and/or more 'robust' way) of determining the AutoSize-size: a function like GetPreferredSize that returns the size without actually having to toggle AutoSize.
This works when you are drawing on a control.
String sMyString = "this is my string";
Font fntFont = new Font("Arial", 8);
SizeF sfMySize = new SizeF(5,5);
sfMySize = System.Graphics.MeasureString(sMyString, fntFont, sfMySize);
This will give you the dimensions of the bounding box around the control text. You would have to work out the appropriate buffer around the text to set the button size.
I've got an app which will run on two different devices - one with a screen size of 240x320, the other 480x640.
For all forms bar one the VS generated code is fine:
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.AutoScroll = true;
For one form i'm capturing a signature. I'm doing this by a panel with a graphics handler; capturing mouse down and move events; this generates a list of vector points which I can draw lines with.
On the smaller res screen this is fine. On the higher res, I can't display my lines.. and I think this is because the panel is beyond the windows form size.
The form is created with a size of 240 x 268; a standard size I think - i've not manually set it, VS does this for me.
In order to get the panel in the right spot on the high res device, the co-ordinates are 3, 290; ie, 290 is past 268. Also the width of the panel is 448 which is somewhat larger than 240.
I'm using .net 2.0 (can't use later). I think I need to resize the form to make it larger but I do want to keep the existing re-sizing for the other controls on the form.
I'm not sure how to do this.
Make the form dock to fill, then use the Anchor properties to ensure controls inside the form resize as expected.
If you want the option of customizing how an individual control resizes, then DONT set the anchor properties on it, and instead handle the Resize event and perform custom resizing/repositioning within code there.
eg
private void form_Resize(object sender, EventArgs e)
{
// Center the control without changing width. Other controls are anchored.
this.control.Left = (this.Width - this.control.Width) / 2;
}
I'm writing this answer for the benefit of those who may have a similar problem in the future. PaulG pointed me in the right direction but I found the root cause to be something else.
The PDA project i've got uses "FormFactor WindowsMobile 6 Classic" which has a default size of 240 x 268.
Changing this to "Windows Mobile 6 Professional VGA" created a much larger form size.
This allowed me to get things positioned correctly for the larger size; then AutoScaleMode to DPI; and manually resizing the panel smaller made it all work.
IE, going from larger to smaller was easy; I didn't get smaller to larger working.
I have ListView and I need to determine item height.
You can use the GetItemRect() method:
int itemHeight = yourListView.GetItemRect(itemIndex).Height;
I am not 100% sure but this might help:
int itemHeight = listView.Items[itemIndex].GetBounds(ItemBoundsPortion.Entire).Height;
I had the same question however there is one issue - until the listview is drawn the values aren't set. And you may want to be able to set the sizes before you add any items (if for example I want to dry a listview that can display 5 entries but will start off empty).
Therefore my workaround was to run the following code, which forces the control to be rendered but without displaying it, in the application's initialisation section and save the values as global variables for later use. And, to get around listviews with different font sizes, to only store the difference between the height and the font height:
Dim lvwTemp As New ListView
lvwTemp.View = View.Details
lvwTemp.Columns.Add("test")
lvwTemp.Items.Add("test")
Dim zTempBitmap As New Bitmap(100, 100)
lvwTemp.DrawToBitmap(zTempBitmap, New Rectangle(0, 0, 100, 100))
zTempBitmap.Dispose()
gintListviewHeaderHeightMinusFontSize = lvwTemp.Items(0).GetBounds(ItemBoundsPortion.Entire).Top - lvwTemp.Font.Height
gintListviewItemHeightMinusFontSize = lvwTemp.Items(0).GetBounds(ItemBoundsPortion.Entire).Height - lvwTemp.Font.Height