Following this Topics Xceed WPF propertyGrid show item for expanded collection, how is it possible with XAML to sort the list by Name when Binding to Classes ?
Everytime I would like to add a new Item it should be immediately sorted. It doesn't work because if I use Classes.orderby(x=>x.name) it breaks all the bindings?
Hello After lot's of time the solution is at the same time and not, but here I gave you all the solution:
1) the getters and setter for your Observablecollection
private TheCollection yourcollection;
public TheCollection Yourcollection{
get{
yourcollection.CollectionChanged -= Your_CollectionChanged;
// use sort-Extension to sort pointprofil
yourcollection.Sort();
// read CollectionChange-Event
yourcollection.CollectionChanged += Your_CollectionChanged;
return yourCollection;
}
}
And your Collection have to inherit from ObservableCollection but at the Same time to get an Extension to sort()
here we go:
static class Extensions
public static void Sort<T>(this ObservableCollection<T> collection) where T : IComparable
{
List<T> sorted = collection.OrderBy(x => x).ToList();
for (int i = 0; i < sorted.Count(); i++)
collection.Move(collection.IndexOf(sorted[i]), i);
}
}
public class YourCollections : ObservableCollection<YourPoints>, ICustomTypeDescriptor, INotifyPropertyChanged
{
}
and YourPoints have to inherit from IComparable
Related
Im designing an UWP movie app and i need several ObservableCollections to store different movie lists. I wanna use ObservableCollection[Index], but dont know how to initialize it.
I've tried the following code but failed:
public static ObservableCollection<Subject>[] FullMovieList = new ObservableCollection<Subject>[6];
How can i accomplish it? Thx~
You create array of ObservableCollection<Subject> correctly.
But you create array only, not collection in array itself.
You should create collections also;
for (int a = 0; a < FullMovieList.Length; a++)
{
FullMovieList[a] = new ObservableCollection<Subject>();
}
Building a new collection each time you want to add range of objects is not good rather i would follow below design.
Build a class inheriting from ObservableCollection , so that you can access Items property which is protected and then create a AddRange method which will add items into it
public class MyObObservableCollection<T> : ObservableCollection<T>
{
public void AddRange(IEnumerable<T> items)
{
foreach (var item in items)
{
this.Items.Add(item);
}
}
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
}
I know that you can set up a ListBox to sort automatically. Is there a way to "catch" the sorting so that when the ListBox swaps the position of two items so that I can do the same reordering on another list box? I want sort one list box by by value but keep those values at the same relative index locations compared to another ListBox somewhere else.
I could write a routine to bubble sort the list so that I could make the changes myself, but it I am wondering if there is a more automated since I will likely have to do this at a few different places in the program.
Unfortunately, the Sorted property does not use the IComparable interface implementation just sorts based on the result of ToString of the items. But instead of setting the Sorted property, you can use a sorted data source (a List<>, for example).
Create a wrapper class for the items in the ListBox and implement the IComparable<T> interface on it. Populate a List<> with these ListBoxItem instances, then call the Sort method on the list. Thus you will able to dispatch the CompareTo calls.
public partial class Form1 : Form
{
private class ListBoxItem<T> : IComparable<ListBoxItem<T>>
where T : IComparable<T>
{
private T item;
internal ListBoxItem(T item)
{
this.item = item;
}
// this makes possible to cast a string to a ListBoxItem<string>, for example
public static implicit operator ListBoxItem<T>(T item)
{
return new ListBoxItem<T>(item);
}
public override string ToString()
{
return item.ToString();
}
public int CompareTo(ListBoxItem<T> other)
{
return item.CompareTo(other.item); // here you can catch the comparison
}
}
public Form1()
{
InitializeComponent();
var items = new List<ListBoxItem<string>> { "Banana", "Apple"};
items.Sort();
listBox1.DataSource = items;
}
In my Windows Phone app I used CollectionViewSource.SortDescriptors to sort my LongListSelectors content. Now I migrated to Windows Runtime app and am using ListView to display my content. (And WinRT doesn't have SortDescriptors.)
Using OrderBy<>() on my ObserveableCollection is not an option, since I add the items dynamically (and this would cause a complete reload of the ListView).
How do I "binary insert" on ObservableCollection (similar to what's possible with List<>) or is there any alternative to CollectionViewSource.
You could use something like this A winRT CollectionView with filtering and sorting
Alternatively you could create a class and override ObservableCollection.InsertItem like this.
public class SortedCollection<T> : ObservableCollection<T> where T : IComparable<T>
{
protected override void InsertItem(int index, T item)
{
int sortedIndex = FindSortedIndex(item);
base.InsertItem(sortedIndex, item);
}
private int FindSortedIndex(T item)
{
//simple sorting algorithm
for (int index = 0; index < this.Count; index++)
{
if (item.CompareTo(this[index]) > 0)
{
return index;
}
}
return 0;
}
}
To use this class create a new collection and add the items.
SortedCollection<int> sortedCollection = new SortedCollection<int>();
sortedCollection.Add(3);
sortedCollection.Add(1);
sortedCollection.Add(5);
sortedCollection.Add(4);
sortedCollection.Add(2);
//the sorted collection will be 1,2,3,4,5
I have a class that defines a custom GetEnumerator() function(by implementing IEnumerable<>). I use it to iterate in a contiguous manner over several ObservableCollection<LogEvent> that are in every TestStep. I have a private ObservableCollection<TestStep> that contains all the needed data.
I would like to use an instance of this class as the ItemsSource of a ListBox. However, the ListBox never gets updated when the underlying data(ObservableCollection<LogEvent>) is updated. Here's a sample of that class:
public class FlatLogViewModel : IEnumerable<LogEvent>
{
public FlatLogViewModel(ObservableCollection<TestStep> subSteps)
{
m_subSteps = subSteps;
}
public IEnumerator<LogEvent> GetEnumerator()
{
foreach (TestStep step in SubSteps)
{
// step.LogEvents is an ObservableCollection<LogEvent>
foreach (LogEvent logEvent in step.LogEvents)
yield return logEvent;
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private ObservableCollection<TestStep> m_subSteps;
}
I'm not sure if I should/can implement INotifyCollectionChanged here. How can I know if ObservableCollection has been modified?
My question is: how can I get the ListBox to display the changes happening in LogEvents(which is of type ObservableCollection<LogEvent>)?
When theObservableCollection changes, how does the ListBox know? You have to implement like you mentioned INotifyCollectionChanged and then update the ItemSource with the new enumerable data inside the event handler.
ObservableCollection is an INotifyCollectionChanged. Use casting
var collectionChanged = yourObCollection as INotifyCollectionChanged;
if( collectionChanged !=null)
{
collectionChanged.CollectionChanged += YourEventHandler;
}
inside the handler do your own logic to update the item source
Have you considered binding to the original collection but running it through a converter in order to pull out the LogEvents?
The converter should be able to simply return subSteps.SelectMany(s => s.LogEvents).
I have a custom collection that I am passing to a WPF client, which is binding the collection to a datagrid using AutoGenerateColumns="True". The datagrid, however, is displaying empty rows (albeit the right number of empty rows). What am I doing wrong? Following is some sample code. For now I've omitted everything having to do with INotifyPropertyChanged and INotifyCollectionChanged because, well, I first want to have some data showing up in the grid.
I should also mention that I've tried implementing the above two interfaces, but they seem to have nothing to do with this issue.
(You might not actually want to look at the sample code as there's absolutely nothing interesting about it. The collection implementation is just wrapping an inner List.)
Some random POCO:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
Simple collection implementation:
public class MyCollection<T> : IList<T>
{
private List<T> list = new List<T>();
public MyCollection()
{
}
public MyCollection(IEnumerable<T> collection)
{
list.AddRange(collection);
}
#region ICollection<T> Members
public void Add(T item)
{
list.Add(item);
}
public void Clear()
{
list.Clear();
}
public bool Contains(T item)
{
return list.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
list.CopyTo(array, arrayIndex);
}
public int Count
{
get { return list.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
return list.Remove(item);
}
#endregion
#region IEnumerable<T> Members
public IEnumerator<T> GetEnumerator()
{
return list.GetEnumerator();
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region IList<T> Members
public int IndexOf(T item)
{
return list.IndexOf(item);
}
public void Insert(int index, T item)
{
list.Insert(index, item);
}
public void RemoveAt(int index)
{
list.RemoveAt(index);
}
public T this[int index]
{
get { return list[index]; }
set { list[index] = value; }
}
#endregion
}
The XAML:
<Window x:Class="TestWpfCustomCollection.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid AutoGenerateColumns="True"
HorizontalAlignment="Stretch"
Name="dataGrid1" VerticalAlignment="Stretch"
ItemsSource="{Binding}"
/>
</Grid>
</Window>
The window's code-behind:
public MainWindow()
{
InitializeComponent();
MyCollection<Person> persons = new MyCollection<Person>()
{
new Person(){FirstName="john", LastName="smith"},
new Person(){FirstName="foo", LastName="bar"}
};
dataGrid1.DataContext = persons;
}
By the way, if you change the code-behind to use a List<Person> instead of the MyCollection<Person>, everything works as expected.
EDIT:
The above code is not taken from the real situation. I have only posted it to show what I am doing in order to test my problem and to make it easier to replicate it. The actual custom collection object is quite complex and I cannot post it here. Again, I'm just trying to understand the basic concept behind what needs to be done in order for a datagrid to properly bind to a custom collection and automatically generate columns for the underlying objects.
Apparently, in order for AutoGenerateColumns to work in a WPF DataGrid, your collection has to implement IItemProperties, although I've found that wrapping my collection in a (windows forms) BindingList does the trick as well (it actually wraps your collection, unlike the ObservableCollection which just copies your collections' members into itself).
What is your MyCollection<T> type adding over List<T>? Either way, I would use an ObservableCollection instead (so that the UI is notified of additions/removals), and implement INotifyPropertyChanged on your Person class so that the UI is notified of changes to that type's property values.
EDIT
I don't think it's particularly trivial binding to your own custom collection type. You'll need to implement INotifyPropertyChanged and INotifyCollectionChanged on that type. This article might be of some use - http://www.e-pedro.com/2009/04/creating-a-custom-observable-collection-in-wpf/
Another option if you are using MVVM, is to use your custom collection type on your model, and use a standard ObservableCollection on your view model and populate your view models collection from your model, then bind your grid to your view models collection.
I think you using your knowlege of Windows Forms by writing
dataGrid1.DataContext = persons;
In WPF DataGrid is connected to the collection (say List) very simple:
dataGrid1.ItemsSource = persons;