How to resize UserControl - wpf

I have a UserControl in my WPF application. The UserControl has a listbox and could be any size, and underneath are some buttons.
When I place my UserControl in one of my Views, I would like it resize vertically as the parent resizes. BUT, I'd like the ListBox to decrease in heigh, not the entire control. This would mean on smaller screens, the buttons remain on screen but they don't see much content in the ListBox. I can use a MinHeight to ensure the buttons and at least 1 item in the listbox is visible!
I found similar question such as WPF user control does not resize with main window but my code doesn't have any height or width
My UserControl
<Grid>
<StackPanel>
<ListBox ItemsSource="{Binding MessageList" ></ListBox>
<WrapPanel HorizontalAlignment="Center">
<Button Content="Expand" HorizontalAlignment="Left" Margin="0,0,40,0"/>
</WrapPanel>
</StackPanel>
</Grid>
And the View
<Grid>
<StackPanel>
<uc:RecentList MessageList="{Binding Messages}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</StackPanel>
</Grid>
It doesn't matter how I resize my View, the UserControl appears to stay at the exact same size.
I tried the ViewBoxbut this was scaling everything!
I also tried (in the UserControl)
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox ItemsSource="{Binding MessageList, RelativeSource={RelativeSource AncestorType=UserControl}}" Grid.Row="0"></ListBox>
<WrapPanel Grid.Row="1" HorizontalAlignment="Center">
<Button Content="Expand" HorizontalAlignment="Left" Margin="0,0,40,0"/>
</WrapPanel>
</Grid>
But no difference.

StackPanel does not stretch its child elements in the direction of its Orientation.
So remove all StackPanels and define two rows in the Grid in the UserControl:
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox Grid.Row="0" ItemsSource="{Binding MessageList ...}"/>
<WrapPanel Grid.Row="1" HorizontalAlignment="Center">
<Button Content="Expand" HorizontalAlignment="Left" Margin="0,0,40,0"/>
</WrapPanel>
</Grid>
In main view:
<Grid>
<uc:RecentList ... />
</Grid>
Take a look at the Panels Overview article on MSDN.

Related

UserControl Expand Vertically when Window is expanded

I'm having trouble getting my UserControl to expand vertically when my window is expanded.
My UserControl currently sits inside a ItemsControl which is stretching correctly by setting the VerticalAlignment="Stretch" property on the ItemsControl.
I add the following UserControl to the ItemsControl:
<UserControl MinWidth="930">
<Grid VerticalAlignment="Stretch" Background="Red">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200" />
<ColumnDefinition Width="730*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="400" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DockPanel Grid.Column="1" Grid.ColumnSpan="1" Grid.RowSpan="2" Grid.Row="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Pink" LastChildFill="True">
<ItemsControl Name="itemPanelOverview" Grid.Column="1" Background="Black" Margin="0"/>
</DockPanel>
</Grid>
</UserControl>
The UserControl is called in an ItemsControl inside of a TabControl like so:
<TabItem>
<TabItem.Header>
HEADER EG
</TabItem.Header>
<ItemsControl Name="contentItems" Grid.ColumnSpan="2" Grid.Column="1" Grid.Row="1" VerticalAlignment="Stretch" Background="Blue">
<Grid Height="35" Background="{DynamicResource GrayMenuGradient}" >
<Image Source="..." Stretch="None" />
<TextBlock Text="{Binding WelcomeMessage}" />
</Grid>
</ItemsControl>
</TabItem>
It appears that the ItemsControl (contentItems) is stretching as expected, as I can see the blue background stretching correctly.
I haven't set the height for this UserControl anywhere other than the Row Definitions. Is there something I'm missing?
There are at least two aspects at play here:
The first is that when you have items in an ItemsControl, each item is actually inside an ItemContainer, so it is the container that you want to stretch.
You can design the container by declaring an ItemContainerTemplate for your ItemsControl: http://msdn.microsoft.com/en-us/library/system.windows.controls.itemcontainertemplate.aspx
The second consideration is the ItemsPanelTemplate, which determines into what type of panel the items are placed. The ability of the items in the ItemsControl to fill up the available space is going to depend on the type of container as much as on the type of ItemContainer. For example, if you use a StackPanel for the ItemsPanelTemplate, it won't fill up available space because StackPanel grows and shrinks according to its contents. A DockPanel could potentially work, but only the last child would grow to fill available space. Perhaps a UniformGrid could do the trick.

