When is horizontalalignment =stretch applied? - wpf

Does the horizontal alignment = stretch get applied in measure or arrange? does it affect the actualwidth and actualheight of the object? or does it work in some other way? I can't seem to find any documentation on how it is actually being applied during layout
Edit: after some testing it appears that Width and actual width are not changed by horizontal alignment = "stretch"
I'm wondering if maybe it is applied on render.

"When the Height and Width properties are explicitly set on an object, these measurements take precedent during layout and can cancel the typical effects of setting HorizontalAlignment to Stretch.
Canvas does not use HorizontalAlignment when composing layout, because Canvas is based on absolute positioning. In general, the value of HorizontalAlignment is potentially treated differently by any given object that is capable of having one or more FrameworkElement objects as child content."
but it doesn't specify whether the stretch actually changes the width or actualwidth or whether it just overrides it. Anyway it overrides it as far as I can tell, leaving width and actualwidth alone.

Related

ScrollViewer's Viewport Height VS Actual Height [duplicate]

This question already has answers here:
WPF DataGrid : CanContentScroll property causing odd behavior
(2 answers)
Closed 9 years ago.
Both are quite general terms but I'm curious to know when these height will be different apart from the case we're using Virtualization?
One more question:
I read on MSDN:
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.
However I'm facing an issue with ViewPort Height: I've 2 listbox in application:
1. Which have Virtualization Enabled and CanContentScroll = True.
2. Which have no virtualization and CanContentScroll = True.
In ListBox 1 while drag-drop Viewport Height comes to 4/5 (Number of elements currently visible). However in ListBox 2 i get Viewport Height equal to Actual Height of Listbox.
Why this difference?
Few more findings:
1. Scrollable Height is number of items not visible in scrollviewer
2. Viewport Height is number of items visible in scrollviewer.
Thus Viewport Height + ScrollableHeight = Extent Height
Can someone please explain what's the difference between two listboxes? I need ViewPort hieght in case of Listbox 1
the ActualHeight is the actual height of the ScrollViewer. The Viewport is what is visible from the ScrollViewers Content. So to answer your question: ViewportHeight differs from ActualHeight if the horizontal Scrollbar is visible by the Height of the Scrollbar.
so, to sum this up:
ActualHeight = ViewportHeight + HorizontalScrollbarHeight
Finally This was the root cause:
Quoting from https://stackoverflow.com/a/3062692/3195477:
You are encountering the differences between physical scrolling and
logical scrolling.
As you have discovered, each has its tradeoffs.
Physical scrolling
Physical scrolling (CanContentScroll=false) just goes by pixels, so:
The viewport always represents exactly the same portion of your scroll
extent, giving you a smooth scrolling experience, and but
The entire contents of the DataGrid must have all templates fully
applied and be measured and arranged to determine the size of the
scrollbar, leading to long delays during loading and high RAM usage,
and It doesn't really scroll items so it doesn't understand
ScrollIntoView very well Logical scrolling
Logical scrolling (CanContentScroll=true) calculates its scroll viewport and extent by items instead of pixels, so:
The viewport may show a different number of items at different times,
meaning the number of items in the viewport as compared to the number
of items in the extent varies, causing the scrollbar length to change,
and
Scrolling moves from one item to the next and never in between,
leading to "jerky" scrolling
but
As long as you're using VirtualizingStackPanel under the hood, it only
needs to apply templates and measure and arrange the items that are
actually visible at the moment, and
ScrollIntoView is much simpler since it just needs to get the right
item index into view
Choosing between them
These are the only two kinds of scrolling provided by WPF. You must
choose between them based on the above tradeoffs. Generally logical
scrolling is best for medium to large datasets, and physical scrolling
is best for small ones.
A trick to speed loading during physical scrolling is to make the
physical scrolling better is to wrap your items in a custom Decorator
that has a fixed size and sets its child's Visibility to Hidden when
it is not visible. This prevents the ApplyTemplate, Measure and
Arrange from occuring on the descendant controls of that item until
you're ready for it to happen.
A trick to make physical scrolling's ScrollIntoView more reliable is
to call it twice: Once immediately and once in a dispatcher callback
of DispatcherPriority.ApplicationIdle.
Making logical scroll scrollbar more stable
If all your items are the same height, the number of items visible in
the viewport at any time will stay the same, causing the scroll thumb
size to stay the same (because the ratio with total number if items
doesn't change).
It is also possible to modify the behavior of the ScrollBar itself so
the thumb is always calculated to be a fixed size. To do this without
any hacky code-behind:
Subclass Track to replace the calculation of Thumb position and size in MeasureOverride with your own
Change the ScrollBar template used for the logical-scrolling ScrollBar to use your subclassed Track instead of the regular
one
Change the ScrollViewer template to explicitly set your custom ScrollBar template on the logical-scrolling ScrollBar
(instead of using the default template)
Change the ListBox template to use explicitly set your custom ScrollViewer template on the ScrollViewer it creates
This means copying a lot of template code fom the built-in WPF
templates, so it is not a very elegant solution. But the alternative
to this is to use hacky code-behind to wait until all the templates
are expanded, then find the ScrollBar and just replace the ScrollBar
template with the one that uses your custom Track. This code saves two
large templates (ListBox, ScrollViewer) at the cost of some very
tricky code.
Using a different Panel would be a much larger amount of work:
VirtualizingStackPanel is the only Panel that virtualizes, and only it
and StackPanel to logical scrolling. Since you are taking advantage of
VirtualizingStackPanel's virtualization abilities you would have to
re-implement all of these plus all IScrollInfo info function plus your
regular Panel functions. I could do something like that but I would
allocate several, perhaps many, days to get it right. I recommend you
not try it.
They can differ from the point of (specified) Height being evaluated to any given time during the (ongoing) rendering process.
From MSDN:
There is a difference between the
properties of Height and Width and
ActualHeight and ActualWidth. For
example, the ActualHeight property is
a calculated value based on other
height 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 Height, that are the basis of
the input change.
Because ActualHeight
is a calculated value, you should be
aware that there could be multiple or
incremental reported changes to it as
a result of various operations by the
layout system. The layout system may
be calculating required measure space
for child elements, constraints by the
parent element, and so on.

Determine the width of the widest element in a ListBox

When you do not explicitly set the width of a ListBox, it will automatically set its width to the width of its widest visible item. This means that as you scroll up and down and the widest visible data item changes, the width of the listbox itself increases and decreases.
So what is the best way to find the width of the widest item in the list, and then set the ListBox width to that? My data template for the ListBox is a StackPanel containing a CheckBox and a TextBlock, so obviously the Textblock width will vary according to the length of the string in it.
There are a couple of suggestions already here on SO, with answers ranging from "take a guess" to "measure the text size" (how do you measure text size in SL without placing it in a UI element?).
Can anyone throw me a few ideas? I am open to iterating all the data template instances if i have to, although a better option may be to find the longest string and calculate from there?
Have not tried this but it can work:
http://thibautvs.com/blog/?p=1775
"key point is to put the ListBox in a grid column having an “auto” size mode"
ListBox uses VirtualizingStackPanel as default ItemsPanel. Because VirtualizingStackPanel creates UI elements only for visible items you might see the behavoir you describe. To mitigate this behaviour you could try to set VirtualizingStackPanel.IsVirtualizing="False" or change ItemsPanel to StackPanel.
Messing around with this has shown me that it isn't possible without going to the extent of extending/overrideing the ListBox - setting the IsVirtualizing property of the VirtualizingStackPanel had no effect.
The solution that i had to go with was to programmatically choose a good width.

Setting control height explicitly

I have a XamDataGrid in one of my user controls, inside of a stackpanel. I want the grid to maintain the same height regardless of how many rows are present in the grid. To do that, I set the grid's Height property to an explicit value.
Is that how things are done in WPF? Every time I do explicit sizing I feel like I am doing WinForms and not using WPF properly. Is setting the Height directly the only/correct solution?
There's nothing wrong with setting an explicit Height in situations where you want an element to always stay the same height. Where it's less appropriate is in situations where sizing is better handled by the parent layout Panel or the element's child content which can use the available space dynamically.
WPF uses a relative measurement system which at first glance is not intuitive. I have never found an example when I was forced to use explicit sizes ( once when I paint something on Canvas). I use styles in 90% cases where I define Padding, Margin, Aligment etc. Sometimes I use MinHeight and MinWidth for simple things.
About that Grid you can put it in the ScrollViewer or ViewBox to have dynamic sizing, yet If it won't be trouble set the explicit Height.

How to calculate a bounding box for an Expander?

I have an Expander control and i need to calculate its bounds without invisible elements and margins. It commonly can be done by VisualTreeHelper.GetDescendantsBounds. But it seems that the rect is calculated by VisualTreeHelper doesn't depend on the expander state. For example:
http://i.piccy.info/i5/58/39/273958/collapsed.jpg
(i can't post images. sorry)
The same result as for expanded state (light green rectangle on the image). Does anybody know how to solve this problem?
The Expander control will set its content's visibility to Collapsed, which means it won't be considered during layout and won't be included in GetDescendantBounds. However, the Expander can be forced to have a larger size by the layout engine, and the Expander's own size is included in GetDescendantBounds.
Try setting VerticalAlignment="Top" on the Expander. The default is Stretch, which will allow it to increase in size if the parent has more space available. Also make sure you aren't explicitly setting the Height property.
This is the sample application. The style is applied here to the TreeView control and its items. But the problem doesn't depend on the style.

WPF - setting HorizontalAlignment= Stretch to Textbox in StackPanel

Why doesn't a textbox stretch to fill space in a stackpanel? Is this by design? In a grid, the textbox stretches as expected.
Yes, it's by design. The StackPanel will allocate the space the TextBox asks for. If you haven't set a width on the TextBox, it will require only enough width to fit its text.
Kent's answer seems right.
To still force override the StackPanel behavior, I think you'd need to dynamically compute-set the Width property of the contained elements OR some funky override of MeasureOverride. I'd rather use another layout manager/panel. Some things I noted..
The default value for HorizontalAlignment and VerticalAlignment properties of child elements is Stretch (if you don't specify one explicitly).
The StackPanel will stretch elements based on its Orientation property value. So
Orientation=Horizontal means all elements will be vertically stretched to max. Elements flow horizontally.
Orientation=Vertical means all elements will be horiz stretched to max. Elements flow vertically.
Unless explicitly specified, Width and Height of child elements are NaN. If you specify an explicit value, StackPanel will honor them over the Horiz and Vert Alignment settings.
The StackPanel itself has HorizontalAlignment and VerticalAlignment that adds a further layout twist. You can experiment with this example.
StackPanel
The default value is stretch for both
HorizontalAlignment and
VerticalAlignment of content that is
contained in a StackPanel.
HorizontalAlignment
When Height and Width properties are
explicitly set on an element, these
measurements take higher precedent
during layout and will cancel the
typical effects of setting
HorizontalAlignment to Stretch.
I needed items to be sized evenly, but stacked vertically.
I used a UniformGrid, and set the Columns property to 1. (tested with a TextBox, and it stretches like you want)

Resources