A WPF ItemsControl and WrapPanel walk into a bar - c#

I have a ItemsControl that displays a bunch of UserControl's inside of a WrapPanel. This works perfectly, unless I have a bunch of UserControls, and then the overflow is rendered off screen, and I can't access it. My goal is to have the WrapPanel wrap horizontally, but once the controls are off the screen, to present a scroll bar, and this seems to not work for me.
<ItemsControl ItemsSource="{Binding Servers, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1" Margin="5,5,5,5">
<local:ServerControl DataContext="{Binding }" /> <!-- The actual UserControl -->
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
When the application first starts, this is what it looks like. What you can't see if that there should be 14 boxes viewed. The WrapPanel is doing its job, but it's rendered outside of the bounds of the window.
This shows all the UserControls, but I had to expand the window to be able to see them all.
Any help would be greatly appreciated.
Full XAML:
<Window x:Class="ServerMonitor.Wpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ServerMonitor.Wpf"
xmlns:models="clr-namespace:ServerMonitor.Wpf.Models"
xmlns:System="clr-namespace:System;assembly=mscorlib"
Title="Leading Hedge Server Monitor" Height="350" Width="800">
<Window.DataContext>
<models:MainWindowViewModel>
<models:MainWindowViewModel.MachineNames>
<!-- Test Servers -->
<System:String>T009</System:String>
<System:String>T010</System:String>
<System:String>T011</System:String>
<System:String>T012</System:String>
</models:MainWindowViewModel.MachineNames>
</models:MainWindowViewModel>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Menu Grid.Row="0">
</Menu>
<ItemsControl Grid.Row="1" ItemsSource="{Binding Servers, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1" Margin="5,5,5,5">
<local:ServerControl DataContext="{Binding }" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</Grid>
</Window>

<ItemsControl ItemsSource="{Binding Servers, Mode=OneWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="1" Margin="5,5,5,5">
<local:ServerControl DataContext="{Binding }" /> <!-- The actual UserControl -->
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Template>
<ControlTemplate TargetType="{x:Type ItemsControl}">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
OR
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Servers, Mode=OneWay}">
...
<ItemsControl/>
</ScrollViewer>

Change you second row height to *
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" /> <-- This is what you want -->
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
Setting a RowDefinition to Auto means it will calculate the cumulative DesiredHeight of all child elements in that row and assign it to the Height property of RowDefinition. So, as your WrapPanel grows, it will apply the height to that row and stretch out your parent Grid.

Related

Why UserControl isn't filling VerticalContentAlignment 'Stretch'?

Here is the parent Xaml:
<Grid VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*" /><!-- Should stretch vertically -->
</Grid.RowDefinitions>
<DockPanel Grid.Row="0">
<!-- Menu definition removed for brevity -->
</DockPanel>
<DockPanel Grid.Row="1"
VerticalAlignment="Stretch"
LastChildFill="True">
<ItemsControl DockPanel.Dock="Top"
ItemsSource="{Binding Designer}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
Background="YellowGreen"/>
</DockPanel>
</Grid>
The ItemsSource binding is to an ObservableCollection. When the view is added to the collection, it gets updated in the main shell (view). Here is the UserControl that is added:
<UserControl x:Class="Prototype.StateMachineDesignerApp.Views.ProjectDesigner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Prototype.StateMachineDesignerApp.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="500"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
Margin="0">
<Grid VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="20" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="0"
BorderBrush="DimGray"
BorderThickness="1"
Background="LightSlateGray"
Margin="1" />
<Border Grid.Row="1"
Margin="1">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="3" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0"
BorderBrush="DarkGoldenrod"
BorderThickness="1" />
<GridSplitter Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
<Border Grid.Column="2"
BorderBrush="DarkGoldenrod"
BorderThickness="1" />
</Grid>
</Border>
</Grid>
</UserControl>
The UserControl is not filling the entire vertical space:
Row[1] of the outermost Grid is stretching vertically as is evidenced by the ItemsControl.Background filling the area.
For reference, this is what I am expecting:
For what its worth, I've looked at numerous questions on SO regarding this exact issue but none of the solutions seem to help. Then again, none of the examples I saw used an ItemsControl with binding.
This happens because the ItemsControl throw all of our items into a vertically aligned StackPanel by default. It's very easy to change though, since the ItemsControl allows you to change which panel type is used to hold all the items.
<ItemsControl DockPanel.Dock="Top"
ItemsSource="{Binding Designer}"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Stretch"
VerticalAlignment="Stretch"
VerticalContentAlignment="Stretch"
Background="Transparent">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

ListView with dynamic raws and columns that stays propetinal aytomatically

