Items in ItemControls size automatically - wpf

I have the next problem. I have a ItemsControl defined with xaml.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Button Grid.Row="0" ></Button>
<ItemsControl Grid.Row="1" x:Name="ItemsControl"></ItemsControl>
</Grid>
In the codebehind I have this ItemsControl be filled with buttons.
public Window1()
{
InitializeComponent();
var list = new List<Button>();
list.Add(new Button() { Name = "btn1", Visibility = Visibility.Visible});
list.Add(new Button() { Name = "btn2", Visibility = Visibility.Collapsed});
list.Add(new Button() { Name = "btn3", Visibility = Visibility.Collapsed});
ItemsControl.ItemsSource = list;
}
Now when the buttons are rendered the height is very small. I want the buttons to be rendered at the max height of the ItemsControl. Is there a way?

try this answer out, I had a go and it worked nicely :D
Stretching controls to fill ItemsControl
Don't forget <RowDefinition Height="*" />
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" ></Button>
<ItemsControl Grid.Row="1" x:Name="ItemsControl">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>

Related

WPF - Set initial size of DataGrid within ItemsControl

I Have multiple DataTable's shown via DataGrid:
<ScrollViewer VerticalScrollBarVisibility="Visible">
<ItemsControl ItemsSource="{Binding Path = Data}" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Height="auto" >
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<DataGrid Height="auto" VerticalAlignment="Stretch" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" ItemsSource="{Binding}">
</DataGrid>
<GridSplitter Grid.Row="0" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
Is there an option to limit the initial size to parent's height, or a part of him (for example 50%), and make it resizable (height) by user ?
You are binding Data property to your ItemsControl. The same can go to the Height. Create public property and bind it to Height in view.
<RowDefinition Height="{Binding MyHeightProperty}" />

Setting columnDefinition and RowDefinition from style template?

Is it possible to add the follow code:
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
into a Style / Setter template for a Grid? And then automatically setup the RowDefinitions for a Grid like this?
<Grid style="{StaticResource MyRowDefs}">
<Button Grid.Row="0" Content="Button1/>
<TextBox Grid.Row="1"/>
<Button Grid.Row="2" Content="Button2/>
</Grid>
Do this solve your issue: How to create reusable WPF grid layout
For your example, you could do something like:
<Window x:Class="ReUseGridTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ReUseGridTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="GridItemsStyle" TargetType="ItemsControl">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<ItemsControl Style="{StaticResource GridItemsStyle}">
<Button Grid.Row="0" Content="Button1"/>
<TextBox Grid.Row="1" Text="Test"/>
<Button Grid.Row="2" Content="Button2"/>
</ItemsControl>
</Window>
You can't set the RowDefinitions property in a Style because it's not a dependency property but you can create a custom Grid type and use this one:
public class CustomGrid : Grid
{
public CustomGrid()
{
RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) };
RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
}
}
Usage:
<local:CustomGrid>
<Button Grid.Row="0" Content="Button1/>
<TextBox Grid.Row="1"/>
<Button Grid.Row="2" Content="Button2/>
</local:CustomGrid>

How to make GridView wrap its contents in XAML?

As the title of the post, I want to make a GridView which can wrap all of it's contents inside, it means if I have 3 items, each of them has 50px of height, so how to make a GridView contains these item that has 50*3=150px of height.
How to do that, please help me!
P/s: I want to do it by configuring XAML code, not in C# code, thanks!
I found out the answer! Just set the height of the GridView to Auto by make it inside a Grid row.
<Grid Grid.Row="0"
Margin="12,24">
<Grid.RowDefinitions>
<RowDefinition Height="64" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Opacity=".8"
Background="White"
Height="64">
<Grid Margin="8">
<TextBlock Text="Students"
VerticalAlignment="Center"
FontSize="24" />
</Grid>
<Grid Height="2"
VerticalAlignment="Bottom"
Background="#B0BEC5" />
</Grid>
<GridView Background="White"
Name="grvMatchGA"
Grid.Row="1"
ItemTemplate="{ThemeResource groupItemTemplate}"
Opacity=".8"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
VerticalAlignment="Stretch">
</GridView>
</Grid>
If you don't provide height for gridview but it's children, it automatically takes the height equivalent to sum of children's height.
For example, you can do something like this-
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="0"/>
<TextBox Grid.Row="1"/>
<Button Grid.Row="2"/>
</Grid>

Create multiple visuals per generated item

