How to group items in ListView solely in xaml - c#

To split into group i have the following code in xaml:
<ListView x:Name="lvPallets" ItemsSource="{Binding Entries}">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<Image Source="{Binding Image}"/>
<TextBlock Text="{Binding Name}" Margin="5,5,5,5" VerticalAlignment="Center" />
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock FontWeight="Bold" FontSize="14" Text="{Binding Name}"/>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
To actually split into groups i added x:Name attribute and some code in code behind file:
public SimulationWindow()
{
InitializeComponent();
_viewModel = new SimulationWindowViewModel();
DataContext = _viewModel;
lvPallets.Loaded += foo;
}
void foo(object sender, EventArgs e)
{
CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(lvPallets.ItemsSource);
PropertyGroupDescription groupDescription = new PropertyGroupDescription("Group");
view.GroupDescriptions.Add(groupDescription);
}
I would like to remove x:Name attribute and function foo. How can i do it?

It is possible to define a CollectionViewSource in the XAML, simply define it in the Resources of the parent and use it via x:Key.
<!-- Define CollectionViewSource -->
<Parent.Resources>
<CollectionViewSource x:Key="GroupCVS" Source="{Binding Entries}">
<!-- Group by the Property 'Group' -->
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Group" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
</Parent.Resources>
<!-- use the CollectionViewSource -->
<ListView ItemsSource="{Binding Source={StaticResource GroupCVS}}">
Note: I haven't found a way to declare the CollectionViewSource directly in the ListView due it must be known before applied (-> StaticResource).

Related

Binding Nested Observable Collection in xaml using c# WPF

I am having a hard time trying to figure out how to bind in xaml a nested Observable Collection. The PLC class contains Tags. This might be familiar if you work in Automation. I have marked the areas of code I am having trouble with by saying "!Can't Figure this out". I am new to xaml and trying to do the binding in the xaml. If it can't be done in the xaml, a code behind solution would be helpful.
PLC Class
public class PLC
{
public string Name { get; set; }
public ObservableCollection<Tag> Tags { get; set; }
public PLC(string name)
{
Name = name;
Tags = new ObservableCollection<Tag>();
}
public override string ToString()
{
return Name;
}
}
Tag Class The PLC's tags when you click on a PLC the ListView to the right will get the tags associated with that PLC.
public class Tag
{
public Tag(string name, int value)
{
Name = name;
Value = value;
}
public string Name { get; set; }
public int Value { get; set; }
}
xaml - note this is a user control binded to the parent's viewmodel.
<UserControl x:Class="Test.UserControls.RuntimeControl"
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"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<TextBlock Foreground="Red" Margin="10,0,0,0" >Runtime</TextBlock>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ListView x:Name="PLCLV" Grid.Column="0" Margin="10" FontSize="25" SelectionMode="Single" ScrollViewer.VerticalScrollBarVisibility="Visible"
BorderThickness="0" ItemsSource="{Binding PLCs}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView Grid.Column="1" Margin="10" FontSize="25" AlternationCount="2" ScrollViewer.VerticalScrollBarVisibility="Visible"
BorderThickness="0 " ItemsSource=***!Can't Figure this out!***>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Focusable" Value="false"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text = "Name: "/>
<Run Text =***!Can't Figure this out!***
<Run Text ="Value: "/>
<Run Text =***!Can't Figure this out!***
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Grid>
</UserControl>
FIX in the UserControl xaml Answer Marked as accepted
<ListView Grid.Column="1" Margin="10" FontSize="25"
DataContext="{Binding SelectedItem, ElementName=PLCLV}"
ItemsSource="{Binding Tags}">
First: you set the ItemsSource by list of PLC on "PLCLV" named ListView , so the type of object in SelectedItem of this ListView must be PLC type (or null), you can make this SelectedItem be the source of Tag list.
<ListView Grid.Column="1" Margin="10" FontSize="25" AlternationCount="2" ScrollViewer.VerticalScrollBarVisibility="Visible"
BorderThickness="0 " ItemsSource="{Binding SelectedItem.Tags, ElementName=PLCLV}">
<!-- Or SelectedItem.(local:PLC.Tags), the "(local:PLC.Tags)" means "the speicified property of specified type on unspecified type boxed property" -->
<!-- you can use Binding.ElementName to find the sepcified "Name/x:Name" named element in visual tree to be the Binding source -->
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Focusable" Value="false"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<!-- "x:Type" is a speicial markup for return a specific "Type" object -->
<!-- Set DataType of DataTemplate can help XAML editor show IntelliSense to help you -->
<!-- "local:" is a prefix of namespace for the type in xml, defined by "xmlns:", here make the editor know the this DataTemplate is apply on the "Tag" type -->
<DataTemplate DataType="{x:Type local:Tag}">
<TextBlock>
<Run Text="Name: "/>
<Run Text="{Binding Name}" />
<Run Text="Value: "/>
<Run Text="{Binding Value}" />
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Or, use DataContext and ItemsSource:
<ListView Grid.Column="1" Margin="10" FontSize="25" AlternationCount="2" ScrollViewer.VerticalScrollBarVisibility="Visible"
BorderThickness="0"
DataContext="{Binding SelectedItem, ElementName=PLCLV}"
ItemsSource="{Binding Tags}">
<!-- Or ItemsSource="{Binding Path=(local:PLC.Tags)}" -->
...
Assuming the DataContext of the UserControl is set correctly (i.e. the binding of PLCs is correct)
Add this property in code behind under PLCs collection property (probably you will need it later in code behind)
private PLC _selectedPlc;
public PLC SelectedPlc
{
get => _selectedPlc;
set
{
_selectedPlc = value;
OnPropertyChanged(nameof(SelectedPlc)); // this will update the second ListView
}
}
public ObservableCollection PLCs {set; get;} // you have this already
The first ListView can see all PLCs.. Let's bind the DataContext of the second ListView with SelectedPlc property, this way, the second ListView will only see one PLC.
<ListView x:Name="PLCLV" Grid.Column="0" Margin="10" FontSize="25" SelectionMode="Single" ScrollViewer.VerticalScrollBarVisibility="Visible"
BorderThickness="0" ItemsSource="{Binding PLCs}"
SelectedItem="{Binding SelectedPlc}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Foreground="{Binding ForegroundColor}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ListView Grid.Column="1" Margin="10" FontSize="25" AlternationCount="2" ScrollViewer.VerticalScrollBarVisibility="Visible"
BorderThickness="0"
DataContext="{Binding SelectedPlc}"
ItemsSource="{Binding Tags}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="Focusable" Value="false"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock>
<Run Text= "Name: "/>
<Run Text="{Binding Name}"
<Run Text="Value: "/>
<Run Text="{Binding Value}"
</TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Do not forget to fill PLCs (and Tags inside each PLC) collections with some data!
Note: there is ForegroundColor property missing in your PLC class!