I am developing a WPF application.
have a list of user controls
<Grid >
<ListView ItemsSource="{Binding MyList}" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" >
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical" ItemWidth="{Binding (ListView.View).ItemWidth, RelativeSource={RelativeSource AncestorType=ListView}}"
MinWidth="{Binding ItemWidth, RelativeSource={RelativeSource Self}}"
ItemHeight="{Binding (ListView.View).ItemHeight, RelativeSource={RelativeSource AncestorType=ListView}}"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate >
<DataTemplate >
<Views:MyUserControl Height="auto" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" SnapsToDevicePixels="True"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
And MyUserControl has another list of user controls "DetailUserControl":
<UserControl x:Class="MyUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="45" />
<RowDefinition />
</Grid.RowDefinitions>
<Border Grid.RowSpan="2" >
<StackPanel VerticalAlignment="Top" Orientation="Horizontal" HorizontalAlignment="Center" >
<TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10" Text="{Binding Title}" />
</StackPanel>
</Border>
<Border Grid.Row="1" Style="{StaticResource SummaryInnerFill}" >
<ItemsControl ItemsSource="{Binding DetailUserControls}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
</Grid>
I used wrap panel to make all the usercontrols to be orderd from left to right one after the other.
I want the layout to be optimized and use as much as it can from the first line, and only then it will move to the second line.
here is an example (sorry for my drawing skills :) )
I have the following layout:
if I change the order inside the tiles I can have the following layout (that contains the same tiles but don't waste space an is much more organized):
Is there a panel I can use that will take care of it?
You can make use of Uniform Control Grid. You can specify Rows and Columns Properties. Controls are added to the grid in the order that they are declared.Its child controls are organised into a tabular structure of rows and columns. Unlike the Grid control, you don't have fine-grained control of the layout.
Example,
<UniformGrid Name="uniformGrid1"
Rows="2"
Columns="3">
<Button Content="blah"
Margin="2" />
<Button Content="blah1"
Margin="2" />
<Button Content="blah2"
Margin="2" />
</UniformGrid>
UniformGrid

Extracting data templates in user controls

There are known problems for setting up caliburn binding in DataTemplates.
In this answer EisenbergEffect suggests extracting Data Templates into a user control.
How can this be achieved?
In my user control I have lots of DataTemplates and ran into those problems. Conventions are not applied and I have to use "classical" Binding.
I could only imagine to extract the whole control with the DataTemplate, this would give me lots of smaller controls, but I see no way to only extract the DataTemplate.
Here is an example XAML
<Grid>
<Grid.Resources />
<Grid.RowDefinitions>
<RowDefinition Height="10*" />
<RowDefinition Height="2*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Border HorizontalAlignment="Stretch"
BorderBrush="Transparent"
BorderThickness="0">
<ScrollViewer HorizontalContentAlignment="Stretch"
Background="Yellow"
BorderBrush="Transparent"
BorderThickness="0"
CanContentScroll="True"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<!-- Conventions work here -->
<ListView x:Name="Computers"
HorizontalContentAlignment="Stretch"
Background="Red"
BorderThickness="0">
<ListView.ItemTemplate>
<DataTemplate>
<Border Background="Transparent"
BorderBrush="Transparent"
BorderThickness="0">
<ListView HorizontalContentAlignment="Stretch"
Background="Black"
ItemsSource="{Binding HardwareComponents}"> <!-- Conventions to not work here -->
<ListView.ItemTemplate>
<DataTemplate>
<Border Background="Aquamarine"
BorderBrush="DarkGray"
BorderThickness="1">
<Grid Background="Transparent" cal:Message.Attach="[Event MouseDown] = [Action Expand($dataContext)]" >
<Grid.RowDefinitions>
<RowDefinition Height="20" />
</Grid.RowDefinitions>
<Border BorderBrush="Red" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<!-- Even more DataTemplates come here -->
</Border>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ScrollViewer>
</Border>
</Grid>

How can my ItemsControl get the width property (from Items)

I have a small problem how to set the ItemsControl Property Width. I use Datatemplates to fill my ItemControl. Every Item is a small Grid (Text and Rectangle) in a Canvas.
When I use the ItemsControl in a Window the ActualWidth (and DesiredWidth) is always 0.0.
Only when i hardcode the Width Property of the Canvas the ItemsControl has a size. But that is far from perfect. I would rather like when the Control gets the width (or DesiredWidth) from the biggest Item.
Here is a short code sample:
<ItemsControl ItemsSource="{Binding mySource}"
ItemTemplate="{DynamicResource myTemplate}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type type:myType}" x:Key="myTemplate">
<Grid Width="Auto" Height="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="1"
Height="1"
HorizontalAlignment="Stretch"
Fill="Blue"/>
<TextBlock Grid.Row="0"
Grid.Column="1"
Margin="3"
Text="{Binding Text}"
FontWeight="Bold"
TextWrapping="Wrap"
TextAlignment="Left"/>
</Grid>
</DataTemplate>
<v:PositionMultiConverter x:Key="PositionMultiConverter"/>
</ItemsControl.Resources>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Bottom">
<Setter.Value>
<MultiBinding Converter="{StaticResource ResourceKey=PositionMultiConverter}">
<Binding Path="Position"/>
<Binding Path="ActualHeight" ElementName="canvas"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas x:Name="canvas" IsItemsHost="True" Width="20"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

How to add DataTemplates to ScatterViewItem

I'm trying to build a kind of Wizard with a ScatterViewItem. In the lower area are buttons for Next, Back and Cancel. When pressing on the Next or Back button the upper part should show the next 'page'. I've tried using a DataTemplate, but the DataTemplate is not showing in the upper area. I get only a blank area.
I'm still new to WPF and Surface and maybe try something that did not work. May you have better ideas.
Here's the code:
<s:SurfaceWindow.Resources>
<DataTemplate x:Key="LoadProject">
<StackPanel Orientation="Vertical">
<Label Name="lbl_Database" Content="Database" />
<s:SurfaceTextBox Name="txt_Database"/>
<Label Name="lbl_Project" Content="Project" />
<s:SurfaceTextBox Name="txt_Project"/>
</StackPanel>
<DataTemplate x:Key="LoadProject">
</s:SurfaceWindow.Resources>
<s:ScatterView>
<s:ScatterViewItem Width="300" Height="300">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<ItemsControl x:Name="DynamicArea" ItemTemplate="{StaticResource LoadProject}" Grid.Column="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<StackPanel Orientation="Horizontal" Grid.Row="1">
<s:SurfaceButton Name="btn_Next" Content="Next" />
<s:SurfaceButton Name="btn_Back" Content="Back" />
<s:SurfaceButton Name="btn_Cancel" Content="Cancel" />
</StackPanel>
</Grid>
</s:ScatterViewItem>
</s:ScatterView>

Categories

Resources