Wrap Border around content with WPF

I have a Grid defined in my WPF application. I want to wrap a Border around the Grid itself. My problem is, the Border is filling the area available to the parent area. Because this, the Border is huge, but my content is small. My XAML is defined as follows:
<Grid>
<Border CornerRadius="0,0,2,2" BorderBrush="Black" BorderThickness="3" Margin="4">
<Grid HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="{Binding Path=Description}" />
<TextBlock Text="{Binding Path=Disclaimer}" />
</Grid>
</Border>
</Grid>
What am I doing wrong? How do I fix this?
Thanks!
you have the border inside your grid. do it like this <Border><Grid>...</Grid></Border>

How to show an element on other elemnts?

<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition ></RowDefinition>
</Grid.RowDefinitions>
<Expander >
<Border Background="Green" Height="50" Width="150"></Border>
</Expander>
<Button Content="ok" Height="20" Grid.Row="1" VerticalAlignment="Top"></Button>
</Grid>
Ummmm if I understand you correctly, you want to have your Expander on top of your Button?
In that case, just have both controls share the same grid cell. You will probably have to adjust your Button's margins or alignment to position it correctly
<Grid>
<Expander Canvas.ZIndex="0">
<Border Background="Green" Height="50" Width="150"></Border>
</Expander>
<Button Canvas.ZIndex="1" Content="ok" Height="20" VerticalAlignment="Top" Margin="0,30,0,0" />
</Grid>
It sounds like you need a canvas here as your parent container. Using a canvas you can use the SetZIndex method to change the Z index of various child controls, which causes one control to render in front of/"over" another control.
See this MSDN documentation on the method

ScrollViewer only scrollable when contents minHeight falls below

