Get height and width of UIElement? - c#

I currently create a class which inherits from a Popup.
Finally it will be used to show the user a popup with validation rules when editing e.g a TextBox.
Therefore I created ValidationPopup with this constructor so far:
public ValidationPopup(UIElement parent, double width) : base()
{
this.parent = parent;
this.width = width;
InitControl();
}
I would like to set the size of the popup equal to the size of the PlacementTarget parent.
In order to allow many different controls to be able to have one of these ValidationPopups I pass an UIElement as parent instead of TextBox etc.
My problem is that I have to pass the width and height values of this control when building an instance of ValidationPopup in order to size it properly.
Is it possible to get these values from UIElement?
I saw RenderSize on MSDN but it only gives me 0.0 values for width and height.
This is where I setup one instance of ValidationPopup:
txtOperator = new TextBox();
layoutGrid.Children.Add(txtOperator);
operatorVPU = new ValidationPopup(txtOperator, txtOperator.Width);
I tried to use RenderSize but this doesn't work until the whole UI is completly finished.
Is there a way to get Height and Width values of an UIElement at this point?
Are there maybe other even better ways to achieve the same result?

Sounds like you need to use the ActualWidth/Height properties. If they are still 0 or NaN, you can try executing this code on a later part of the view creation.

Related

Setting Width of Textbox according to maxlenth

I am using winforms application and i want to set that width of textbox which will show characters till max length,in short something like width = maxlength.Any predefined property is there? or we have to calculate it manually?
//I am looking for this type of logic
private void Form1_Load(object sender, EventArgs e)
{
//sample project
textBox2.Width = textBox2.MaxLength;
textBox3.Width = textBox3.MaxLength;
textBox4.Width = textBox4.MaxLength;
}
You have a Unit Mismatch: Width is in Pixels, MaxLength in characters.
So you need to measure the Font using e.g. Graphics.MeasureString.. Works best for fixed Fonts like Consolas.
You can measure the font e.g. like this, using 'x' as a medium width letter:
using (Graphics G = textBox2.CreateGraphics())
textBox2.Width = (int) (textBox2.MaxLength *
G.MeasureString("x", textBox2.Font).Width);
There are other Font measurement methods like TextRenderer.MeasureText you could use; also both methods have options to fine tune the measurement. The default above will include some leeway.
If you use a fixed Font the width will be OK, if you don't you'll need to decide whether you'd rather be on the safe side (use "W" or "M" to measure) or not. "X" is a likely compromise. Or you could adapt dynamically in the e.g. the TextChanged event..
Use the Anchor property to define how a control is automatically resized as its parent control is resized
Anchoring a control to its parent control ensures that the anchored edges remain in the same position relative to the edges of the parent control when the parent control is resized.
Try this:
textbox1.MaxLength = 0//The default value is 0, which indicates no limit;
Refer this msdn link for more info:
msdn link for textbox maxlength

Why doesn't -= operator resize the Form size like += do?

I can resize automatically my form size at run-time using:
Height += X
But
Height -= X
Has no side effect. It doesn't change anything in the form. Why that and how can I force the form to resize?
The solution:
AutoSizeMode = GrowAndShrink;
It's possible that you are running into a minimum possible size issue - many controls allow you to access their size properties but do not allow changes or certain changes to come into effect.
You can experiment with possible values in the designer and see which ones it rejects or alters.
It may be that a property of your form is affecting this - for example the MinimumSize property.

Why is the ListBox's DesiredSize.Width always zero?

I am creating a custom control in C#, and want to have a grid cell that contains a ListBox, which can be hidden or shown as desired. Hiding it is easy, I just set the Width to zero, however when I want to show it, I need to know the width that the ListBox would like to use.
I thought that DesiredSize.Width would give me this vale, but it's always zero, even after calling Measure(). Here is the code I'm using to show the ListBox...
_lb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
_lb.Width = _lb.DesiredSize.Width;
Any ideas how I find the desired width?
If your ListBox is in the cell of a grid, give that grid column a name. Then in code, you can access the .ActualWidth property of that grid column and use that value to set the width of your ListBox.
That assumes of course that the width of your grid column is not set to Auto, because that would still give you a 0 value.
_lb.Width = myGridColumn.ActualWidth
You might need to subtract a little bit from the column width to make your control fit nicely.
EDIT
One thing that I've found is that the ListBox must have items added to it before it will return anything other than 0 when it is measured.
string myItem = "Don't ask for a bath in Athabaska";
_lb.Items.Add(myItem);
_lb.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
double width = _lb.DesiredSize.Width;
As long as the ListBox has already been added to the window/usercontrol/grid, the above code returns a value of 227.53 for the width variable; using my defaults for font family and size.
If the ListBox has not been added to the window, or it doesn't have any items in it, it will return 0 for the .DesiredSize.Width property.
Also, if the .Visibility property is set to Collapsed instead of Hidden, the width will be 0.
Don't set the width to 0 when starting. Leave the width alone initially, set the .Visibility to Hidden. It will render to the needed width, but won't be shown. Then you can measure it and start playing around with the width.

