WPF - DockPanel Question - wpf

I am very new to WPF and am trying to understand the DockPanel control. I am building a Window, and am using a DockPanel in order to dock controls the way I want them. However, is it possible to dock the DockPanel itself, say to the top of the Window? I can't seem to find a way to do this. Ideally I would like to have the DockPanel dock itself to the top of my Window so that when the Window is resized, it grows or shrinks as the window is resized horizontally.
The problem is I cannot find a Doc property on the DockPanel itself. Is this not possible to do?

You would need to place it within another DockPanel if you wished to dock it.
That being said, my experience has been that when a layout gets to the level of complexity you are describing, it's often better to switch to using a Grid control. This gives you a huge amount of control over layout, and makes having dynamically growing rows/columns (with fixed size portions between) easy.
Edit in response to comments:
You can use a Grid that scales as needed. For example, by using star syntax, you can have a grid where the top row is always 1/3rd of the window size, and the bottom row is 2/3rds, like so:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
</Grid>
For details on options for sizing, see GridLength. You can use "Auto" (fit to contents), "*" to scale based on space, or a fixed height (put in a number).

I'm not sure if this what you want but I have a DockPanel with a Menu. When I resize the window the menu resizes with it.
Here is my code:
<Grid>
<DockPanel Height="50" Margin="0" Name="MenuDockPanel" VerticalAlignment="Top" Width="Auto">
<Menu Height="23" Name="MenuPanel" Width="Auto" VerticalAlignment="Top">
<MenuItem Header="_File">
</Menu>
</DockPanel>
<Grid>
Hope this helps

Related

Two components in the same .xaml Grid.Row. When I scroll down, one of them floats, the other scrolls. I need both of them to scroll

I've come accross some .xaml code I need to fix. Currently, it's made of 2 grid components with this layout:
<Grid d:SomeDataContext>
<Grid.RowDefinitions>
<RowDefinition Height="100" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListView Style="{StaticResource SomeListStyle}" Grid.Row="0" Margin="0,0,0,0" Grid.RowSpan="2">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" Margin="0,0,0,80" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<mycontrol:Panel1Control />
<mycontrol:Panel2Control />
<mycontrol:Panel3Control />
<mycontrol:Panel4Control />
<mycontrol:Panel5Control />
</ListView>
<controls:HeaderControlTransparent Grid.Row="0" />
</Grid>
When this xaml is interpreted, in run time you get a nice list scroller made up of Panel1, Panel2, etc... On top of that, there's some transparent header aligned to the top of the screen.
In fact, this header is "SO aligned" it doesn't move when scrolling, but floats.
The desired placement is something like:
[Header]
[Panel1]
[Panel2]
[Panel3]
[Panel4]
[Panel5]
And the desired behaviour when someone scrolls, shoule be the [header] element disappearing from the upper part of the screen as if it was one of the other panels.
Desired (not happening)
...
[Panel3]
[Panel4]
[Panel5]
Undesired (happening)
[Header]
[Panel4]
[Panel5]
Currently the header doesn't scroll and it just floats on top of everything, aligned to the top part of the screen, while the panels scroll.
Any hint I should be addressing? I think everything is in order. I can't see why one row of the grid scrolls and the other doesn't.
I'm kind of new to .xaml so, this may also be one of the reasons of my troubles.
Thanks.
So may as well add a little to the comment for some additional explanation to future readers and apparently some easy points.
In your example there's two rows (for I guess some other reason) of the Grid with both of the children within the same cell. Since the Header control part sits underneath the ListView in the DOM then it's rendered logically on top of the underlying ListView.
A ListView already as a ScrollViewer built into the control template to nest its items in. However the OP requires elements outside of the controls template to scroll with the items in the ListView.
So by Embedding both the ListView and the Header control in the right order within their own parent ScrollViewer, the desired result can be had of scrolling both bits of content.
e.g. (in pseudo)
<Grid>
<ScrollViewer>
<objects2/>
</ScrollViewer>
<object1/>
</Grid>
Would float object 1 over top of objects 2 and keep object 1 stationary while everything else scrolls. However;
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<object1/>
<objects2 Grid.Row="1"/>
</Grid>
</ScrollViewer>
Will scroll all the children keeping their stacked location.
Hope this helps, cheers!

