ItemsControl with horizontal orientation - c#

Do you know any controls inherited from the ItemsControl that have horizontal orientation of items?

Simply change the panel used to host the items:
<ItemsControl ...>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>

While the promoted answer is great, here's an alternative if you want the items to stretch.
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Rows="1" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

This is an example of how to do horizontal scrolling within an ItemsControl.
Firstly the main window viewmodel class used to get/set the list of items we wish to display.
MainWindowViewModel.cs
using System.Collections.Generic;
namespace ItemsControl
{
public class Item
{
public Item(string title)
{
Title = title;
}
public string Title { get; set; }
}
public class MainWindowViewModel
{
public MainWindowViewModel()
{
Titles = new List<Item>()
{
new Item("Slide 1"),
new Item("Slide 2"),
new Item("Slide 3"),
new Item("Slide 4"),
new Item("Slide 5"),
new Item("Slide 6"),
new Item("Slide 7"),
new Item("Slide 8"),
};
}
public List<Item> Titles { get; set; }
}
}
The main window xaml for the view:
MainWindow.xaml
<Window x:Class="ItemsControl.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:ItemsControl"
mc:Ignorable="d"
Title="MainWindow" Height="400" Width="400">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid Margin="5">
<ScrollViewer
VerticalScrollBarVisibility="Disabled"
HorizontalScrollBarVisibility="Auto">
<ItemsControl
x:Name="SearchResultList"
ItemsSource="{Binding Titles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
Margin="5"
BorderThickness="1"
BorderBrush="Aqua">
<TextBlock
Text="{Binding Title}"
HorizontalAlignment="Center"
VerticalAlignment="Top"
FontSize="12"
TextWrapping="Wrap"
TextAlignment="Center"
FontWeight="DemiBold"
Width="150"
Height="150" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</Window>
Depending on how high/wide your client area is, this will result in this kind of layout, overflow items scrolled horizontally:
More details can be found at this blog link, including an example on how to do the scrolling vertically:
http://www.technical-recipes.com/2017/how-to-orient-wrappanel-items-within-itemscontrol-lists-vertically-and-horizontally/

The top answer is good, but I couldn't get it to work with UserControls. If you need UserControls, this should help.
ItemsControl with Horizontal Usercontrols
My Version:
<Window.Resources>
<DataTemplate x:Key="ItemTemplate2">
<StackPanel>
<uc:MyUserControl MinWidth="20" BorderBrush="Black" BorderThickness="0.1" />
</StackPanel>
</DataTemplate>
<ItemsPanelTemplate x:Key="ItemsPanelTemplate1">
<StackPanel Orientation="Horizontal" Margin="0,0,0,0"/>
</ItemsPanelTemplate>
</Window.Resources>
<StackPanel>
<ItemsControl x:Name="list_MyControls"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="0,8,0,0"
ItemTemplate="{StaticResource ItemTemplate2}"
ItemsPanel="{StaticResource ItemsPanelTemplate1}" />
</StackPanel>
To bind to data, you will need to add an ItemsSource to the ItemsControl in the XAML or code behind. Also note that uc: would be the xmlns:uc="NamespaceOfMyControl" declared at the top of the file.

For a long list with hundreds or even thousands of items, you should better use VirtualizingStackPanel to avoid performance issues.

Related

Bound User-Controls in an ObservableCollection aren't showing up in uniformgrid

