I creating a control for WPF, and I have a question for you WPF gurus out there.
I want my control to be able to expand to fit a resizable window.
In my control, I have a list box that I want to expand with the window. I also have other controls around the list box (buttons, text, etc).
I want to be able to set a minimum size on my control, but I want the window to be able to be sized smaller by creating scroll bars for viewing the control.
This creates nested scroll areas: One for the list box and a ScrollViewer wrapping the whole control.
Now, if the list box is set to auto size, it will never have a scroll bar because it is always drawn full size within the ScrollViewer.
I only want the control to scroll if the content can't get any smaller, otherwise I don't want to scroll the control; instead I want to scroll the list box inside the control.
How can I alter the default behavior of the ScrollViewer class? I tried inheriting from the ScrollViewer class and overriding the MeasureOverride and ArrangeOverride classes, but I couldn't figure out how to measure and arrange the child properly. It appears that the arrange has to affect the ScrollContentPresenter somehow, not the actual content child.
Any help/suggestions would be much appreciated.
You problem arises, because
Control
s within aScrollViewer
have virtually unlimited space available. Therefore your innerListBox
thinks it can avoid scrolling by taking up the complete height necessary to display all its elements. Of course in your case that behaviour has the unwanted side effect of exercising the outerScrollViewer
too much.The objective therefore is to get the
ListBox
to use the visible height within theScrollViewer
iff there is enough of it and a certain minimal height otherwise. To achieve this, the most direct way is to inherit fromScrollViewer
and overrideMeasureOverride()
to pass an appropriately sizedavailableSize
(that is the givenavailableSize
blown up to the minimal size instead of the "usual" infinity) to theVisual
s found by usingVisualChildrenCount
andGetVisualChild(int)
.for 2 ScrollViewer
code:
Create a method in the code-behind that sets the ListBox's MaxHeight to the height of whatever control is containing it and other controls. If the Listbox has any controls/margins/padding above or below it, subtract their heights from the container height assigned to MaxHeight. Call this method in the main windows "loaded" and "window resize" event handlers.
This should give you the best of both worlds. You are giving the ListBox a "fixed" size that will cause it to scroll in spite of the fact that the main window has its own scrollbar.
I ended up combining Daniels answer and Heiner's answer. I decided to post the entire solution to make it easier for people to adopt this if needed. Here's my decorator class:
and here's how I use it in the xaml:
The above creates a textbox that will grow vertically (until it hits MaxHeight) but will match the parent's width without growing the outer ScrollViewer. Resizing the window/ScrollViewer to less than 100 wide will force the outer ScrollViewer to show the horizontal scroll bars. Other controls with inner ScrollViewers can be used as well, including complex grids.
While I wouldn't recommend creating a UI that requires outer scroll bars you can accomplish this pretty easily:
I don't really recommend this. WPF provides exceptional layout systems, like Grid, and you should try to allow the app to resize itself as needed. Perhaps you can set a MinWidth/MinHeight on the window itself to prevent this resizing?
I used Daniels solution. That works great. Thank you.
Then I added two boolean dependency properties to the decorator class:
KeepWidth
andKeepHeight
. So the new feature can be suppressed for one dimension.This requires a change in
MeasureOverride
: