UserControl CollectionViewSource not working - c#

I have a user-control containing this XAML
<UserControl x:Class="QA.JobListControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:QA" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<CollectionViewSource x:Name="itemsSource" IsSourceGrouped="True" />
</UserControl.Resources>
<ListView x:Name="JobListView" Margin="-10,-10,0,0" Padding="120,0,0,60" IsSwipeEnabled="False" ItemsSource="{Binding Source=itemsSource}" SelectionChanged="JobListView_SelectionChanged" SelectionMode="Single">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<Border HorizontalAlignment="Stretch">
<TextBlock Text='{Binding Status}' Margin="10" />
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10">
<TextBlock Text='{Binding TaskName}' />
<TextBlock Text='{Binding DueDate}' />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</UserControl>
And to set the content I am using this C#-code
itemsSource.Source = Tasks.OrderBy(Tsk => Tsk.DueDate).GroupBy(Tsk => Tsk.Status);
It is showing some of the elements (but they are shown as empty elements), and not all are shown
What could be wrong?
If I am using this C#-code it is working (but it is not grouped)
JobListView.ItemsSource = Tasks.OrderBy(Tsk => Tsk.DueDate);
UPDATE
After adding the StaticResource like below, it now shows multiple groups without items
ItemsSource="{Binding Source={StaticResource itemsSource}}"

So I think you are misunderstanding the basics behind the GroupBy method. GroupBy, as opposed to most other Linq extensions, will not return a simple list of objects, instead it will return a list of IGrouping. IGrouping interface exposes a Key property which will hold the value of the grouping discriminator you passed in the GroupBy lambda.
Therefore, to get the list to display the group name, you have to bind the group header template to Key instead of Status.
<TextBlock Text='{Binding Key}' Margin="10" />
Also if reference your CollectionViewSource as a resource, you need to define a resource key to reference it later in your XAML as a StaticResource.
<CollectionViewSource x:Name="itemsSource" x:Key="groupedTasks" IsSourceGrouped="True" />
And in the list view.
<ListView x:Name="JobListView" ItemsSource="{Binding Source={StaticResource groupedTasks}}">
This way I got your example to work as expected.
As an additional read I dearly recommend you to read this article by Sergei Barskiy which demonstrates how to use grouping in XAML lists and also provides a GroupedData class that in my opinion is much better than the default IGrouping object to expose your data and consume it in the UI.

Related

ComboBox displays a list of string wrong

In XAML, I bound a ComboBox to a List of strings called Tags, which I have in my static class called Settings.
Here's XAML:
<Window x:Class="CSV_To_Tags_App.Window2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:CSV_To_Tags_App"
Title="Window2" Height="435" Width="566">
<Grid>
<StackPanel Orientation="Horizontal" DataContext="x:Static loc:Settings">
<ItemsControl ItemsSource="{x:Static loc:Settings.Tags}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</Window>
An here's my Settings class:
public static class Settings
{
public static List<string> Tags = new List<string>() { "Header1", "Header2", "Header3", "Header4" };
}
So, I'd like to get a combobox which would display a list of Tags.
I'm using DataTemplate, because later I'll have to display a bigger list of objects and each of them will have ComboBox displayed next to it.
Instead I'm getting this:
I get four ComboBoxes and each of them contains letters of the tags I put in my list. So the first ComboBox has letters: H-e-a-d-d-e-r-1, the second has H-e-a-d-d-e-r-2, and so on.
I'd rather want to get one ComboBox containing all four tags.
How can I achieve that?
You don't need to use a StackPanel for this purpose. You just need one ComboBox and set it's ItemsSource. Like this:
<ComboBox ItemsSource="{x:Static loc:Settings.Tags}" VerticalAlignment="Top"/>
And if you want to use a DataTemplate you can use it like this:
<ComboBox ItemsSource="{x:Static loc:Settings.Tags}" VerticalAlignment="Top">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
You do not need an Items Control for this to work properly. Just a combobox is enough inside the StackPanel to work. Check below code :
<Grid>
<StackPanel Orientation="Horizontal" DataContext="x:Static loc:Settings">
<ComboBox VerticalAlignment="Top" ItemsSource="{x:Static loc:Settings.Tags}" />
</StackPanel>
</Grid>

Bind an ObservableDictionary in WPF xaml file

I had an ObservableCollection<MyDataType>object named myCollection that I was presenting in a GUI using a <DataTemplate> in my .XAMLfile (See code below).
But now the type of myCollection has changed to a Dictionary<UInt64,MyDataType>.
How would I present it now? Here I found an implementation of an ObservableDictionary<>. But I don't know how I would bind this to my GUI?
Thank you for any assistance!
<Page x:Class="Project.ThisPage"
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-ThisPage.ProjectSpace"
mc:Ignorable="d"
d:DesignHeight="480" d:DesignWidth="640"
Title="ThisPage"
Name="thisPage">
<Page.Resources>
<DataTemplate x:Key="MyDataTemplate" DataType="{x:Type local:MyDataType}">
<Label Content="{Binding Name}"/>
<Label Content="{Binding Id}"/>
</DataTemplate>
</Page.Resources>
<Grid>
<ScrollViewer>
<StackPanel>
<ItemsControl ItemsSource="{Binding ElementName=thisPage,Path=myCollection}" ItemTemplate="{StaticResource MyDataTemplate}" />
</StackPanel>
</ScrollViewer>
</Grid></Page>
myCollection.Values will give you MyDataType collection and myCollection.Keys will give you UINT64 collection. So bind to MyDataType collection just update binding like:
ItemsControl ItemsSource="{Binding ElementName=thisPage,Path=myCollection.Values}" ItemTemplate="{StaticResource MyDataTemplate}" />

