I have following UserControl :
<Grid x:Name="LayoutRoot">
<CheckBox x:Name="seat" Margin="2,2,2,2.901" BorderBrush="#FF003FFF" Content="{Binding Path=TypeSSeat, ElementName=UserControl}" />
</Grid>
With This CodeBehind :
[DefaultValue(Nothing)]
public enum TypeSeat
{
Nothing,FirstClass, businessclass , economyclass ,NoSeat
}
public partial class UCSeat : UserControl
{
public TypeSeat TypeSSeat
{
get
{
return (TypeSeat)GetValue(ItemTextProperty);
}
set
{
SetValue(ItemTextProperty, value);
}
}
public static readonly DependencyProperty ItemTextProperty =
DependencyProperty.Register("TypeSSeat", typeof(TypeSeat), typeof(UCSeat), new PropertyMetadata(default(TypeSeat)));
i want to fill itemscontrol with this usercontrol but after run i have just one checkBox.
this is my windows code :
<ItemsControl Name="icName" Height="366" VerticalAlignment="Top" ItemsSource="{Binding Path=UCSeat}" >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0,0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<local:UCSeat HorizontalAlignment="Left" Width="156.8" TypeSSeat="{Binding seat1}" ToolTip="1"/>
<local:UCSeat HorizontalAlignment="Left" Width="156.8" TypeSSeat="{Binding seat2}" ToolTip="2"/>
and with this code behind :
List<SeatList> lst = new List<SeatList>();
lst.Add(new SeatList { seat1 = TypeSeat.FirstClass, seat2 = TypeSeat.FirstClass, seat3 = TypeSeat.NoSeat, seat4 = TypeSeat.FirstClass, seat5 = TypeSeat.FirstClass, seat6 = TypeSeat.Nothing, seat7 = TypeSeat.Nothing, seat8 = TypeSeat.Nothing, seat9 = TypeSeat.Nothing, seat10 = TypeSeat.Nothing, seat11 = TypeSeat.Nothing, seat12 = TypeSeat.Nothing, seat13 = TypeSeat.Nothing, seat14 = TypeSeat.Nothing });
icName.ItemsSource = lst;
Your DataTemplate uses a grid as it's layout control. The two UCSeat user controls are placed in this grid without specifying which column they should be located in.
This means both UCSeat controls are placed on top of each other which might make it look as if only one checkbox is being displayed.
Either change the second UCSeat entry to include the Grid.Column="1" to make the second user control show in the second column
OR use a StackPanel container with Orientation="Horizontal" instead of the Grid container meaning both will layout horizontally automatically
You have all the items there, however they are all stacked on top of each other.
To properly layout items you need to set your ItemsPanelTemplate to whatever container you are placing your items in (such as a Grid), and use the ItemContainerStyle to set any specific properties of your items (such as Grid.Row and Grid.Column
Here's some sample XAML taken from my blog post about the ItemsControl
<ItemsControl ItemsSource="{Binding MyCollection}">
<!-- ItemsPanelTemplate -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- ItemContainerStyle -->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column"
Value="{Binding ColumnIndex}" />
<Setter Property="Grid.Row"
Value="{Binding RowIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
<!-- ItemTemplate -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
This assumes you are binding to a collection of objects that look something like this :
public class MyObjectModel
{
public string Name { get; set; }
public int ColumnIndex{ get; set; }
public int RowIndex{ get; set; }
}
And results in something like this :
I'd highly recommend reading the actual post as well if you're new to using an ItemsControl :)
Related
My code has a RightListBox and a CheckBox (under another ListBox), like this:
I'd like to show the RightListBox items in the CheckBox (i.e., T1, T2, T3), but it actually shows the ViewModel name. I tried in many ways (please see my XAML), but none of them shows the RightListBox items.
Here is the code:
MainWindow.xaml.cs
using ListBoxMoveAll.Model;
using ListBoxMoveAll.ViewModel;
using System.Collections.ObjectModel;
using System.Windows;
namespace ListBoxMoveAll
{
public partial class MainWindow : Window
{
public ObservableCollection<GraphViewModel> RightListBoxItems { get; }
= new ObservableCollection<GraphViewModel>();
public MainWindow()
{
InitializeComponent();
DataContext = this;
RightListBoxItems.Add(new GraphViewModel("T1", new Graph(10)));
RightListBoxItems.Add(new GraphViewModel("T2", new Graph(20)));
RightListBoxItems.Add(new GraphViewModel("T3", new Graph(30)));
}
}
}
MainWindow.xaml
<Window x:Class="ListBoxMoveAll.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:ListBoxMoveAll"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="600">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="5*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox x:Name="RightListBox" Grid.Row="0" Grid.RowSpan="3" Grid.Column="2"
ItemsSource="{Binding RightListBoxItems}" DisplayMemberPath="Text"
SelectionMode="Extended" Margin="0,10"/>
<StackPanel Grid.Column="4" Grid.Row="0" Grid.RowSpan="3" Margin="0,10">
<!--<ListBox ItemsSource="{Binding Items, ElementName=RightListBox}">-->
<!--<ListBox ItemsSource="{Binding Items, ElementName=RightListBox}" DisplayMemberPath="Text">-->
<!--<ListBox ItemsSource="{Binding SelectedItems, ElementName=RightListBox}">-->
<ListBox ItemsSource="{Binding SelectedItems, ElementName=RightListBox}" DisplayMemberPath="Text">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<CheckBox Content="{TemplateBinding Content}" x:Name="checkBox"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</StackPanel>
</Grid>
</Window>
Graph.cs
namespace ListBoxMoveAll.Model
{
public class Graph
{
public Graph(int step) { Step = step; }
public int Step { get; set; }
}
}
GraphViewModel.cs
using ListBoxMoveAll.Model;
namespace ListBoxMoveAll.ViewModel
{
public class GraphViewModel
{
public string Text { get; }
public Graph Graph { get; }
public GraphViewModel(string text, Graph graph) => (Text, Graph) = (text, graph);
}
}
This looks so simple, but I still can't find a solution. So, please help me. Thank you.
Instead of ListBox.ItemContainerStyle you can use ItemTemplate. And then use binding to your Text property (not the Content) in GraphViewModel. Something like that
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Text}" x:Name="checkBox"/>
</DataTemplate>
</ListBox.ItemTemplate>
Another option is to use the same approach with your different list box
<ListBox ItemsSource="{Binding SelectedItems, ElementName=RightListBox}" DisplayMemberPath="Text"/>
You also should bind IsChecked property of your checkbox to a ViewModel property, there is no such property right now
For Content you need to specify which property you want to display for the checkbox.
<CheckBox Content="{Binding Path=Content.Text,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}" x:Name="checkBox"/>
EDIT: Well, looks like the binding to the ChessPiece item was a Resharper issue - I shut it off, and the error went away. Unbelievable.
I've been trying to bind items in a collection to grid location. I've been able to bind a collection and draw the items, but in the XAML, I'm unable to reference the property I need.
As per this example: http://wpf.2000things.com/2011/12/21/455-using-itemcontainerstyle-to-bind-data-elements-in-a-collection-to-a-grid/
The code:
public class ChessPiece
{
public string Text { get; set; }
public int Row { get; set; } // 0..n-1
public int Column { get; set; } // 0..n-1
public ChessPiece(string text, int row, int col)
{
Text = text;
Row = row;
Column = col;
}
}
public class MainViewModel : ViewModelBase
{
public ObservableCollection<ChessPiece> ChessPieces { get; private set; }
/// <summary>
/// Initializes a new instance of the MainViewModel class.
/// </summary>
public MainViewModel()
{
ChessPieces = new ObservableCollection<ChessPiece>();
ChessPieces.Add(new ChessPiece("QR-BLK", 1, 1));
ChessPieces.Add(new ChessPiece("QN-BLK", 1, 2));
ChessPieces.Add(new ChessPiece("QB-BLK", 0, 3));
////if (IsInDesignMode)
////{
//// // Code runs in Blend --> create design time data.
////}
////else
////{
//// // Code runs "for real"
////}
}
}
XAML:
<Window x:Class="MVVMLightTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding Main, Mode=OneWay, Source={StaticResource Locator}}" Height="550" Width="525">
<Grid Margin="0,0,0,0">
<ItemsControl ItemsSource="{Binding ChessPieces}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid ShowGridLines="True">
<Grid.RowDefinitions >
<RowDefinition Height="162.667"></RowDefinition>
<RowDefinition Height="171.333"></RowDefinition>
<RowDefinition Height="165.333"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="162"></ColumnDefinition>
<ColumnDefinition Width="206">/>
<ColumnDefinition Width="263"/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Text}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Row}"/>
<Setter Property="Grid.Column" Value="{Binding Column}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Grid>
</Window>
In the mainviewmodel, I have an observablecollection of chesspieces, which I populate with new chesspieces.
I can bind to the Row property at other levels of the XAML, just not in the Style setter where I'm trying to tie it to a grid location - the collection is accessible to bind to, but not an individual item.
Any help would be most appreciated!
This is nigh-on impossible to do with simple bindings. The container, not the control generated by the template, is what you need to set the Row/Grid on, and getting to it is not fun.
My solution, while ugly was to create my own special Grid, and use ArrangeOverride:
class DynamicGrid : Grid
{
protected override System.Windows.Size ArrangeOverride(System.Windows.Size arrangeSize)
{
UIElementCollection elements = base.InternalChildren;
for (int i = 0; i < elements.Count; i++)
{
elements[i].SetValue(Grid.RowProperty, correctRow);
}
return base.ArrangeOverride(arrangeSize);
}
}
Obviously thats just the structure, you'll have to determine the best way to get the data needed to set the row/column into this object. The good news is, when you set this Grid as the ItemsPanelTemplate, you can do your regular bindings to any DPs you set up on your custom Grid:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:DynamicGrid />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
Of course, getting to the right piece of data will be non-trivial, but compared to setting the container Grid.Row property from inside the template, its much easier.
It is necessary to arrange elements in certain cells, but they all fall into the upper-left cell. Thought does not work due to the dynamic creation of rows and columns, but in static ad, nothing has changed.
Please help.
XAML
<ItemsControl x:Name="ItControl" ItemTemplate="{StaticResource DefaultGridItemTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Grid.Column" Value="{Binding Col}"/>
<Setter Property="Grid.Row" Value="{Binding Row}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
C#
public class FilterItem
{
public int id_node { get; set; }
public int id_h { get; set; }
//[MaxLength(50)]
public string name { get; set; }
public int Col { get; set; }
public int Row { get; set; }
}
Unfortunately that will not work since there is nothing to bind to as the container isn't in XAML. What you have to do is subclass the ItemsControl and override the PrepareContainerForItemOverride function like so:
public class CustomItemsControl : ItemsControl
{
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
FrameworkElement source = element as FrameworkElement;
source.SetBinding(Grid.ColumnProperty, new Binding { Path = new PropertyPath("Col") });
source.SetBinding(Grid.RowProperty, new Binding { Path = new PropertyPath("Row") });
}
}
Then change your XAML to your CustomItemsControl instead of the items control:
<local:CustomItemsControl x:Name="ItControl" ItemTemplate="{StaticResource DefaultGridItemTemplate}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Grid >
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition/>
</Grid.ColumnDefinitions>
</Grid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!--<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Grid.Column" Value="{Binding Col}" />
<Setter Property="Grid.Row" Value="{Binding Row}"/>
</Style>
</ItemsControl.ItemContainerStyle>-->
</local:CustomItemsControl>
try to add the main ItemsControl inside StackPanel with Orientation vertical.
Thanks
I'm trying to find a way to display a horizontal List of items in WPF, the trick is that the Window which contains the List will be displayed on various screen sizes and all the items in the list need to be resized to fill the available space without any use of scroll bars.
I've found that a ViewBox control can be used to achieve the desired affect, but the ViewBox works only if I set <RowDefinition Height="300"/>.This approach doesn't work because if you have a certain number of items in the List they start becoming cut off.
If I remove <RowDefinition Height="300"/> then the first item in the list fills the screen and the rest are cut off
Any suggestions on how I can make all the items in the list resize to fill the available space no matter what the screen resolution is?
XAML:
<Window x:Class="ViewBoxExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" WindowState="Maximized">
<ItemsControl ItemsSource="{Binding Path=Employees}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="300"/>
</Grid.RowDefinitions>
<Viewbox Grid.Row="0" Grid.Column="0">
<TextBlock Text="{Binding Path=Name}" />
</Viewbox>
<Viewbox Grid.Row="0" Grid.Column="1">
<TextBlock Text="{Binding Path=Surname}" />
</Viewbox>
<Viewbox Grid.Row="0" Grid.Column="2">
<TextBlock Text="{Binding Path=Age}" />
</Viewbox>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Window>
C#:
using System.Collections.Generic;
using System.Windows;
namespace ViewBoxExample
{
public partial class MainWindow : Window
{
public List<Employee> _employees = new List<Employee>();
public List<Employee> Employees
{
get { return _employees; }
set { _employees = value; }
}
public MainWindow()
{
InitializeComponent();
Employees = new List<Employee>()
{
new Employee{ Name="Name1",Surname="Surname1",Age=20},
new Employee{ Name="Name2",Surname="Surname2",Age=30},
new Employee{ Name="Name3",Surname="Surname3",Age=40},
new Employee{ Name="Name4",Surname="Surname4",Age=50},
new Employee{ Name="Name5",Surname="Surname5",Age=60},
};
this.DataContext = this;
}
}
public class Employee
{
public string Name { get; set; }
public string Surname { get; set; }
public int Age { get; set; }
}
}
Just put your ItemsControl in ViewBox
<Viewbox>
<ItemsControl ItemsSource="{Binding Path=Employees}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<Viewbox Grid.Row="0" Grid.Column="0">
<TextBlock Text="{Binding Path=Name}" />
</Viewbox>
<Viewbox Grid.Row="0" Grid.Column="1">
<TextBlock Text="{Binding Path=Surname}" />
</Viewbox>
<Viewbox Grid.Row="0" Grid.Column="2">
<TextBlock Text="{Binding Path=Age}" />
</Viewbox>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Viewbox>
EDIT: if i am doing the same type of thing i would use the actual Height and Width approach as explained by Kent Boogaart in this Answer
I have two DataTemplates defined within my XAML, each used for a seperate ItemsControl panel.
The main ItemsControl lists Foo objects stored within an ObservableCollection object.
The Foo object itself has its own set of items stored within as an ObservableCollection object.
I tried to define the XAML in a way that allows for each of the ObservableCollection Foo items to be displayed with its name in a header (The first ItemsControl). From this the list within each Foo item itself should be displayed horizontally (Using the second ItemsControl) with a related field directly below. If enough items are present then they should wrap to the next line where necessary.
This is how the UI currently stands:
This is how I wish the UI to actually appear:
My Markup (Button controls are for another aspect of the UI) :
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ItemsControl x:Name="ContentList" ItemTemplate="{StaticResource GameTemplate}" Grid.Column="0" />
</ScrollViewer>
<StackPanel Grid.Column="1" Background="DarkGray">
<Button Click="OnLoad">_Load</Button>
<Button Click="OnSave">_Save</Button>
<Button Click="OnAdd">_Add</Button>
<Button Click="OnDelete">_Delete</Button>
</StackPanel>
</Grid>
DataTemplate for listing Foo items:
<DataTemplate x:Key="GameTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Content="{Binding Name}" Grid.Row="0" Background="Gray" FontSize="16" />
<ItemsControl x:Name="imageContent"
ItemsSource="{Binding FileList}"
ItemTemplate="{StaticResource GameImagesTemplate}"
Grid.Row="1" />
</Grid>
</DataTemplate>
DataTemplate for listing items within each Foo item:
<DataTemplate x:Key="GameImagesTemplate">
<WrapPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical" >
<Image Source="{Binding FileInfo.FullName}"
Margin="8,8,8,8"
Height="70"
Width="70" />
<Label Content="{Binding Name}" />
</StackPanel>
</WrapPanel>
</DataTemplate>
I'm fairly new to WPF so I have a feeling it's an issue caused by how I am using the controls.
What WPF changes would I need to make in order to generate the UI I would like?
I think its because you are adding each image item to a new WrapPanel in GameImagesTemplate , you should just have to set the ItemsControl ItemsPanelTemplate to WrapPanel in the GameTemplate
Example:
Xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="252.351" Width="403.213" Name="UI" >
<Window.Resources>
<DataTemplate x:Key="GameImagesTemplate" >
<StackPanel>
<Image Source="{Binding FileInfo.FullName}" Margin="8,8,8,8" Height="70" Width="70" />
<Label Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="GameTemplate">
<StackPanel>
<Label Content="{Binding Name}" Grid.Row="0" Background="Gray" FontSize="16" />
<ItemsControl x:Name="imageContent" ItemsSource="{Binding FileList}" ItemTemplate="{StaticResource GameImagesTemplate}" >
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" ScrollViewer.HorizontalScrollBarVisibility="Disabled" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<ItemsControl ItemsSource="{Binding ElementName=UI, Path=FileList}" Grid.Column="0" ItemTemplate="{StaticResource GameTemplate}" />
</ScrollViewer>
</Grid>
</Window>
Code:
public partial class MainWindow : Window
{
private ObservableCollection<Foo> _fileList = new ObservableCollection<Foo>();
public MainWindow()
{
InitializeComponent();
foreach (var item in Directory.GetDirectories(#"C:\StackOverflow"))
{
FileList.Add(new Foo
{
Name = item,
FileList = new ObservableCollection<Bar>(Directory.GetFiles(item).Select(x => new Bar { FileInfo = new FileInfo(x) }))
});
}
}
public ObservableCollection<Foo> FileList
{
get { return _fileList; }
set { _fileList = value; }
}
}
public class Foo
{
public string Name { get; set; }
public ObservableCollection<Bar> FileList { get; set; }
}
public class Bar
{
public FileInfo FileInfo { get; set; }
}
Result