How can I bind and sort a collection - wpf

If I have an unsorted collection, is there an easy way to bind and sort it. I would like to do it in XAML (no Linq, no C#)
If my DataContext has a property, say, MyItems, it is easy to bind against it:
<ListBox ItemsSource={Binding MyItems}/>
However, I'd like to sort it as well. Using the CollectionViewSource should be the solution but it does not work for me:
<ListBox>
<ListBox.ItemsSource>
<Binding>
<Binding.Source>
<CollectionViewSource Source={Binding MyItems}/>
</Binding.Source>
</Binding>
</ListBox.ItemsSource>
</ListBox>
At this point, my ListBox loses its elements.
Am I missing something obvious?

You can define the CollectionViewSource as a resource and provide your desired sorting...
<Window.Resources>
<CollectionViewSource x:Key="cvs" Source="{Binding MyItems}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="MyItemName" Direction="Ascending"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Window.Resources>
<Grid>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}"/>
</Grid>
The scm namespace is xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"

Create a CollectionViewSource in the CodeBehind which reads from MyItems, and bind your ListBox to that
<ListBox ItemsSource="{Binding MyCollectionViewSource"} />

Neither of the other answers actually address sorting. They are both right about a CollectionViewSource, but you can use that to do the sorting, with CollectionViewSource.SortDescription. Taken from here and modified:
<Window.Resources>
<src:MyItems x:Key="MyItems"/>
<CollectionViewSource Source="{StaticResource MyItems}" x:Key="cvs">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="CityName"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Window.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}"
DisplayMemberPath="CityName" Name="lb">
<ListBox.GroupStyle>
<x:Static Member="GroupStyle.Default"/>
</ListBox.GroupStyle>
</ListBox>
In this example,CityName would be the property on each item in MyItems used to do the sorting

Related

CollectionViewSource: TreeView not updating, ViewModel looking good

I'm trying to populate a TreeView in a TestExplorerControl:
I never had to use a CollectionViewSource until now, so I used this tutorial to get a grasp of how to group my ObservableCollection<TestMethod> in XAML, and use that grouping for my tree view - I implemented the data templates in the <UserControl.Resources>, because I want the flexibility to eventually allow the user change the way tests are regrouped:
<CollectionViewSource x:Key="OutcomeGroupViewSource" Source="{Binding Model.Tests}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="Result.Outcome" />
</CollectionViewSource.GroupDescriptions>
</CollectionViewSource>
<DataTemplate x:Key="TestMethodTemplate" DataType="{x:Type local:TestMethod}">
<StackPanel Orientation="Horizontal">
<Image .../>
<TextBlock .../>
</StackPanel>
</DataTemplate>
<HierarchicalDataTemplate x:Key="OutcomeTemplate" DataType="{x:Type CollectionViewGroup}"
ItemsSource="{Binding Items}"
ItemTemplate="{StaticResource TestMethodTemplate}">
<StackPanel Orientation="Horizontal">
<Image ../>
<TextBlock ../>
<TextBlock ../>
</StackPanel>
</HierarchicalDataTemplate>
Then I have this markup for the actual <TreeView>:
<TreeView Grid.Row="2"
ItemsSource="{Binding Source={StaticResource OutcomeGroupViewSource}, Path=View.Groups}"
ItemTemplate="{StaticResource OutcomeTemplate}" />
What's wrong with this markup, for the TreeView to fail updating? The ViewModel clearly has all the data I need to display (the breakpoint that was hit is on the current line, in yellow):
Found it. It's the Path of the ItemsSource binding in the tree view:
ItemsSource="{Binding Source={StaticResource OutcomeGroupViewSource}, Path=View.Groups}"
Path=View.Groups satisfies IntelliSense, but is wrong.
It needs to be Path=Groups, even if the designer supposedly can't resolve the property:

Master-Detail Display with Sorting

