WPF UniformGrid Layout - wpf

I have an ItemsControl in which I plan on hosting a listing of fruit objects. I have a list of Fruit objects all with their own DataTemplates. I have a Grape object, an Orange object and a Kiwi object.
I want to use a UniformGrid so that all cells have equal size, but I want the Kiwi object to span only 1 cell, I want the Grape to span 2 cells (ColumnSpan = 2) and I want the Orange control to span 4 cells (ColumnSpan = 2 and RowSpan = 2)
How can I accomplish this?

Items in a UniformGrid cannot span multiple cells.
Why not use a regular Grid instead with the height/width of the rows/columns set to * so they're all equally sized? If you want cells to be a perfect square with the height matching the width, be sure to bind the Grid's Height to the Width, or vice versa.
It should be noted that you need to apply the Grid positioning properties in the ItemContainerStyle, not on the item itself, since an ItemsControl wraps each element inside a ContentPresenter before adding that item to the ItemsPanel (see my blog post here for more details)
<ItemsControl ItemsSource="{Binding MyCollection}">
<!-- ItemsPanelTemplate -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- ItemContainerStyle -->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column"
Value="{Binding ColumnIndex}" />
<Setter Property="Grid.Row"
Value="{Binding RowIndex}" />
<!-- Can either use a DataTrigger to determine these values,
or store them on the object itself -->
<Setter Property="Grid.RowSpan"
Value="{Binding RowSpan}" />
<Setter Property="Grid.ColumnSpan"
Value="{Binding ColumnSpan}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>

You cant do row or column spanning in a uniform grid - see stackoverflow question below
WPF: Is it possible to do a row/column span in UniformGrid?
However, you may be able to achieve a similar effect with a WrapPanel if your items have uniform dimensions. Heres an example
<ItemsControl Width="300">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Items>
<Rectangle Fill="Orange" Height="100" Width="100" />
<Rectangle Fill="Red" Height="100" Width="100" />
<Rectangle Fill="Blue" Height="100" Width="100" />
<Rectangle Fill="Green" Height="100" Width="200" />
<Rectangle Fill="Yellow" Height="100" Width="100" />
<Rectangle Fill="Brown" Height="100" Width="100" />
<Rectangle Fill="Black" Height="100" Width="200" />
</ItemsControl.Items>
</ItemsControl>

Related

WPF Menu - How to use a different template for top-level items

I would like to apply different templates to my WPF menu control depending on whether the item is a "top level" item or not. In particular, I want a larger icon above the text for top-level items and a smaller icon to the left of the text for the other items.
This is the (working) XAML I'm using now, which correctly formats the top-level items but leaves the other items looking silly and too-large:
<WrapPanel Height="Auto">
<Menu ItemsSource="{Binding DataContext.EventMenu.TopLevel, ElementName=UserControl}">
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="IsEnabled" Value="true"/>
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="CommandParameter" Value="{Binding EventType}"/>
</Style>
</Menu.ItemContainerStyle>
<Menu.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image Grid.Row="0" Width="32" Height="32" Margin="5" VerticalAlignment="Center" Source="{Binding Icon32}"/>
<TextBlock Grid.Row="1" Margin="5" Text="{Binding Name}"/>
</Grid>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
</WrapPanel>
I'd like the other items to have a template like this:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Height="*"/>
<ColumnDefinition Height="Auto"/>
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Width="16" Height="16" Margin="5" VerticalAlignment="Center" Source="{Binding Icon16}"/>
<TextBlock Grid.Column="1" Margin="5" Text="{Binding Name}"/>
</Grid>
How can I have two different templates distinguished by "top-levelness". I would prefer not to have to add a property to indicate which is a top-level item, but can do so if necessary.
You should create two styles for the menu control (in a separate resource dictionary). One is with a style key (for the top-level items) and another one without a key so that one will be the default style. Therefore you need to set the style explicitly only in the top-level items.

WPF DataTemplate - define in which grid's column TextBox will be displayed [duplicate]

Hope this is not a dupe.
I would like to be able to do the following in XAML:
<DataTemplate DataType="{x:Type TestApp:ButtonVM}">
<Button
Grid.Column="{Binding GridColumn}"
Grid.Row="{Binding GridRow}"
Content="{Binding Path=Info}"
/>
</DataTemplate>
The Content binding works fine but Grid.Column and Grid.Row simply don't exist in the produced object. Not even when I set them to some value without binding (like in Grid.Column="1"). I've snooped the application and saw that inside my grid nobody ever sets Grid.Column and Grid.Row.
Any ideas?
Solved it myself with help from the blogs.
As far as I understand you simply can't do the attached property binding inside.
The following solves the problem in an instant (ItemContainerStyle!):
<DataTemplate DataType="{x:Type TestApp:GridVM}">
<ItemsControl ItemsSource="{Binding Path=Children}">
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding GridRow}" />
<Setter Property="Grid.Column" Value="{Binding GridColumn}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid ShowGridLines="True" Style="{Binding Path=Style}">
<Grid.RowDefinitions>
<RowDefinition Height=".5*" />
<RowDefinition Height=".5*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" />
<ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</DataTemplate>

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>

How to create a bordered table in Silverlight?