C# TabControl binding with linq groupby statement in WPF

I am new to WPF and I am trying to bind a List of grouped object to a tabControl and I just manage to get halfway there
Here is my C# Code In the constructor :
IEnumerable<Validation> validations = ReflectiveEnumerator.GetEnumerableOfType<Validation>().Where(validation => validation.IsActive);
tabControl.ItemsSource = validations.GroupBy(validation => validation.TabName);
and my xaml code is :
<TabControl x:Name="tabControl" Margin="10,10,10,37" ItemsSource="{Binding Groups}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding ValidationName}"/>
</WrapPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
and so I get a tab by group which is what I was expecting but within my wrap panel I only have 1 ValidationName per Tab, I was expecting multiple ones. what is missing to have the content panel Iterate through my group.
When I don't group by I have multiple tabItem with the same Name and that's not what I'm looking for.
My Validation class looks like :
public class Validation
{
public string ValidationName {get; private set;}
public string TabName{get; private set;}
public bool IsActive{get; private set;}
}
You could use an ItemsControl in the ContentTemplate of the TabControl:
<TabControl x:Name="tabControl" Margin="10,10,10,37" ItemsSource="{Binding Groups}">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ValidationName}" Margin="10"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
The above sample markup will display a TextBlock per item in each group in a WrapPanel inside each TabItem.

How to bind a list of list to a ListView control

I have The following structure
QPage class Object contains a List<List<QLine>>
QLine object contains List<Qword>
every list of words constructs a line and every list of lines consists a group(paragraph) and every list of paragraphs consists a page.
I want to bind the page to structure like this in XAML
<ListView>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock>
</TextBlock>
</StackPanel>
</StackPanel>
</ListView>
where each item of the ListView is a paragraph(List<QLine>) and each vertical stack panel holds an item of the List<QLine> and each item of the horizontal stack panel holds an item of the List<Qword> and the texblock is bound to Qword.text property. I have no idea how to do such binding from the XAML code.
Hopefully I did not miss some list but this should work. Basically it's a ListBox that hosts List<List<QLine>> (called it QPageList). Then you have ItemsControl that hosts each List<QLine> in vertical panel and finally there is another ItemsControl that hosts List<Qword> from QLine (called it QwordList) where each QWord is displayed as TextBlock on horizontal StackPanel
<!-- ItemsSource: List<List<QLine>>, Item: List<QLine> -->
<ListBox ItemsSource="{Binding QPageList}">
<ListBox.ItemTemplate>
<DataTemplate>
<!-- ItemsSource: List<QLine>, Item: QLine -->
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- ItemsSource: QLine.List<QWord>, Item: QWord -->
<ItemsControl ItemsSource="{Binding QwordList}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Item: QWord -->
<TextBlock Text="{Binding text}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
What you are looking for is ListView.ItemTemplate. Basically, you need to provide your list with a way to understand the nested data structure of your rows.
Here is a good tutorial to get you started on ItemTemplates.
Once your list has an item template then you just bind the ListView directly to your data source and that's it.
Hopefuly this will prove helpful. Taken from here
Authors (list)
- - Name
- - Books (list)
| - - Title
| - - Contents
Sample code :
<Window.Resources>
<DataTemplate x:Key="ItemTemplate1">
<StackPanel>
<TextBlock Text="{Binding Name}"/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="ItemTemplate2">
<StackPanel>
<TextBlock Text="{Binding Title}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid x:Name="LayoutRoot" DataContext="{Binding Source={StaticResource AuthorDataSource}}">
<ListBox x:Name="AuthorList" ItemTemplate="{DynamicResource ItemTemplate1}" ItemsSource="{Binding Authors }" >
<ListBox x:Name="BookList" DataContext="{Binding SelectedItem, ElementName=AuthorList}" ItemsSource="{Binding Books }" ItemTemplate="{DynamicResource ItemTemplate2}" />
<TextBlock x:Name="BookContent" DataContext="{Binding SelectedItem, ElementName=BookList}" Text="{Binding Contents }" />
</Grid>
Forward to what Alex is saying, it'd be a good idea to Encapsulate (If that's the correct word here) the object within classes, i.e.:
public class Page
{
public List<Paragraph> Paragraphs { get; set; }
}
public class Paragraph
{
public List<QWord> Sentences { get; set; }
}
public class Sentence
{
public List<QWord> Words { get; set; }
}
That'd help when you bind data in your XAML, i.e. if you look at the tutorial which Alex provided:
<TextBlock Text="Name: " />
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
<TextBlock Text="{Binding Age}" FontWeight="Bold" />
<TextBlock Text=" (" />
<TextBlock Text="{Binding Mail}" TextDecorations="Underline" Foreground="Blue" Cursor="Hand" />
<TextBlock Text=")" />
Hope that helps you.