So, I'm fairly new to C#/XAML and have been trying to teach myself MVVM by reworking an old project. I ran into a problem where a user-control is supposed to be added to a uniformgrid. The user-control shows up fine if I implement it by itself, but if I add it to a ObservableCollection and then try to bind that to a uniformgrid, the path to the user-control gets displayed rather than the actual UI element. Unfortunately I'm new enough to C# and MVVM that I can't identify what specifically is the problem, which makes it hard to search online for.
<UserControl x:Class="CMS.Views.MonthView"
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:CMS.Views"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ItemsControl ItemsSource="{Binding Dates}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid IsItemsHost="True" Columns="7"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
</UserControl>
MonthView.cs
namespace CMS.Views
{
public partial class MonthView : UserControl
{
public MonthView()
{
InitializeComponent();
MonthViewModel monthViewModelObject = MonthViewModel.GetMonthViewModel();
this.DataContext = monthViewModelObject;
}
}
}
MonthViewModel
namespace CMS.ViewModels
{
class MonthViewModel
{
private readonly ObservableCollection<DayViewModel> _dates = new ObservableCollection<DayViewModel>();
public IReadOnlyCollection<DayViewModel> Dates
{
get { return _dates; }
}
public static MonthViewModel GetMonthViewModel()
{
var month = new MonthViewModel();
month.testdaymodel();
return month;
}
public void testdaymodel()
{
DayViewModel DVM = DayViewModel.GetDayViewModel();
DVM.LoadDate(DateTime.Now);
_dates.Add(DVM);
}
}
}
DayView's XAML which has the DataTemplate
<UserControl x:Class="CMS.Views.DayView"
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:CMS.Views"
mc:Ignorable = "d"
MinWidth="100" MinHeight="100" BorderBrush="LightSlateGray" BorderThickness="0.5,0.5,1.5,1.5">
<UserControl.Resources>
<ResourceDictionary>
</ResourceDictionary>
</UserControl.Resources>
<DataTemplate x:Name ="DayBox">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="21"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border x:Name="DayLabelRowBorder" CornerRadius="2" Grid.Row="0" BorderBrush="{x:Null}" Background="{DynamicResource BlueGradientBrush}">
<Label x:Name="DayLabel" Content="{Binding Path = Info.Day, Mode = OneWay}" FontWeight="Bold" FontFamily="Arial"/>
</Border>
<!--This will be bound to the event schedule for a given day-->
<StackPanel Grid.Row="1" x:Name="DayAppointmentsStack" HorizontalAlignment="Stretch" Background="White" VerticalAlignment="Stretch">
</StackPanel>
</Grid>
</DataTemplate>
</UserControl>
EDIT: The same rule applies whether you're using a simple control like a Label or your own control like DayView.
You need to set the ItemsControl.ItemTemplate that will be bound to each item from your IReadOnlyCollection<DayViewModel>.
Then, make a DataTemplate of your liking. Like this:
<ItemsControl ItemsSource="{Binding Dates}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid IsItemsHost="True" Columns="7"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- This control is automatically bound to each DayViewModel instance -->
<local:DayView />
<!--
<Label Content="{Binding PropertyToDisplay}" ></Label>
-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
You didn't show the DayViewModel class, so you can just change the PropertyToDisplay to the actual property you want your view to display.
EDIT: Making DayView the ItemsControl.Template will automatically bind it to the type of the items in the ItemSource.
That means you can treat DayView like a UserControl with DayViewModel as its DataContext without explicitly setting it.
I am assuming that the actual View of your DayView is the Grid inside the DataTemplate, so I just modified the code as follows:
DayView.xaml
<UserControl x:Class="CMS.Views.DayView"
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:CMS.Views"
mc:Ignorable = "d"
MinWidth="100" MinHeight="100" BorderBrush="LightSlateGray" BorderThickness="0.5,0.5,1.5,1.5">
<UserControl.Resources>
<ResourceDictionary>
</ResourceDictionary>
</UserControl.Resources>
<!-- <DataTemplate x:Name ="DayBox"> -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="21"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Border x:Name="DayLabelRowBorder" CornerRadius="2" Grid.Row="0" BorderBrush="{x:Null}" Background="{DynamicResource BlueGradientBrush}">
<Label x:Name="DayLabel" Content="{Binding Path = Info.Day, Mode = OneWay}" FontWeight="Bold" FontFamily="Arial"/>
</Border>
<!--This will be bound to the event schedule for a given day-->
<StackPanel Grid.Row="1" x:Name="DayAppointmentsStack" HorizontalAlignment="Stretch" Background="White" VerticalAlignment="Stretch">
</StackPanel>
</Grid>
<!-- </DataTemplate> -->
</UserControl>