I am currently using Silverlight 3. I want to create the equivalent of a 2x2 HTML table. I want each cell to have a black border. How do I do this in Silverlight? Isn't there a property I can set on a Grid element to make each cell have a border?
Nope. Grid is simply one of a number of panel types that are designed to layout their children in specific way. Grids are used extensively in many different and often nested ways. They are lightweight and therefore do not carry loads of baggage that may or may not get used, such as in this a bunch of properties to determine borders on "cells".
To create a border on each cell simply use the Border control:
<Grid>
<Grid.Resources>
<Style x:Key="borderStyle" TargetType="Border">
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Padding" Value="2" />
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Style="{StaticResource borderStyle}" Grid.Row="0" Grid.Column="0">
<!-- Cell 0.0 content here -->
</Border>
<Border Style="{StaticResource borderStyle}" Grid.Row="0" Grid.Column="1">
<!-- Cell 0.1 content here -->
</Border>
<Border Style="{StaticResource borderStyle}" Grid.Row="1" Grid.Column="0">
<!-- Cell 1.0 content here -->
</Border>
<Border Style="{StaticResource borderStyle}" Grid.Row="1" Grid.Column="1">
<!-- Cell 1.1 content here -->
</Border>
</Grid>

WPF Style Setter * Height and Width

I'm trying to create a WPF application which consists of a 9x9 grid with the row and columns using different styles. What I'm hoping to do is provide a star value for the height and width of the column and row definitions. This does not appear to work in the current context. Is this even possible, and if so, how?
<Window x:Class="BaroqueChessUI.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="500" Width="500">
<Window.Resources>
<Style x:Key="LightBackground" >
<Setter Property="Control.Background" Value="Teal" />
</Style>
<Style x:Key="DarkBackground" >
<Setter Property="Control.Background" Value="Maroon" />
</Style>
<Style x:Key="FileStyle">
<Setter Property="Control.Width" Value="0.12" />
</Style>
<Style x:Key="RankStyle">
<Setter Property="Control.Height" Value="0.12" />
</Style>
<Style x:Key="FileHeadingStyle">
<Setter Property="Control.Width" Value="0.04" />
</Style>
<Style x:Key="RankHeadingStyle">
<Setter Property="Control.Height" Value="0.04" />
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Name="rdRank" Style="{StaticResource FileHeadingStyle}" />
<RowDefinition Name="rdRank1" Style="{StaticResource FileStyle}" />
<RowDefinition Name="rdRank2" Style="{StaticResource FileStyle}" />
<RowDefinition Name="rdRank3" Style="{StaticResource FileStyle}" />
<RowDefinition Name="rdRank4" Style="{StaticResource FileStyle}" />
<RowDefinition Name="rdRank5" Style="{StaticResource FileStyle}" />
<RowDefinition Name="rdRank6" Style="{StaticResource FileStyle}" />
<RowDefinition Name="rdRank7" Style="{StaticResource FileStyle}" />
<RowDefinition Name="rdRank8" Style="{StaticResource FileStyle}" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Name="cdFile" Style="{StaticResource RankHeadingStyle}" />
<ColumnDefinition Name="cdFile2" Style="{StaticResource RankStyle}" />
<ColumnDefinition Name="cdFile3" Style="{StaticResource RankStyle}" />
<ColumnDefinition Name="cdFile4" Style="{StaticResource RankStyle}" />
<ColumnDefinition Name="cdFile5" Style="{StaticResource RankStyle}" />
<ColumnDefinition Name="cdFile6" Style="{StaticResource RankStyle}" />
<ColumnDefinition Name="cdFile7" Style="{StaticResource RankStyle}" />
<ColumnDefinition Name="cdFile8" Style="{StaticResource RankStyle}" />
</Grid.ColumnDefinitions>
</Grid>
A grid column definition / row definition define layout, and within the defined areas you should add controls which should be styled (using the attached properties as you are could get tedious), so try not styling the rowdefintions / columnsdefinitions themselves.
Styling Items:
You can enter a control into a row / column like so (sorry if i'm patronizing):
<Rectangle Grid.Row="0" Grid.Column="0" ></Rectangle>
Then define the Style on the control within the Row/Column.
<Rectangle Grid.Row="0" Grid.Column="0" Style="{StaticResource DarkBackground}"></Rectangle>
Sizing (Star Values):
Note: that the Grid will dynamically fill the available area as your code stands and you will only need to apply star values if you define a fixed height and width to the Grid and want proportional allocation of remaining space.
In other words... with regards to achieving "star value":
What I'm hoping to do is provide a
star value for the height and width of
the column and row definitions.
Why not just enter the value like so to your definitions?:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Name="rdRank" Height="500" />
<RowDefinition Name="rdRank1" Height="60*" />
<RowDefinition Name="rdRank2" Style="40*" />
</Grid.RowDefinitions>
</Grid>
In this example the rowdefinition named "rdRank" would have a fixed height of "500", and the remaining space would be allocated to "rdRank1" which would take up 60% and "rdRank2" 40%.
**Attached Properties: **
In your style:
<Style x:Key="RankStyle">
<Setter Property="Control.Height" Value="0.12" />
</Style>
You are stating any control within the item this style is applied to that has a property called Height should take the value of 0.12. Control.Height will end up filtering down so to speak.
If you are aiming to achieve a height of 0.12* on the Row use:
<Style x:Key="NewRankStyle" TargetType="{x:Type RowDefinition}">
<Setter Property="Height" Value="0.12*" />
</Style>
..
<Grid>
<Grid.RowDefinitions>
<RowDefinition Name="rdRank" Style="{StaticResource FileHeadingStyle}" />
<RowDefinition Name="rdRank1" Style="{StaticResource NewRankStyle}" />
Using a 'TargetType' allows you to target Type specific properties and as a result allows use of Star Values.
Hope this clears up a few concepts for you.
The row or column star sizing only works if you give the grid a width and height. If the grid is auto-sizing based on its content, then star sizing doesn't work.

Resources