I have looked at ~15 threads on this topic, none of which seem to address my problem.
I have two tables in a strongly typed dataset. One is a list of Categories with primary key CategoryID. The second is a list of Items with the foreign key the same CategoryID. In my dataset, I define the relationship betweeen the two tables as relCtoI.
My wpf form contains a listbox listing the categories and a listbox that is to display those items from the category listbox category that the user picks. Both listboxs are to be sorted by name.
My pseudo-XAML looks like this:
<Window.Resources>
<CollectionViewSource x:Key="cvsCategories"
Source="{Binding Source={StaticResource MyDataSet}, Path=Categories}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="CategoryName" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<CollectionViewSource x:Key="cvsItems"
....Same thing for the Items with Path=Items ...
</CollectionViewSource>
<DataTemplate x:Key="myTemplate"
<TextBlock Text="{Binding Source={StaticResource cvsItems}, Path=ItemName}" />
</DataTemplate>
</Window.Resources>
<Grid DataContext="{StaticResource cvsCategories}">
<....define columns />
<ListBox Name="lbxCategories"
Grid.Column="0"
ItemsSource="{Binding}"
DisplayMemberPath="CategoryName" />
<ListBox Name="lbxItems"
Grid.Column="1"
ItemsSource="{Binding Path=relCtoI}"
ItemTemplate="{StaticResource myTemplate}"/>
The Category listbox works fine but the Item listbox is blank. What is wrong with my XAML?

How to set ItemsSource in XAML?

I have set ItemsSource of a ListBox as follows :
<ListBox ItemsSource="{Binding abc}" />
What I want
<ListBox>
<listBox.ItemsSource>
?????????????
<listBox.ItemsSource>
</ListBox>
<Window xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ListBox>
<ListBox.ItemsSource>
<x:Array Type="sys:String">
<sys:String>1st item</sys:String>
<sys:String>2nd item</sys:String>
</x:Array>
<ListBox.ItemsSource>
</ListBox>
</Window>
<ListBox>
<listBox.ItemsSource>
<Binding Path = "abs" />
<listBox.ItemsSource>
</ListBox>
Xamarin Example
If you wandered into this page looking for a Xamarin example (the question seems generic to XAML), then you can try -
<Picker x:Name="picker"
Title="Select a monkey"
TitleColor="Red">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>Baboon</x:String>
<x:String>Capuchin Monkey</x:String>
<x:String>Blue Monkey</x:String>
<x:String>Squirrel Monkey</x:String>
<x:String>Golden Lion Tamarin</x:String>
<x:String>Howler Monkey</x:String>
<x:String>Japanese Macaque</x:String>
</x:Array>
</Picker.ItemsSource>
</Picker>
From -
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/picker/populating-itemssource#populating-a-picker-with-data
This uses Picker as an example, but the ItemsSource syntax is interchangeable based on the outer control, like so -
<ListView>
<ListView.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>mono</x:String>
<x:String>monodroid</x:String>
<x:String>monotouch</x:String>
<x:String>monorail</x:String>
<x:String>monodevelop</x:String>
<x:String>monotone</x:String>
<x:String>monopoly</x:String>
<x:String>monomodal</x:String>
<x:String>mononucleosis</x:String>
</x:Array>
</ListView.ItemsSource>
</ListView>
#HighCore, #DanPazey, and #Vishal:
In fact, the markup binding syntax may prove to be useful and even necessary.
Not to mention multibinding, consider the following.
Suppose you need to bind your ListBox to CollectionViewSource (for sorting or else). Like this:
<Window.Resources>
<CollectionViewSource x:Key="abc_CVS_Key" Source="{Binding abc}" />
</Window.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource abc_CVS_Key}}">
</ListBox>
You may want then, for technical reasons, to limit a scope of the CVS resource to only the ListBox in question.
If you write down ItemsSource binding in an attribute
<ListBox ItemsSource="{Binding Source={StaticResource abc_CVS_Key}}">
<ListBox.Resources>
<CollectionViewSource x:Key="abc_CVS_Key" Source="{Binding abc}" />
</List.Resources>
</ListBox>
your code will compile, but runtime your program will not find your abc_CVS_Key resource key, because the resource has been defined later in code. You need to define the resource before you refer to it in ListBox' ItemsSource binding. Like this:
<ListBox>
<ListBox.Resources>
<CollectionViewSource x:Key="abc_CVS_Key" Source="{Binding abc}" />
</List.Resources>
<ListBox.ItemsSource>
<Binding Source="{StaticResource abc_CVS_Key}" />
</ListBox.ItemsSource>
</ListBox>
This code compiles and executes OK.