Create buttons programmatically with unique id

I have created some buttons by first filling a class containing get/set methods and then using that info in XAML.
C#
List<MediaDetail> movies = new List<MediaDetail>();
...
...
MovieListView.ItemsSource = movies;
XAML
<Window.Resources>
<DataTemplate x:Key="ItemTemplate">
<WrapPanel Orientation="Vertical" Width="Auto">
<Button Width="200" Height="300" Click="SelectMovie_Click" Name ="NEED THIS TO BE DYNAMIC">
<Button.Template>
<ControlTemplate>
<Image Source="{Binding image}"/>
</ControlTemplate>
</Button.Template>
</Button>
<TextBlock Text="{Binding title}" HorizontalAlignment="Center"/>
</WrapPanel>
</DataTemplate>
</Window.Resources>
<ListView Name="MovieListView" ItemTemplate="{StaticResource ItemTemplate}" ItemsSource="{Binding Path = movies}" Margin="0,0,0,0" SelectionChanged="MovieListView_SelectionChanged" Grid.Row="1">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="5" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
</ListView>
However the issue is, I need each button to have a unique id. I read elsewhere that this can't be done in XAML but I'm not sure how or where in my C# code to create these buttons.
I think the most flexible solution here would be to define a behavior:
public class UniqueNameBehavior : Behavior<FrameworkElement>
{
protected override void OnAttached()
{
base.OnAttached();
//assign unique name to the associated element, for example:
AssociatedObject.Name = Guid.NewGuid().ToString().Replace("-", null);
}
}
In XAML, attach this behavior to any element:
<Button>
<i:Interaction.Behaviors>
<local:UniqueNameBehavior/>
</i:Interaction.Behaviors>
</Button>
where xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" from System.Windows.Interactivity assembly.

Bind datagrid to an ObservableCollection list

I am new with WPF and I would like to bind from xaml to an ObservableCollection. Currently I am doing like this:
in xaml:
<Grid Grid.Column="1">
<local:ProcessingStep DataContext="{Binding ProcessingSteps[0]}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
<Grid Grid.Column="2">
<local:ProcessingStep DataContext="{Binding ProcessingSteps[1]}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
<Grid Grid.Column="3">
<local:ProcessingStep DataContext="{Binding ProcessingSteps[2]}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Grid>
in cs:
public ObservableCollection<ProcessingStepView> ProcessingSteps
{
get
{
return m_ProcessingSteps ??
(m_ProcessingSteps = new ObservableCollection<ProcessingStepViewl>
{
new ProcessingStepView("Step1"),
new ProcessingStepView("Step2"),
new ProcessingStepView("Step3")
});
}
}
How can I bind the list directly to wpf? e.g: if I have 100 steps, it is not nice to do one by one.
You can use ItemsControl with ItemTemplate and bind your list:
<ItemsControl ItemsSource="{Binding ProcessingSteps}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ProcessingStep />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
ItemsControl is basically just a repeater, without selection, that will repeat ItemTemplate as many times as many items there are in ProcessingSteps and place them in whichever panel you choose. In this case horizontal StackPanel

WPF Empty Row in ItemsControl Binding with ObservableCollection

I have ItemsControl Binding to an ObservableCollection, every thing is ok except when ObservableCollection is empty, the ItemsControl will show one empty row !!
<ItemsControl Visibility="Visible" ItemsSource="{Binding ocItemsinInvoice,Mode=TwoWay}"
x:Name="test" Margin="10,-32,0,207" Width="412" HorizontalAlignment="Left">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" VerticalAlignment="Top" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="btnOpenInvoice" Style="{StaticResource OpenInvoicesButton}"
FontSize="12" Width="300" Height="60" Foreground="#ff252526">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Item.ItemName}"
HorizontalAlignment="Center" VerticalAlignment="Center" />
</StackPanel>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
How can I remove it?
public ObservableCollection<object> bindedObservableCollection
{
get{
ObservableCollection<object> newlist = new ObservableCollection<object>(yourObservableCollection);
return newlist;
}
}
I had this issue when binding to a list that was part of a datagrid, I found casting it to a new list of same type removed the blank editors record that is part of the datagrid.

