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
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>
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.
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
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.
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).