C# - WPF Reverse binded ObservableCollection in XAML

I have a ObservableCollection which is binded as ItemSource to my ComboBox. It's a ComboBox which shows you your changes which you've done. My Problem is now, that the newest Change is displayed on the bottom of the list.
I tried to reverse it in the property, but when I return ComboboxName.Reverse() I get an IEnumerable back. I also don't want to make a 2nd collection with the reversed data.
An other way would be to solve this in XAML. Does someone have an idea how I can do that?
I found this answer here, but i dont get it how i can implement it.
Reverse order of ObservableCollection
scm stands for
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
There a solution that works for me. You have to adapt it to your case:
<Window.Resources>
<CollectionViewSource x:Key="customerGroups" Source="{Binding Path=Customers}">
<CollectionViewSource.GroupDescriptions>
<PropertyGroupDescription PropertyName="IsCompany"></PropertyGroupDescription>
</CollectionViewSource.GroupDescriptions>
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="IsCompany" Direction="Descending"/>
<scm:SortDescription PropertyName="DisplayName" Direction="Ascending"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
</Window.Resources>
Then you reference this resource as ItemsSource of your ComboBox, and bind the content to the property you need:
<ComboBox ItemsSource="{Binding Source={StaticResource customerGroups}}">
<DataTemplate>
<TextBlock Text="{Binding Path= FirstName}"></TextBlock>
</DataTemplate>
</ComboBox>

Nested Datagrid in ListBox

I have a datagrid nested inside the ItemTemplate of a ListBox. I'm trying to display a tree like data structure using this. My classes are as follows.
The object in my data context contains a List<Section> named Sections, my ListBox is bound to this. Each Section contains a List<Item> named Items, the DataGrid in eac ItemTemplate is bound to this.
When I run the app, I get a null reference exception from the XAML at the line with the binding. Is there a better/alternative way of doing this, or am I missing a trick with the binding?
<Window.Resources>
<CollectionViewSource x:Key="SectionSource" /><!-- this is initialized and filled with an ObservableCollection<Section> Sections when the window loads-->
</Window.Resources>
<ListBox x:Name="lstIngredients" ItemsSource="{Binding Source={StaticResource SectionSource}}">
<ListBox.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<CollectionViewSource x:Key="itemsSource" Source="{Binding Items}"/>
</DataTemplate.Resources>
<DataGrid x:Name="dgItems" IsReadOnly="false" AutoGenerateColumns="False" SelectionMode="Single" SelectionUnit="FullRow" IsSynchronizedWithCurrentItem="True"
DataContext="{Binding}"
ItemsSource="{Binding Source={StaticResource Items}}"
EnableRowVirtualization="false"
VirtualizingStackPanel.VirtualizationMode="Standard"
<DataGrid.Columns>
<DataGridTemplateColumn Width="2*" Header="{lex:LocText ChickenPing.Shared:Strings:Measurement}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="quantity" Text="{Binding Measurement}" TextTrimming="CharacterEllipsis" TextAlignment="Left"/>
<!-- Null reference on this line caused by the binding. If I set this to any DependencyProperty on an Item object, I get a null reference-->
</DataTemplate>
This need to be path
ItemsSource="{Binding Source={StaticResource Items}}"
ItemsSource="{Binding Path=PropertyThatIsCollection}"
And delete the DataContext line
I eventually tracked this down to an event which was set in one of the TemplateColumns. Switching the event from
<TextBlock x:Name="quantity" Text="{Binding Measurement}" GotFocus="txt_GotFocus" />
to
<Style x:Key="FocusableTextbox" TargetType="{x:Type TextBox}">
<EventSetter Event="GotFocus" Handler="txt_GotFocus" />
</Style>
...
<TextBlock x:Name="quantity" Text="{Binding Measurement}" Style={StaticResource FocusableTextbox} />

Resources