WPF ItemsControl: Can't seem to get items to display in the view

The code I'm working on is part of a WPF application that should display a set of stacks of playing cards that the user can interact with. So there's three UserControls I'm working with:
MainControl
CardStackControl
CardControl
In MainControl.xaml:
<StackPanel
Grid.Row="0"
Orientation="Horizontal">
<ItemsControl
ItemsSource="{Binding Path=CardStacks}"
>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl>
</StackPanel>
My data templates are defined in a Resources.xaml file:
<DataTemplate x:Key="MainTemplate" DataType="{x:Type ViewModel:MainViewModel}">
<View:MainControl />
</DataTemplate>
<DataTemplate DataType="{x:Type ViewModel:CardStackViewModel}">
<View:CardStackControl />
</DataTemplate>
<DataTemplate x:Key="CardTemplate" DataType="{x:Type ViewModel:CardViewModel}">
<View:CardControl />
</DataTemplate>
CardStackControl.xaml:
<StackPanel Orientation="Vertical">
<TextBlock
Height="30"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Path=CardName}"
/>
<ItemsControl
Height="Auto"
ItemsSource="{Binding Path=Cards}"
ItemTemplate="{StaticResource CardTemplate}"
>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl>
<TextBlock
Height="30"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Path=Count}"
/>
</StackPanel>
CardControl.xaml:
<TextBlock
Name="TitleTextBlock"
Grid.Row="0"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding Path=Name}"
>
MainViewModel:
public ObservableCollection<CardStackViewModel> CardStacks;
// ...
CardStackViewModel:
public class CardStackViewModel
{
private CardStack Model;
public string CardName
{
get
{
return this.Model.CardType.Name;
}
}
public int Count
{
get
{
return this.Model.Cards.Count;
}
}
public ObservableCollection<CardViewModel> Cards { get; private set; }
// ...
CardViewModel:
/// <summary>
/// A reference to the Card type.
/// </summary>
private Card Model;
/// <summary>
/// Retrieves the name of the card.
/// </summary>
public string Name
{
get
{
return this.Model.CardType.Name;
}
}
public string Caption
{
get
{
return this.Model.CardType.Text;
}
}
// ...
In the MainViewModel, CardStackViewModel constructors, both ObservableCollection instances are initialized, populated and filled with some test data.
When I load the application and create the MainViewModel, the UserControls don't show up, and none of the test data is visible. I'm using this tutorial as a guide, so that MainViewModel corresponds to the "Workspaces" ViewModel on the tabbed control in that window. Granted, this example should layout each control horizontally, which is fine for now - I'm still learning WPF.
Obviously, I've left off the code-generated tags in the .xaml files and other scaffolding code (I can amend this to post up that code if it's not clear from these examples). I'm only testing/binding to the string properties on the Model classes for now.
The way you declare the ItemsPanelTemplate is incorrect : you're declaring it as an item of the ItemsControl. You should do it like that :
<ItemsControl
ItemsSource="{Binding Path=CardStacks}"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
The ItemsControl in MainControl.xaml isn't specifying an ItemTemplate. Add:
ItemTemplate="{StaticResource MainTemplate}"
I think you'll also need to propagate the DataContext down on the user controls e.g.
<DataTemplate x:Key="MainTemplate" DataType="{x:Type ViewModel:MainViewModel}">
<View:MainControl DataContext="{Binding}" />
</DataTemplate>
(and similarly for the other user controls in the other data templates).

Categories