Measure and Arrange a WPF control to use up available space

I have a window that has a menu, a toolbar at the top, and various other controls. I then have my own control that derives from ContentControl that I want to have use up all remaining space. I can't leave it to its own devices unfortunately, because the control is a Win32 control that's sort of... put inside this WPF control, and I need to use SetWindowPos.
At the moment what I am doing is using ArrangeOverride, getting the MainWindow.Content control and looking at the Height and Width. I then use Size I get in as a parameter and call the SetWindowPos function. It's written in C++/CLI, and here's the code:
Size WebView::ArrangeOverride(Size finalSize)
{
Application::Current->MainWindow->Measure(finalSize);
UIElement^ obj = dynamic_cast<UIElement^>(Application::Current->MainWindow->Content);
double objHei = obj->RenderSize.Height;
double objWid = obj->RenderSize.Width;
SetWindowPos(hWnd, NULL, objWid-finalSize.Width, objHei-finalSize.Height, finalSize.Width, finalSize.Height, NULL);
So in my head I thought this would then set the position of the control to within the remaining available space. And it does sort of work, but it seems as if the MainWindow.Content control is not being measured until afterwards? What am I doing wrong here?
edit: most of the problems seem to be when full-screening the window and then un-fullscreening it.
I have managed to fix this by using the answer to this question here
I simply put my control into a Frame, so it'd be the parent.
Then using the Point I set the window position to that, along with the size that is passed through as a parameter to the ArrangeOverride method.

FlowLayoutPanel autowrapping doesn't work with autosize

.NET Framework / C# / Windows Forms
I'd like the FlowLayoutPanel to automatically adjust its width or height depending on number of controls inside of it. It also should change the number of columns/rows if there is not enough space (wrap its content). The problem is that if I set autosize then the flowlayoutpanel doesn't wrap controls I insert. Which solution is the best?
Thanks!
Set the FlowLayoutPanel's MaximumSize to the width you want it to wrap at. Set WrapContents = true.
Have you tried using the TableLayoutPanel? It's very useful for placing controls within cells.
There is no such thing like impossible in software development. Impossible just takes longer.
I've investigated the problem. If there is really need for Flow Layout, it can be done with a bit of work. Since FlowLayoutPanel lays out the controls without particularly thinking about the number of rows/columns, but rather on cumulative width/height, you may need to keep track of how many controls you've already added. First of all, set the autosize to false, then hook your own size management logic to the ControlAdded/ControlRemoved events. The idea is to set the width and height of the panel in such a way, that you'll get your desired number of 'columns' there
Dirty proof of concept:
private void flowLayoutPanel1_ControlAdded(object sender, ControlEventArgs e)
{
int count = this.flowLayoutPanel1.Controls.Count;
if (count % 4 == 0)
{
this.flowLayoutPanel1.Height = this.flowLayoutPanel1.Height + 70;
}
}
if the panel has initially width for 4 controls, it will generate row for new ones. ControlRemoved handler should check the same and decrease the panel height, or get all contained controls and place them again. You should think about it, it may not be the kind of thing you want. It depends on the usage scenarios. Will all the controls be of the same size? If not, you'd need more complicated logic.
But really, think about table layout - you can wrap it in a helper class or derive new control from it, where you'd resolve all the control placing logic. FlowLayout makes it easy to add and remove controls, but then the size management code goes in. TableLayout gives you a good mechanism for rows and columns, managing width and height is easier, but you'd need more code to change the placement of all controls if you want to remove one from the form dynamically.
If possible, I suggest you re-size the FlowLayoutPanel so that it makes use of all the width that is available and then anchor it at Top, Left and Right. This should make it grow in height as needed while still wrapping the controls.
I know this is an old thread but if anyone else wonders on here then here's the solution I produced - set autosize to true on the panel and call this extension method from the flow panel's Resize event:
public static void ReOrganise(this FlowLayoutPanel panel)
{
var width = 0;
Control prevChildCtrl = null;
panel.SuspendLayout();
//Clear flow breaks
foreach (Control childCtrl in panel.Controls)
{
panel.SetFlowBreak(childCtrl, false);
}
foreach (Control childCtrl in panel.Controls)
{
width = width + childCtrl.Width;
if(width > panel.Width && prevChildCtrl != null)
{
panel.SetFlowBreak(prevChildCtrl, true);
width = childCtrl.Width;
}
prevChildCtrl = childCtrl;
}
panel.ResumeLayout();
}
Are you adding the controls dynamically basing on the user's actions? I'm afraid you'd need to change the FlowLayout properties on the fly in code, when adding new controls to it, then refreshing the form would do the trick.

Categories