I am using telerik Radgridview in silverlight application. In that i have bind the itemsource with observable collection. Whenever the collection is changed, the radgridview.ItemSource is bound to observable collection. Everythings works fine but whenever item is added to collection, it is just append in the grid. I want to sort the collection after adding. Is there any simple way out for that?
Depends a on your implementation details, but I think you should be able to do it most cases with the OrderBy in LINQ and the CollectionChanged event. You just have to resubscribe to the event. Here's the code that worked for me in quick test:
public class BindingViewModel : ViewModelBase
{
public BindingViewModel()
{
Items = new ObservableCollection<int>();
Items.CollectionChanged += Items_CollectionChanged;
}
public void Add(int value)
{
Items.Add(value);
}
private void Items_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Items = new ObservableCollection<int>(Items.OrderBy(x => x));
Items.CollectionChanged += Items_CollectionChanged;
}
private ObservableCollection<int> _items;
public ObservableCollection<int> Items
{
get { return _items; }
set
{
_items = value;
OnPropertyChanged("Items");
}
}
}
Related
I have a problem. I created a ListView with as itemsource a List called unknownDeviceList from my ViewModel. Here is my ViewModel:
public class VM_AddDeviceList : BindableObject
{
private List<UnknownDevice> _unknownDeviceList;
public List<UnknownDevice> unknownDeviceList
{
get
{
return _unknownDeviceList;
}
set
{
if (_unknownDeviceList != value)
{
_unknownDeviceList = value;
OnPropertyChanged();
}
}
}
public List<UnknownDevice> deviceList_raw;
public VM_AddDeviceList()
{
deviceList_raw = new List<UnknownDevice>();
unknownDeviceList = new List<UnknownDevice>();
MyHandler();
}
private async Task LoadUnknownDeviceList()
{
deviceList_raw = await App.RestService.GetDevices();
foreach (UnknownDevice device in deviceList_raw)
{
bool containsItem = App.knownDeviceList.Any(item => item.MAC == device.MAC);
if (!containsItem)
{
unknownDeviceList.Add(device);
}
}
}
public Task MyHandler()
{
return LoadUnknownDeviceList();
}
}
Now I can see that unknownDeviceList gets filled in the foreach, but on the screen the ListView stays empty. What am I doing wrong?
Something with the async and await?
You are raising PropertyChanged when setting unknownDeviceList to inform the view that the list has changed. Anyway, there is no way for the view to know that there were items added to unknownDeviceList.
The most idiomatic way to solve the issue would be to use an ObservableCollection<string> instead.
private ObservableCollection<string> _unknownDevices = new ObservableCollection<string>();
public ObservableCollection<string> UnknownDevices => _unknownDevices;
Please note that I've used the expression body syntax for read-only properties for UnknownDevices, it's not a field.
Since ObservableCollection<string> implements INotifyCollectionChanged which can be subscribed to by the binding to UnknownDevices the view is informed about the changes in UnknownDevices and will be updated when any items are added or removed.
I have a datagrid in a WPF app that is bound to an ObservableCollection like so
<DataGrid ItemsSource="{Binding Spring.SpringData, Mode=OneWay}" />
My data displays fine, and I can edit the data in my grid, but it does not fire the PublishSpringChange event when I manually edit the data in the grid. The underlying data changes, but the event does not fire, what am I missing?
With a model of Spring that has the following
public class Spring : INotifyPropertyChanged
{
private ObservableCollection<SpringData> _SpringData;
public ObservableCollection<SpringData> SpringData
{
get { return _SpringData; }
}
public Spring()
{
....
_SpringData = new ObservableCollection<SpringData>();
SpringData.CollectionChanged += PublishSpringChange;
...
}
private void PublishSpringChange(object sender, NotifyCollectionChangedEventArgs e)
{
// Code that does not run!
}
}
with a SpringData class of
public class SpringData: BindableBase
{
private double _force;
private double _displacement;
public SpringData(double displacement, double force)
{
Displacement = displacement;
Force = force;
}
public double Displacement
{
get { return _displacement; }
set { SetProperty(ref _displacement, value); }
}
public double Force
{
get { return _force; }
set { SetProperty(ref _force, value); }
}
}
INotifyCollectionChanged only fires when you actually modify the collection. This is when you Add, Remove, Move, Replace or Reset items in the collection. It will not fire when one of the properties in a SpringData object is changed.
In order to listen to changes for a SpringData object, assuming it implements INotifyPropertyChanged, you will need to hook up listeners to the PropertyChanged event of each of the items.
Its quite useful to have a single handler for all properties changing sometimes. Here's how you can do it.
Handle CollectionChanged as you are above:
_SpringData = new ObservableCollection<SpringData>();
SpringData.CollectionChanged += PublishSpringChange;
Now for all added and removed objects to the collection add a handler to PropertyChanged:
private void PublishSpringChange(object sender, NotifyCollectionChangedEventArgs e)
{
foreach (INotifyPropertyChanged added in e.NewItems)
{
added.PropertyChanged += SpringDataOnPropertyChanged;
}
foreach (INotifyPropertyChanged removed in e.OldItems)
{
removed.PropertyChanged -= SpringDataOnPropertyChanged;
}
}
private SpringDataOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
//your code here
}
I have two lists:
public List<Line> UnfilteredList {get; private set;}
public List<Line> FilteredList {get {
return this.UnfilteredList.Where(x=> x.IsItem);
}
}
I have the grid binding to the Filtered List. This is showing a list of the items being filtered. However, when I add an item to UnfilteredList, this doesn't show up on the grid because assumingly the databinding does not refresh.
What are the possible solutions to this and how do I notify the DataGrid View to re-bind to the list once an item has been changed/added/removed from the UnfilteredList?
The list is being bound by using
this.dgvMain.DataSource =new BindingList<Line>(FilteredList);
You need to re-bind based on two scenarios:
A new list is created and when items are added/removed. Because of this, I would recommend using an ObservableColleciton to observe the adding/removing of items, and then you can force a re-bind whenever this happens.
private ObservableCollection<Line> _unfilteredList;
public ObservableCollection<Line> UnfilteredList
{
get { return _unfilteredList; }
private set
{
_unfilteredList = value;
UpdateList();
}
}
private List<Line> _filteredList;
public List<Line> FilteredList
{
get
{
return _filteredList;
}
private set
{
_filteredList = value;
RaisePropertyChanged();
}
}
private void UpdateList()
{
if (UnfilteredList != null)
{
FilteredList = null;
FilteredList = UnfilteredList.Where(x=> x.IsItem).ToList();
}
}
Then, you can call UpdateList when items are added/removed
// subscribe
UnfilteredList += OnCollectionChanged;
private void OnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if ((e.NewItems != null) || (e.OldItems != null))
{
UpdateList();
}
}
I have a ListView bound to a view model which contains an ObservableCollection in a simple example app.
public class ViewModel : INotifyPropertyChanged
{
public ViewModel(){}
public event PropertyChangedEventHandler PropertyChanged;
public ObservableCollection<Item> _items;
public ObservableCollection<Item> Items
{
get
{
return this._items;
}
set
{
if (value != this._items)
{
this._items = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Items"));
}
}
}
}
public class Item
{
public string Name;
}
The following function is bound to the SelectionChanged event of the ListView
private void ItemListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
model.Items.Add(new Item { Name = "added item" });
model.Items = new ObservableCollection<Item> { new Item { Name = "new item 1" }};
}
When the event fires, this should happen
new item ("added item") appended to existing ObservableCollection
ObservableCollection set to a new collection [single item, "new item 1"]
What actually happens:
ObservableCollection set to new collection [single item, "new item 1"]
new item ("added item") appended to end of new collection
Can anyone explain why these are happening in the wrong order?
Can anyone explain why these are happening in the wrong order?
My guess is that they're not happening in the wrong (reversed) order but the append is happening twice. Executing
model.Items = ... ;
in the SelectionChanged of those same Items is pretty bold. It will trigger a SelectionChanged again, and only because the Selection then remains at none (Index -1) you do not get into an infinite loop.
observable collection not need inotify, try this:
public class ViewModel : INotifyPropertyChanged
{
public ViewModel(){}
public ObservableCollection<Item> Items ;
}
A ObservableCollection
private ObservableCollection<string> _items = new ObservableCollection<string>();
public ObservableCollection<string> Items { get { return _items; } }
is updated on user interactions (a TextBox event).
In a ListBox I'll show the current values
Binding listBinding = new Binding {Source = Items};
listbox.SetBinding(ListBox.ItemsSourceProperty, listBinding);
That works so far: When adding a new value the list is immediately updated.
But now I have to requirements:
Sort values
Add one item at the beginning of the list
I solved as followed:
public IEnumerable<string> ItemsExtended
{
get
{
return new[] { "first value" }.Concat(Items.OrderBy(x => x));
}
}
I changed the binding to that IEnumerable and the list contains a sorted list with "first value" at position one.
Unfortunately when the list should be updated on user interaction it does not work any more. Even changing IEnumerable to ObservableCollection again and directly referring to the private ObservableCollection does not solve the issue:
return new ObservableCollection<string> (new[] { "bla" }.Concat(_items.OrderBy(x => x)));
How to update the list when _items changes?
Off the top of my head. You could implement INotifyPropertyChanged for the class that your collection belongs to . After that add a CollectionChanged handler for your _items collection and fire PropertyChanged("ItemsExtended") in that handler. Also using yield return in the getter will avoid creating a new collection just to add the item at the top.
It should look something like this
public partial class MyClass : INotifyPropertyChanged
{
ObservableCollection<string> _items;
public MyClass()
{
_items = new ObservableCollection<string>();
_items.CollectionChanged += (s, e) => { OnPropertyChanged("Items"); };
}
public IEnumerable<string> Items
{
get
{
yield return "first value";
foreach (var item in _items.OrderBy(x=>x))
yield return item;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}