Lets take this code as basis:
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button
x:Name="_buttonAdd"
Grid.Row="0"
Click="_buttonAdd_Click"
Content="Daten hinzufügen" />
<Button
x:Name="_buttonDel"
Grid.Row="1"
Click="_buttonDel_Click"
Content="Daten löschen" />
<DataGrid
x:Name="_dataGrid"
Grid.Row="2"
MinHeight="200"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AutoGenerateColumns="True"
ItemsSource="{Binding Path=MitarbeiterList}"
VerticalScrollBarVisibility="Auto">
</DataGrid>
</Grid>
</ScrollViewer>
I want the ScrollViewer only to scroll, if the MinHeight of the DataGrid falls below 200.
On the other side I want the ScrollViewer not to scroll, if the MinHeight exceeds or in
other words: I want the DataGrid to stretch vertically to the visible area and shows its
own Scrollbar if necessary.
I hope you guys can solve my problem.
thanks in advance.
Thanks for your answer Wonko. Removing the scrollviewer isn't the key, but I can understand your thoughts. I finally did it my way, with a multibinding on MaxHeight:
<DataGrid
x:Name="_dataGrid"
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AutoGenerateColumns="True"
ItemsSource="{Binding Path=MitarbeiterList}"
VerticalScrollBarVisibility="Auto">
<DataGrid.MaxHeight>
<Multibinding Converter="{StaticResource MaxHeightConverter}">
<Binding Path="ActualHeight"
ElementName="_hostingWindow" />
<Binding Path="DataGridLocationPoint" />
</Multibinding>
</DataGrid.MaxHeight>
</DataGrid>
The MaxHeightConverter just subtract the ActualHeight of the window with the Y-Coordinate of the DataGrid-Location: So this sets the MaxHeight of the DataGrid always to the remaining available area of the window. The DataGridLocationPoint is set when the size of the window is changed. Like this:
public void dataGrid_SizeChanged(...)
{
GeneralTransform transform = dataGrid.TransformToAncestor(this);
Point DataGridLocationPoint = transform.Transform(new Point(0, 0));
}
(Sorry the code may not run, because I wrote it out of my Brain)
As you've written the code, the Grid will always have a minimum height of 300: 50 each for the first two rows, plus 200 for the DataGrid (as set by its MinHeight property). If the container of this Grid (or, actually, the ScrollViewer you've declared) becomes less than that, the ScrollViewer will show its vertical ScrollBar.
Personally, unless your container is going to be less than the size of the Buttons plus some of the DataGrid, I would simply remove the outer ScrollViewer. I can't tell for sure, but I believe that this will give you the functionality you desire.
The Buttons will always be visible, and the DataGrid will fill the remaining space. If the DataGrid needs more room than is visible, its ScrollBar will automatically show up.
One other thing I would probably do is to put the buttons next to each other horizontally - this will not only clean up the UI, but also save you some real estate. You could even make the row autosize, and size the buttons accordingly.
In summary, it might look something like this (I'd probably make a Style for the buttons as well, but you get the idea):
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button
x:Name="_buttonAdd"
Grid.Column="0"
Grid.Row="0"
HorizontalAlignment="Left"
Margin="5"
Click="_buttonAdd_Click"
Content="Daten hinzufügen" />
<Button
x:Name="_buttonDel"
Grid.Column="1"
Grid.Row="0"
HorizontalAlignment="Right"
Margin="5"
Click="_buttonDel_Click"
Content="Daten löschen" />
<DataGrid
x:Name="_dataGrid"
Grid.Column="0"
Grid.Row="1"
Grid.ColumnSpan="2"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
AutoGenerateColumns="True"
ItemsSource="{Binding Path=MitarbeiterList}"
VerticalScrollBarVisibility="Auto">
</DataGrid>
</Grid>

Bind the Height of the Listbox inside the StackPanel to StackPanel`s height

I want to bind the Height of the ListBox to the Height of the StackPanel so the ListBox stretches itself Vertically so the green area is not visible anymore.
When there is no item in the listbox its hidden.
When there is item > 1 the ListBox must be stretching itself to the add/del buttons so the add/del buttons are always at the bottom of the stackpanel(dont want to use dockpanel for that)
How can I do that? I get no binding erros?
<StackPanel x:Name="stack" Background="Green" DataContext="{Binding DocumentViewModelList/}" Orientation="Vertical" >
<ListBox SelectionMode="Single" VirtualizingStackPanel.IsVirtualizing="False"
SelectedItem="{Binding SelectedDocumentViewModel,Mode=TwoWay}"
Height="{Binding ElementName=stack,Path=Height}"
Width="Auto"
Focusable="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Grid.Row="1"
Name="documentListBox"
BorderThickness="1"
ItemsSource="{Binding DocumentList}"
Visibility="{Binding ElementName=documentListBox,Path=HasItems, Converter={StaticResource boolToVisibilityConverter}}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Id}" />
<TextBlock Text="{Binding Path=Name}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<!--<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding Mode=TwoWay, Path=IsSelected}" />
</Style>
</ListBox.ItemContainerStyle>-->
</ListBox>
</StackPanel>
Simply use a Grid instead of a StackPanel:
<Grid x:Name="grid"
Background="Green"
DataContext="{Binding DocumentViewModelList}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox Grid.Row="0"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" ..... />
<UniformGrid Grid.Row="1"
Rows="1"
HorizontalAlignment="Center"
VerticalAlignment="Bottom">
<Button Content="Delete" />
<Button Content="Add" />
<Button Content="Open" />
</UniformGrid>
</Grid>
The ListBox simply takes up the entire space of the first row, while the UniformGrid takes up the bottom row with only the space it needs (and makes the buttons all the same size as a bonus).
No need for hard-coded width/height values (or any bindings for height/width), and no value converters needed.
To implement the selective height (if has items x else y) use a value converter... also, I think height will be NaN so try ActualHeight (Bad practice but might work)... use a tool like snoop to see the values!
Is their a specific reason you are using a stackpanel? I grid will work better (StackPanel only give the min space needed where a grid can give as much space as needed)?
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox Grid.Row="1" />
<StackPanel Orientation="Horizontal" Grid.Row="1">
<!-- Buttons -->
</StackPanel>
</Grid>

Resources