How to force Grid to shrink an auto sized row that contains ScrollViewer (when needed)?

How would one create this layout?:
The idea is to keep the blue text always just below the green element BUT when there is not enough space, the green element should be placed inside a scrollable container (with vertical scrollbar visible).
I have tried StackPanel and Grid and both failed. The StackPanel does not shrink rows at all. The Grid does the same if the top RowDefinition's height is set to "auto" (the ScrollViewer inside does not show its scrollbar). If it's set to a star, the blue text is moved down to at least half of the space (depends of the bottom row's height setting). The bottom row's height can be either an "auto" or a star with VerticalAlignment of the blue text set to Top BUT the row's height must be kept at least as big as the text's. Of course the whole area (black box) cannot be stretched by its content - it's determined by the size of the window.
How? Please keep the responses to be based on XAML if possible.
Inspired by Eirik's approach of constraining a greedy container by putting it inside other tight container, I've discovered a really simple way to achieve what I wanted. I only needed a container that shrinks its one child first, then (when the first one completely disappears) the second one. And there is such container: the DockPanel. Here goes:
<DockPanel LastChildFill="False">
<DockPanel DockPanel.Dock="Top">
<TextBlock DockPanel.Dock="Bottom" TextWrapping="Wrap" Grid.Row="1">Automatically wrapped text of unknown length.</TextBlock>
<ScrollViewer>
<TextBlock TextWrapping="Wrap">In this case the element is too big to fit inside whole space (the black box) with the blue text below. I want the scrollbar to be shown instead of moving the blue text outside of the black box (and clipped)</TextBlock>
</ScrollViewer>
</DockPanel>
</DockPanel>
As simple as that! :) I hope it helps someone.
<Grid Name="outerGrid">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Canvas>
<Grid MaxWidth="{Binding ElementName=outerGrid, Path=ActualWidth}" MaxHeight="{Binding ElementName=outerGrid, Path=ActualHeight}">
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ScrollViewer Width="{Binding ElementName=outerGrid, Path=ActualWidth}">
<TextBlock TextWrapping="Wrap">In this case the element is too big to fit inside whole space (the black box) with the blue text below. I want the scrollbar to be shown instead of moving the blue text outside of the black box (and clipped)</TextBlock>
</ScrollViewer>
<TextBlock TextWrapping="Wrap" Grid.Row="1">Automatically wrapped text of unknown length.</TextBlock>
</Grid>
</Canvas>
</Grid>
The auto height will grow to match the height of the content of that row.
The star height will let that row grow in height to fill the rest of the height of the grid, preventing the ScrollViewer to grow more than what's visible.
Edit: If you put the Grid inside another Grid like the XAML above you should get the behavior you want.
The second row of the outer row acts as a "filler" to fill the rest of the space the outer Grid.
Edit 2: Try the edited XAML above. I've put the inner Grid inside a Canvas (to prevent clipping) and bound the MaxWidth and MaxHeight of the inner Grid to the ActualWidth and ActualHeight of the outer Grid to keep the inner Grid the same size as the outer Grid.
Edit 3: Added binding to the Width of the ScrollViewer to keep it the same width as the rest.

WP7: ScrollViewer viewport size

