Why is my Grid's width NaN? - wpf

I have the following mark-up in a view. When I get WindowContainer.Width during start-up code for the view, it returns NaN.
<Border BorderThickness="10">
<StackPanel x:Name="Panel" Orientation="Vertical" >
<Grid x:Name="WindowContainer" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Loaded="WindowContainer_OnLoaded">
<delphi:Win32WindowHost x:Name="Host" />
</Grid>
<TextBlock x:Name="InfoTextBlock" HorizontalAlignment="Right" />
</StackPanel>
</Border>
Does Stretch make the grid stretch to accomodate all its content, or to fill its container? I want it to stretch to fill the container, the Border, and have a proper double width I can use to size a floating window to be the same size.

Does Stretch make the grid stretch to accomodate all its content, or to fill its container?
From MSDN about HorizontalAlignment="Stretch":
Child elements are stretched to fill the parent element's allocated layout space. Explicit Width and Height values take precedence.
Why is my Grid's width NaN?
NaN is to mean "not set". FrameworkElement is the base class for many Controls in WPF and if you do not explicitly set the Height and Width properties then in the class constructor will be a default value of NaN.
When I get WindowContainer.Width during start-up code for the view, it returns NaN
In this case try get the ActualWidth, instead of Width, because:
ActualWidth property is a calculated value based on other width inputs, and the layout system. The value is set by the layout system itself, based on an actual rendering pass, and may therefore lag slightly behind the set value of properties such as Width that are the basis of the input change.

Related

How to use ScrollViewer.ScrollToVerticalOffset?

I hope this isn't a duplicate but I can't find any documentation or examples on how to actually use ScrollToVerticalOffset(). I'm using it in a Windows Phone 8 app, but I think it will still apply to WP7 and Silverlight (although, feel free to correct me if I'm wrong).
So here is my basic set up (pseudo-code from memory):
<phone.PivotItem>
<ScrollViewer>
<Grid Height="1500">
<Grid.RowDefinitions>
<!-- about 20 rows, all auto-height -->
</Grid.RowDefinitions>
<Border Grid.Row="0">
<TextBox x:Name="txt1" />
</Border>
<Border Grid.Row="1">
<TextBox x:Name="txt2" />
</Border>
<!-- ...... -->
<Border Grid.Row="19">
<TextBox x:Name="txt20" />
</Border>
</Grid>
</ScrollViewer>
</phone.PivotItem>
So as you can see, I've got a ScrollViewer within a PivotItem, and inside is a Grid. In the Grid there are about 20 TextBoxs, each within a Border. I am dynamically setting focus to one of these TextBoxs when this page loads, so anytime I set focus to TextBox #6-20 (roughly) - I have to manually scroll down to see it. I want to auto-scroll my ScrollViewer so that whichever TextBox has focus, it will be centered for the user to see.
The documentation for ScrollToVerticalOffset() says:
Scrolls the content that is within the ScrollViewer to the specified
vertical offset position.
And that it accepts a type of System.Double.
What I don't understand is A) the value I'm supposed to pass, and B) how I could even get that value? Is it supposed to be a number between 0 and the height of my Grid (1500)? If so, how could I determine the position of any given TextBox so I can scroll to it?
If there are any straightforward examples, please feel free to link out to them. I'm not sure if the content within the ScrollViewer matters when calling this method, but in case it does I wanted to show exactly how I'm using it.
Many thanks in advance!
You can see any UIElement's position relative to another UIElement using the UIElement.TransformToVisual call.
First, get the transform between the TextBox and ScrollViewer.
GeneralTransform transform = textBox.TransformToVisual(scrollViewer);
Then, figure out what point (0,0) of the TextBox is relative to the ScrollViewer. Meaning, the TextBox origin (0,0) is located at what ScrollViewer position.
Point textBoxPosition = transform.Transform(new Point(0, 0));
Now that you know the Y position of the TextBox relative to the ScrollViewer, scroll to that Y offset.
scrollViewer.ScrollToVerticalOffset(textBoxPosition.Y);
Good luck!
This is a very old post, but the meaning of VerticalOffset varies.
Most of the solutions I have seen assume VeritcalOffset is in pixels. This is not always the case.
From: https://learn.microsoft.com/en-us/dotnet/api/system.windows.controls.scrollviewer.extentheight
If CanContentScroll is true, the values of the ExtentHeight, ScrollableHeight,
ViewportHeight, and VerticalOffset properties are number of items. If
CanContentScroll is false, the values of these properties are Device Independent Pixels.