Here's the plan. I have an ItemsControl that will have a two columns Grid as its main panel:
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
For each item in the underlying VM, I need to generate a Label that will go into first column, and a Rectangle that will go into the second one. How can I achieve this?
<ItemsControl.ItemTemplate>
<DataTemplate DataType="local:TimeSlotVM">
???
</DataTemplate>
</ItemsControl.ItemTemplate>
As you want to use Grid.Row attached property, then assign this attached property to the parent ContentPresenter by the GridHelper.
And the visual tree will be like that:
Grid
-ContentPresenter Grid.Row="1"
-Gird
-ContentPresenter Grid.Row="0"
-Gird
-ContentPresenter Grid.Row="2"
-Gird
...
GridHelper.cs
public class GridHelper
{
public static readonly DependencyProperty GridRowProperty =
DependencyProperty.RegisterAttached("GridRow", typeof(bool), typeof(GridHelper),
new PropertyMetadata(false, new PropertyChangedCallback(OnGridRowPropertyChanged)));
public static bool GetGridRow(DependencyObject d)
{
return (bool)d.GetValue(GridRowProperty);
}
public static void SetGridRow(DependencyObject d, bool value)
{
d.SetValue(GridRowProperty, value);
}
private static void OnGridRowPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
Grid grid = d as Grid;
bool gridRow = (bool)e.NewValue;
if (gridRow)
{
// construct the required row definitions
grid.LayoutUpdated += (s, e2) =>
{
// iterate over any new content presenters (i.e. instances of our DataTemplate)
// that have been added to the grid
var presenters = grid.Children.OfType<ContentPresenter>().ToList();
foreach (var presenter in presenters)
{
// the child of each DataTemplate
var itemGrid = VisualTreeHelper.GetChild(presenter, 0) as Grid;
if (itemGrid != null)
{
Grid.SetRow(presenter, Grid.GetRow(itemGrid));
if (Grid.GetRow(itemGrid) > grid.RowDefinitions.Count - 1)
{
presenter.Visibility = Visibility.Collapsed;
}
}
}
};
}
}
}
XAML
<ItemsControl ItemsSource="{Binding ItemList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid Name="outer" local:GridHelper.GridRow="True">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
...
</Grid.RowDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Grid.Row="{Binding Row}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Field}" />
<Rectangle Grid.Row="{Binding Row}" Grid.Column="1" Fill="Blue" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
What about using a UniformGrid and a Collection of 24 Empty TimeSlotVM's and then insert your TimeSlotVM at the desired postion in the Collection like this :
in your VM :
TimeSlotCollection=new ObservableCollection<TimeSlotVM>(new TimeSlotVM[24]);
TimeSlotCollection.Insert(10,new TimeSlotVM());
TimeSlotCollection.Insert(5, new TimeSlotVM());
and in your View :
<Grid>
<Grid.Resources>
<DataTemplate DataType="{x:Type local:TimeSlotVM}">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding BindingField}" />
<Rectangle Grid.Column="1" Fill="Blue" />
</Grid>
</DataTemplate>
</Grid.Resources>
<ItemsControl ItemsSource="{Binding TimeSlotCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="24"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>

How to crop the elements not fit to a WrapPanel

The example of the WrapPanel.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ItemsControl Grid.Row="1" Style="{StaticResource DestinationItemsControlStyle}"
DataContext="{StaticResource ViewModelKey}"
ItemsSource="{Binding Stations}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Margin="5" Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Style="{DynamicResource DestinationButtonStyle}">
<TextBlock Text="{Binding FullName}" Style="{StaticResource DestinationTextBlockStyle}"
TextTrimming="CharacterEllipsis" />
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
If there are to many elements then all these "excessive" elements become rendered in half of their's real size. Is there a way not to render such the elements?
Am I forced to use something like VirtualizedWrapPanel?
I also want to notice that I can't use a scroll bar. All the "excessive" elements should be rendered on the next page which can be visited by the user's click on the button "Next".
This XAML:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="1024" Width="1280">
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ItemsControl Name="ItemsControl1" Grid.Row="1"
ItemsSource="{Binding Stations}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Margin="5" Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="20" MinHeight="50" MinWidth="400">
<TextBlock Text="{Binding FullName}"
TextTrimming="CharacterEllipsis" />
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
and the addition of 70 items in the Stations collection at startup, produces this result:
What does your DestinationItemsControlStyle look like?
For reference, this is the code-behind that runs on Startup:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
ViewModelKey vmk = new ViewModelKey();
ItemsControl1.DataContext = vmk;
}
}
and in the ViewModelKey class:
public class ViewModelKey : INotifyPropertyChanged
{
public ObservableCollection<station> Stations { get; set; }
public ViewModelKey()
{
Stations = new ObservableCollection<station>();
for (int i = 1; i < 70; i++)
{
Stations.Add(new station("This is station " + i.ToString()));
}
}
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}

Resources