Windows Phone 7. I have a ScrollViewer inside a StackPanel inside a PivotItem inside a Pivot. Above the ScrollViewer, there are some other controls. My intention is that the ScrollViewer takes the available lower part of the screen (~400px), and its content is scrolled vertically (content height ~800px).
Now, right now there's no vertical scrolling - when I try to drag, the view returns in the previous position, as if the viewport size exactly matches the content size. When I look at the ViewportHeight property, it's ~800px - same as content.
Height of the ScrollViewer is not set ("Auto"); I was assuming it would take exactly the available space. That's obviously not the case. Question - short of setting Height by hand, is there a way to implement the logic of "viewport height is exactly how much vertical space you've got left"?
EDIT: here's the XAML, irrelevant details removed:
<Pivot x:Name="Root">
<ctls:PivotItem>
<ctls:PivotItem.Header>Title</ctls:PivotItem.Header>
<StackPanel>
<!-- More stuff here-->
<ScrollViewer Name="MenuPanel" HorizontalScrollBarVisibility="Disabled">
<Canvas x:Name="Menu" HorizontalAlignment="Left" VerticalAlignment="Top">
</Canvas>
</ScrollViewer>
</StackPanel>
</ctls:PivotItem>
</Pivot>
Width and height of the canvas are set in code.
Two things:
A StackPanel doesn't allow it's children to automatically take up the rest of the space available. Use a Grid, instead, with defined Rows. This allows your ScrollViewer to be in a container which is the exact height remaining vertically.
Your Canvas (inside the ScrollViewer) is aligned to top and left, and without a size defined, is exactly 0 pixels high and 0 pixels wide.
Good luck.
<Pivot x:Name="Root">
<ctls:PivotItem>
<ctls:PivotItem.Header>Title</ctls:PivotItem.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<!-- More stuff here-->
</Grid>
<ScrollViewer
Grid.Row="1"
Name="MenuPanel">
<Canvas x:Name="Menu"
Height="500"
Width="500"/>
</ScrollViewer>
</StackPanel>
</ctls:PivotItem>
</Pivot>
Without seeing your XAML this is assummed - but based on commonly seen issues
The ScrollViewer is actually being assigned all the space it needs to include all it's content items.
Either give it an absolute height or wrap it in a Grid, which will limit it to the available space within the StackPanel.

Silverlight 4 - ScrollViewer and child DataGrid MinHeight