Have WPF Window resize automatically based on the remaining area in DockPanel

I have a scenerio where I want to dynamically render a custom form object. This form is similar to a WinForms form. It will display one of more button bars, buttons can only live within a button bar in our implementation, and numerous edit control that the user will use for data entry.
In rendering the form we have in a configuration file a row height and column width, both as a pertcentage of the application area. All forms share a common row height and column width. The actual device unit value of these are calculated on applicaiton initialization.
My issue is with sizing the form. For example, I have a form that is supposed to be 15 rows by 80 columns, let's say this translates to 500 units high and 800 units wide. This would be the form area, not including button bars. Right now I am calculating the window height and width in a FormPreviewManager. However, it is cumbersome as I have to give the total units, including the size of the button bar(s) and the windows borders, which may change as they have the option of including a title bar or not in the floating form window.
Within the rendering of the form I use a DockPanel to render the button bar(s) and claim the remaining space as my grid of rows and columns. I tried creatign the button bar(s), setting the remaining space to my FormGrid object, then specifically setting the Width and Height for the FormGrid but it doesn't change the size of the containing window.
How can I make it so that the floating window sizes automatically? I want to be able to draw the button bar(s) then say remaining space is 500 units high and 800 units wide, the window then adjust to whatever size it needs to hold this data, no more and no less.
Any help on this, or a possible other approach, would be great.
Update
Basically if this was my entire XAML definition for my window I would like to be able to set the width and height of UnusedContent and have the window resize accordingly.
<Grid>
<DockPanel>
<StackPanel DockPanel.Dock="Top" Height="50" />
<StackPanel DockPanel.Dock="Left" Width="50" />
<ContentControl x:Name="UnusedContent" />
</DockPanel>
</Grid>
If I set UnusedContent to 200 by 200 it would create a window with interior dimensions of 250 by 250, the content area plus the two stack panels.
You can try placing a control to fill up the remaining DockPanel space, and then binding to that control's ActualHeight and ActualWidth properties
<DockPanel>
<StackPanel DockPanel.Dock="Top" Height="50" />
<StackPanel DockPanel.Dock="Left" Width="100" />
<ContentControl x:Name="Spacer" />
</DockPanel>
<Grid Height="{Binding ElementName=Spacer, Path=ActualHeight}"
Width="{Binding ElementName=Spacer, Path=ActualWidth}" />

Restrict the Width of a ContentPresenter's to its Dynamic Content

I would like to be able to dynamically adjust the size of a content control.
Here's a simple example:
...
<Slider x:Name="width" Minimum="40" Value="100" Maximum="300"/>
...
<ContentPresenter Width="{Binding Value, ElementName=width}" Content="Some value">
<ContentPresenter.ContentTemplate>
<DataTemplate>
<Grid MaxWidth="200" MinWidth="80">
<Rectangle Fill="Wheat" />
<TextBlock Text="{Binding}"/>
</Grid>
</DataTemplate>
</ContentPresenter.ContentTemplate>
</ContentPresenter>
Given this example, I would like to force the width of the content ContentPresenter to stay within the min and max width of its generated child (80 - 200 in this case).
Obviously with a simple example like this I could just change the range of the slider, but my real scenario is more complicated. I'm trying to restrict the size of a popup screen to its generated content. I can't set an explicit range on the popup because I have no idea what the content is going to be like before hand. The content has to be able to restrict itself.
Unfortunately MaxWidth and MinWidth on children are pretty much ignored. MinWidth results in cropping when the parent is set smaller. MaxWidth results in lots of empty space. It looks like I will have to set MaxWidth and MinWidth in the same place that I am dynamically updating the Width value.
My suggestion would be to replace the ContentPresenter with a custom panel (alternately, enclose a custom panel in the ContentPresenter if you cannot replace it) that doles out the exact size you want in the way that you want. It's fairly easy and simple to declare a class that derives from Panel and then override MeasureOverride and ArrangeOverride. In this case you could measure everything with the base MeasureOverride and then just assign the size you want as your available space. In ArrangeOverride just assign the direct child of your panel, to be the final size of the panel (rect = 0,0,width,height).

Silverlight resize textbox according to text size