WPF how to bind a tabitem with a list

I have the following problem:
i have some data loaded in my application, that need to be put in a tab control.
The data is in the format:
class objectType1
{
string property_1;
string prorerty_2;
}
class mainObject
{
string mainProperty_1;
string mainProperty_2;
List<objectType1> objectsList;
}
and all the data is loaded in an object of type
List<mainObject> myListofObjects
So far i managed to create the tabitems with respect to myListofObjects item
(ie if the list has 5 objects, 5 tabs are created with the header containing the information mainProperty_1 and mainProperty_2)
now i need to add the data contained in each objectsList into their respective tab...
the mainProperty_1 represents an image, which must be loaded...
<TabControl x:Name="_DataList" Margin="10">
<!-- Header -->
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Height="18" Source="{Binding mainProperty_1/>
<TextBlock Text="{Binding mainProperty_2}" Margin="2,0,0,0" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<!-- Content -->
<TabControl.ContentTemplate>
<DataTemplate x:Name="objectDataTemplate">
<Grid Margin="5">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding property_1}" ToolTip="{Binding property_2}" IsHitTestVisible="false" Stretch="Uniform"/>
</StackPanel>
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
I pass the data to the tabcontrol in code behind with
_DataList.ItemsSource = myListofObjects;
this is not working for the content...
the header loads just fine (both image and the text...)
anyone has any idea how to do it?
Thanks a lot!
your ContentTemplate seems to be wrong:
<TabControl x:Name="_DataList" Margin="10">
<!-- Header -->
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Height="18" Source="{Binding mainProperty_1/>
<TextBlock Text="{Binding mainProperty_2}" Margin="2,0,0,0" />
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<!-- Content -->
<TabControl.ContentTemplate><!-- its bound to one mainObject -->
<DataTemplate x:Name="objectDataTemplate">
<!-- if you wanna bind to something from your objectsList you have to threat it like a list, cause it is :) -->
<ListBox Itemssource={Binding objectsList}>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type objectType1}">
<StackPanel Orientation="Horizontal">
<Image Source="{Binding property_1}" ToolTip="{Binding property_2}" IsHitTestVisible="false" Stretch="Uniform"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
1) Have you tried to connect to list via binding?
var b = new Binding("myListofObjects");
BindingOperations.SetBinding(_DataList, ItemsControl.ItemsSourceProperty, b);
2) If you're using binding, have you set appropriate DataContext?

Setting to DataContext not reflecting in Listbox

I have a ViewModel and i am setting an instance of it to DataContext.In xaml i have binded the Listbox itemsource to an observable collection in ViewModel.But when i run the program nothing appears in listbox.What may be the Reason.
My ViewModel class:
public class ViewModel
{
public ObservableCollection<Data> _collectionData = new ObservableCollection<Data>();
-----
-----
}
Xaml:
ListBox Name="myListBox" Margin="8,113,8,8" ItemsSource="{Binding _collectionData}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" >
<Border BorderThickness="3" BorderBrush="#A5FFFFFF" Width="80" Margin="0,20,0,20" Height="60">
<Image Source="{Binding ImageUrl, Mode=OneWay}" VerticalAlignment="Stretch" Margin="0,0,0,0" Width="80" Height="60" Stretch="Fill" />
</Border>
<TextBlock TextWrapping="Wrap" Text="{Binding Title}" FontSize="40" FontWeight="Normal" VerticalAlignment="Center" Margin="30,0,0,0" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Constructor of main page:
ViewModel vm = new ViewModel();
this.DataContext = vm;
But when i give myListBox.ItemsSource = vm._collectionSplashData; it works .what may be the reason?
Make property instead of field
see Binding Sources Overview
Applying both ItemsSource and Datacontext not needed.

Categories