edit: I'm rewriting almost the entire question because I realized the question was incorrect and confusing. I apologize for this, but the question had incorrect assumptions that made it impossible to answer. I originally tried to simplify it to make it easier to understand, but this made it impossible to replicate my problem.
If I have an DataGrid with a MinHeight in a ScrollViewer, I would expect that as my ViewPort shrinks, the ActualHeight of the element would be decreased until it hits MinHeight before the scrollbars show up.
Instead, it seems that when the datagrid's rows cumulative heights add up to more than the MinHeight, this value overrides MinHeight
Is there a way to do this without manually sizing everything and having a ton of code?
Example:
<ScrollViewer VerticalScrollBarVisibility="Auto" Background="Red">
<Grid x:Name="LayoutRoot" Background="Black" HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition MinHeight="20"/>
<RowDefinition Height="80"/>
</Grid.RowDefinitions>
<sdk:DataGrid AutoGenerateColumns="True" Name="dataGrid1" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" MinHeight="20" />
<Rectangle Fill="Blue" Width="100" Height="80" Grid.Row="1" />
</Grid>
</ScrollViewer>
If you were to populate this grid with some rows, if you maximize the window, the grid takes up most of the space and has white space after the rows. If you shrink it down, the layout takes away from the white space until that space runs out, then the root level ScrollViewer kicks in, even though MinHeight has not been reached.
If you replace the DataGrid with another rectangle, the behavior is different (obviously). The new rectangle would shrink down to height 20.
How do I achieve this with the grid? My requirements are to have nested scrollbars on my SL page (which I find distasteful, but it's not in my control). The idea is that the top level scrollbars are a "last resort" of sorts.
What about this:
<ScrollViewer>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="250" />
</Grid.RowDefinitions>
<Rectangle MinHeight="150" Background="Red" Grid.Row="0" />
<Rectangle Height="250" Background="Blue" Grid.Row="1" />
</Grid>
You did not have the Grid.Row values set on either of the rectangles.
You've not provided sufficient information to solve your specific problem. However, it is easy to demonstrate that the ScrollViewer does work in exactly the fashion you desire by distilling down to something as simple as:
<UserControl ...>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Border MinHeight="200" BorderBrush="Blue" BorderThickness="1" Background="Red"/>
</ScrollViewer>
</UserControl>
Put this in a standalone Silverlight application in the main page and you'll see that the ScrollViewer only displays the vertical scroll bar when the window is small enough. You can download the solution here.
This is because ScrollViewer itself has a border and padding that occupies little space of its own. Try considering little extra height that should match space of scrollbar border.
Another option will be to change the control template of scrollviewer and remove the border and extra space occupied around content presenter. And set horizontal scroll visibility to collapsed so it will not occupy space.

How can I get a vertical scrollbar in my ListBox?

In the example below I have a ListBox with dozens of font names in it.
I would have thought it would automatically have a vertical scrollbar on it so that you can select ANY font, not just the first ones in the list, but it doesn't.
So I added a "ScrollViewer" and that puts a "scrollbar area" on the right but there is no scrollbar in the scrollbar area so that you can scroll (!).
Why isn't a scrollbar automatic and how do I force it to have a scrollbar?
<StackPanel Name="stack1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ScrollViewer>
<ListBox Grid.Row="0" Name="lstFonts" Margin="3" ItemsSource="{x:Static Fonts.SystemFontFamilies}"/>
</ScrollViewer>
</Grid>
</StackPanel>
The problem with your solution is you're putting a scrollbar around a ListBox where you probably want to put it inside the ListBox.
If you want to force a scrollbar in your ListBox, use the ScrollBar.VerticalScrollBarVisibility attached property.
<ListBox
ItemsSource="{Binding}"
ScrollViewer.VerticalScrollBarVisibility="Visible">
</ListBox>
Setting this value to Auto will popup the scrollbar on an as needed basis.
ListBox already contains ScrollViewer. By default the ScrollBar will show up when there is more content than space. But some containers resize themselves to accommodate their contents (e.g. StackPanel), so there is never "more content than space". In such cases, the ListBox is always given as much space as is needed for the content.
In order to calculate the condition of having more content than space, the size should be known. Make sure your ListBox has a constrained size, either by setting the size explicitly on the ListBox element itself, or from the host panel.
In case the host panel is vertical StackPanel and you want VerticalScrollBar you must set the Height on ListBox itself. For other types of containers, e.g. Grid, the ListBox can be constrained by the container. For example, you can change your original code to look like this:
<Grid Name="grid1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" Name="lstFonts" Margin="3"
ItemsSource="{x:Static Fonts.SystemFontFamilies}"/>
</Grid>
</Grid>
Note that it is not just the immediate container that is important. In your example, the immediate container is a Grid, but because that Grid is contained by a StackPanel, the outer StackPanel is expanded to accommodate its immediate child Grid, such that that child can expand to accommodate its child (the ListBox).
If you constrain the height at any point — by setting the height of the ListBox, by setting the height of the inner Grid, or simply by making the outer container a Grid — then a vertical scroll bar will appear automatically any time there are too many list items to fit in the control.
I added a "Height" to my ListBox and it added the scrollbar nicely.
Scroll Bar is added to the List box automatically unless its visibility is set to Hidden. Whenever the size of List Items exceeds the one, which can be shown inside a list box vertical or horizontal list box can be seen during the run time.
In my case the number of items in the ListBox is dynamic so I didn't want to use the Height property. I used MaxHeight instead and it works nicely. The scrollbar appears when it fills the space I've allocated for it.
I was having the same problem, I had a ComboBox followed by a ListBox in a StackPanel and the scroll bar for the ListBox was not showing up. I solved this by putting the two in a DockPanel instead. I set the ComboBox DockPanel.Dock="Top" and let the ListBox fill the remaining space.
XAML ListBox Scroller - Windows 10(UWP)
<Style TargetType="ListBox">
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Visible"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Visible"/>
</Style>

Resources