I'm using Silverlight 4 and I want to create a childwindow with a TextBox, that the TextBox's width will be constant and it's height will be resized according to the size of the assigned text.
Any ideas how can I do it?
Set the Width property on your textbox to whatever you want it to stay at -- then set the TextWrapping property to "Wrap", and then make sure the content control holding is not set up to stretch it vertically and it will do what you want (text wraps and the box grows vertically to contain it as you enter stuff).
Ultra simple example:
<Grid x:Name="LayoutRoot" VerticalAlignment="Top">
<TextBox Name="tbTest" TextWrapping="Wrap" Width="300" />
</Grid>

What is the difference between Width and ActualWidth in WPF?

I am currently working with Panels in WPF, and I noticed that as regards the Width and Height properties, there are also two other properties called ActualWidth and ActualHeight.
ActualWidth
Gets the rendered width of this
element. This is a dependency
property. (Inherited from
FrameworkElement.)
Width
Gets or sets the width of the element.
This is a dependency property.
(Inherited from FrameworkElement.)
Reference: MSDN
Can anyone point out the differences between the two and when to use either one ?
Width/Height is the requested or layout size. If you set to Auto, then the value is double.NaN when you access the property in code behind.
ActualWidth/ActualHeight and RenderSize.Width/RenderSize.Height both return the element's rendered size, as RenderSize is of type Size. If you want/need the actual size of the item, then use any of these attributes.
I find ActualWidth most useful when I want to bind the width or height of one element to another.
In this simple example I have two buttons arranged side by side and a comment underneath that is constrained to the width of the StackPanel containing the two buttons.
<StackPanel>
<StackPanel Margin="0,12,0,0" Orientation="Horizontal" Name="buttonPanel" HorizontalAlignment="Left" >
<Button Content="Yes - Arm the missile" FontWeight="Bold" HorizontalAlignment="Left"/>
<Button Content="No - Save the world" HorizontalAlignment="Left" Margin="7,0,0,0"/>
</StackPanel>
<TextBlock Text="Please choose whether you want to arm the missile and kill everybody, or save the world by deactivating the missile."
Width="{Binding Path=ActualWidth,ElementName=buttonPanel}" Margin="0,5,0,0" HorizontalAlignment="Left" TextWrapping="Wrap"/>
</StackPanel>
ActualWidth accounts for padding in the value so anytime you need to know that number you can call Actualwidth instead of width and avoid the calculation.
edit: removed Margin b/c it isn't part of ActualWidth.
ActualWidth is set by the rendering system, and may be different depending on the widths of other elements and overall size constraints. As a result, it can not be changed. Width is a property that can be changed, and should be used to increase or decrease the width of the element.
From MSDN:
This property is a calculated value based on other width inputs, and the layout system. The value is set by the layout system itself, based on an actual rendering pass, and may therefore lag slightly behind the set value of properties such as Width that are the basis of the input change.
There is a very good reason not to use the ActualWidth to bind to (obviously ActualHeight accordingly).
When you set the Width of an element, to the ActualWidth of another one you may break the layout chain.
In the best case your element/control needs to be parsed after the layout process of the parent (the binding source) finished. That means additional time.
If it is at the same hierarchy level as the parent the layout process needs two runs (at least) to calculate a definitive size.
For example I had a control which had it's size property overridden in a style that would set it to the TemplatedParent (don't do):
<Rectangle DockPanel.Dock="Top" Width="{TemplateBinding ActualWidth}"
Height="1" Fill="#000000"/>
When resizing the containing window, the control would prevent the container from becoming smaller and brake the layout. Setting it to the Width will resolve the problem (do):
<Rectangle DockPanel.Dock="Top" Width="{TemplateBinding Width}"
Height="1" Fill="#000000"/>
If you have to use the ActualWidth in general something is wrong with your xaml. Better fix that instead of messing up with the final sizes of the layout run.
It's exactly that, the render width != layout width. One is intended to be used for layout the other one is intended for render. It like with WinForms, there was a Size and a ClientSize property, the differ slightly and you should use the Atual/Client size of rendering and the Width/Height for layout.
You can set the Width property, but not the ActualWidth property.
The Width property is used to determine how the panel is rendered, then the ActualWidth is set to the actual width that was used. This may not be the same value as Width, depending on the size of it's child elements and constrictions from it's parent element.
The ActualWidth is not set immediately when setting the Width property, but will be updated (one or more times) during rendering.

Resources