Why would you use DataTemplate in a ListView?

could someone please explain to me what is the difference if I use
DataTemplate inside the ListView in XAML?
I have used ListView to display the content from my ObservableCollection without using DataTemplate and with DataTemplate it seems to look exactly the same?
Why then would I want to use Data Template? I would like to see a simple explanation/example.
DataTemplate is used to show data in ways beyond a simple text block. Sure doing this:
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
Doesn't buy you much. But it allows you to do stuff like this:
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding ImagePath}"/>
<TextBlock Text="{Caption}"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
Which is pretty cool! You can put anything inside the template, so it makes ItemsControl (and its derivatives) some of the most powerful classes in WPF.
Please take a look at #HighCore's answer here WPF MVVM Why use ContentControl + DataTemplate Views rather than straight XAML Window Views?.
<Window x:Class="MyViews.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml
xmlns:viewmodel="clr-namespace:Project.ViewModel"
DataContext="{Binding Main, Source={StaticResource Locator}}"
Title="{Binding Title, Mode=TwoWay}" Height="{Binding Height, Mode=TwoWay}" Width="{Binding Width, Mode=TwoWay}" Left="{Binding Left, Mode=TwoWay}" Top="{Binding Top, Mode=TwoWay}" WindowStartupLocation="CenterScreen">
<Window.Resources>
<HierarchicalDataTemplate DataType="{x:Type viewmodel:OtherViewModel1}">
<ContentPresenter Content="{Binding Path=Text}" />
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type viewmodel:OtherViewModel2}">
<view:ConnectivityLED />
</DataTemplate>
</Window.Resources>
So you can see Mainwindow is talking to more than 1 viewmodel, Main is its own view model which is its datacontext and it also has reference to otherviewmodel1 and otherviewmodel2. Hope this helps, Cheers!

How do I add items dynamically to a WrapPanel?

I have the following XAML:
<Window x:Class="ImageComparing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Height="350" Width="525" xmlns:my="clr-namespace:ImageComparing" Title="Image comparing">
<DockPanel>
<ToolBar Name="toolbar1" DockPanel.Dock="Top" Height="41" Background="#FFA5D95A">
/*other content*/
</ToolBar>
<WrapPanel Name="wrapPanel1" >
/*other content*/
<Label Content="Label" Height="28" Name="label1" />
</WrapPanel>
</DockPanel>
</Window>
I want to add content to wrapPanel1 - I tried the following code:
if (info.Attributes == FileAttributes.Directory)
wrapPanel1.Children.Add(new FolderEntry(info.Name));
else
wrapPanel1.Children.Add(new FileEntry(info.Name));
For some reason, the items don't show up. How can I fix that problem?
You should to use some ItemsControl, then add/remove the items from the item source property. You can use the ItemsPanel property or changing the template. For instance, using a ListBox and setting the Template property:
<ListBox Grid.Row="2" ItemsSource="{Binding Items}">
<ListBox.Template>
<ControlTemplate>
<WrapPanel IsItemsHost="True"/>
</ControlTemplate>
</ListBox.Template>
</ListBox>
Now when you add or remove items from the Items property in your ViewModel/DataContext the item will be showed using the WrapPanel, and not the StackPanel that is the ListBox's default.
Hope this helps...

WPF Item Templates - TabControl

I am writing an application in which I utilize a tab control which will start with one tab open but allows the user to open multiple other tabs.
Each tab that is openned should have a treeview inside which I fill using databinding when the user loads a file.
I am new to WPF but I feel as if there is a way in which I can create a template containing each of the elements the TabItems should contain. How can I do this using templates? Right now my WPF for the tab items is the following
<TabItem Header="Survey 1">
<TreeView Height="461" Name="treeView1" VerticalAlignment="Top"
Width="625" Margin="0,0,6,0" ItemTemplateSelector="{StaticResource TreeviewDataSelector}" />
</TabItem>
I think you want something like this:
<Window x:Class="TestWpfApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<DataTemplate x:Key="TabItemTemplate">
<TreeView Height="461" Name="treeView1" VerticalAlignment="Top"
Width="625" Margin="0,0,6,0" ItemTemplateSelector="{StaticResource TreeviewDataSelector}" />
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl ItemsSource="{Binding ListThatPowersTheTabs}"
ItemTemplate="{StaticResource TabItemTemplate}">
</TabControl>
</Grid>
You basically create re-usable templates as static resources which you refer to by their key name.
Usually in this sort of situation I bind my TabControl.ItemsSource to a ObservableCollect<ViewModelBase> OpenTabs, so my ViewModel is in charge of adding/removing new tabs as needed.
Then if you want something in every Tab, then overwrite the TabControl.ItemTemplate and use the following line to specify where to display the currently selected TabItem
<ContentControl Content="{Binding }" />
If you don't need to setup something in every single tab, you don't need to overwrite the TabControl.ItemTemplate - it will default to displaying the currently selected ViewModelBase in the Tab.
And I use DataTemplates to specify which View to use
<DataTemplate TargetType="{x:Type local:TabAViewModel}">
<local:TabAView />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TabBViewModel}">
<local:TabBView />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TabCViewModel}">
<local:TabCView />
</DataTemplate>

Categories