Binding a BindingList to a collection - c#

I have a library that returns a collection like this:
public IEnumerable Alerts { .. }
and I want to turn this collection into a BindingList for use in the GUI. What is the best way to keep a BindingList synchronised with an IEnumerable collection?
edit: For this problem, assume I have no control of the library and the actual implementation uses a List.
But I do not want to touch this code.
This library also has a nice interface with AddAlert, RemoveAlert etc. What is the best way to keep the GUI synchronised with all these changes?

Assuming the class you are wrapping exposes things like Insert, you should just be able to derive from BindingList<T>, overriding a few key methods - something like:
class MyList<T> : BindingList<T>
{
private readonly Foo<T> wrapped;
public MyList(Foo<T> wrapped)
: base(new List<T>(wrapped.Items))
{
this.wrapped = wrapped;
}
protected override void InsertItem(int index, T item)
{
wrapped.Insert(index, item);
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
wrapped.Remove(this[index]);
base.RemoveItem(index);
}
protected override void ClearItems()
{
wrapped.Clear();
base.ClearItems();
}
// possibly also SetItem
}
This should result in the lists staying in sync as you manipulate them.

Related

Some circular, repeated event handling here? But what and how?

How can I avoid this warning message in VS2017 (or: what may be happening):
'ObsCol<T>.ObsCol()' contains a call chain that results in a call to a virtual method
defined by the class.
Review the following call stack for unintended consequences:
ObsCol<T>..ctor()
ObservableCollection<T>.add_CollectionChanged(NotifyCollectionChangedEventHandler): Void
this is the code:
[Serializable]
public class ObsCol<T> : ObservableCollection<T>
{
public ObsCol()
{
this.CollectionChanged += new NotifyCollectionChangedEventHandler(ObsCol_CollectionChanged);
}
private void ObsCol_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
MainViewModel.IsDirty = e.NewItems != null || e.OldItems != null;
}
}
The purpose of this all is to know in the ViewModel whether or not the calculation output corresponds to the input data.
Upon entering the constructor, CollectionChanged is empty, so my handler is the only thing in the list.
Changing CollectionChanged does not change the collection, does it?
Things seem to work alright, though...
The Problem: some subclass could override the event, leading to unexpected construction behavior
public class Boom<T> : ObsCol<T>
{
public override event NotifyCollectionChangedEventHandler CollectionChanged
{
add { throw new NotImplementedException(); }
remove { }
}
}
Solutions:
Use OnCollectionChanged override
public class Solution1<T> : ObservableCollection<T>
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
DoSomething();
base.OnCollectionChanged(e);
}
...
}
Seal the class
public sealed class Solution2<T> : ObservableCollection<T>
{
public Solution2()
{
this.CollectionChanged += new NotifyCollectionChangedEventHandler(ObsCol_CollectionChanged);
}
...
}
Seal the event
public class Solution3<T> : ObservableCollection<T>
{
public sealed override event NotifyCollectionChangedEventHandler CollectionChanged
{
add { base.CollectionChanged += value; }
remove { base.CollectionChanged -= value; }
}
...
}
What you do depends on your use case... the first solution is most flexible, since you can still create subclasses with all kinds of override.
With the SerializableAttribute in your question, it might be worth to seal the whole class since you probably don't want to serialize sub-types anyway (Solution 2).
If you are ever in a situation, where the OnEvent/RaiseEvent method is not available for whatever reason, Solution 3 allows to seal only the minimal subset of functionality that is used in the constructor.

Cascading IPropertyChanged

I have a data structure organised as such:
A List<Graphic> containing a List<Symbol> which contains a List<Alias> amongst other things.
I want to be able to run a function within the Graphic class whenever anything changes within an alias/symbol/graphic. The best way that I can see to do this would be to implement IPropertyChanged on each of the three classes. However, is it possible to cascade these whilst getting a reference to the Graphic as to what exactly changed?
Note: The changes will generally be to the properties within an Alias but it is just as plausible that a Symbol could be removed/added or renamed.
You can leverage class ObservableCollection<T> that implements INotifyCollectionChanged and INotifyPropertyChanged
Basically, you need to create a derived class and override some methods
public class Data
{
public ObservableCollection<String> InnerCollection { get; set; }
}
public class collection : ObservableCollection<Data>
{
protected override void InsertItem(int index, Data item)
{
item.InnerCollection.CollectionChanged += InnerCollection_CollectionChanged;
base.InsertItem(index, item);
}
private void InnerCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
//Actually it does not make any sense. You may need to construct something special. But firing an event it would be enough
OnCollectionChanged(e);
}
protected override void RemoveItem(int index)
{
var date = base.Items[index];
date.InnerCollection.CollectionChanged -= InnerCollection_CollectionChanged;
base.RemoveItem(index);
}
}
Using something like this, you can nest your events as deep as you want.

How to make a List of interfaces to perform a action

I know the question can be answered by saying foreach(var item in items){item.doSomething()}; but what i'm after is slightly different. Here is the interface.
ManagableClass .cs
public interface ManagableClass : System.IDisposable
{
void Initialize();
}
and below is how I would like to see my code look like
MainManagerClass.cs
public class MainManagerClass: ManagableClass
{
private List<ManagableClass> minions;
public void Initialize()
{
TellMinionsTo(Initialize);
}
public void Dispose()
{
TellMinionsTo(Dispose);
}
private void TellMinionsTo(Action doSomething)
{
foreach (ManagableClass minion in minions)
{
minion.doSomething();
}
}
}
I know that this code that is here will not work, but it seems like this should be doable in C#. Anyone know if this can be done? If not it's not like it's the end of the world, I'll just do a foreach in each method.
The problem with your code is that you pass a delegate to a method of a certain instance (yourself), while what you want is to invoke a certain method on all minions.
You can use lambda expressions, something like
public void Dispose()
{
TellMinionsTo(minion=>minion.Dispose());
}
private void TellMinionsTo(Action<ManagableClass> doSomething)
{
foreach (ManagableClass minion in minions)
{
doSomething(minion);
}
}
I don't like using List's method directly. Tomorrow you'll have to work via an interface and your IList may not have to be a List at all.
What you are looking for is a Composite pattern. This pattern will allow you to have a class that will implement the correct method and differ it to a list of items. However, in your case, you need to modify your interface to implement the DoSomething() method. So, instead, this should look like this.
public interface ISomething
{
void DoSomething();
}
public class SomethingManager : ISomething
{
private List<ISomething> _items = new List<ISomething>();
public void DoSomething()
{
_items.ForEach(i => i.DoSomething());
}
}
Is that what you are looking for ?

Model List<T> and ViewModel ObservableCollection<T> Duplicate Data?

What is the best practice for having a List<T> in the business layer that needs to be displayed on a UI? I currently use databinding with an ObservableCollection<T> in the viewmodel that duplicates the data of the List<T>. The obvious problem of this is when the List<T> is modified in the business layer the ObservableCollection<T> needs to be recreated so the changes of the List<T> are reflected in the UI. This can't be the best way.
I also will not accept using an ObservableCollection<T> in the business layer as an answer.
Thanks!
If you insist on having List<T> and separate events notifying about list modification, then duplication is the only sensible way.
If you have ListChanged event with no details on what was actually changed, you can avoid duplication and just wrap the list in a proxy collection implementing INotifyCollectionChanged interface which will fire appropriate CollectionChanged events in NotifyCollectionChangedAction.Reset mode.
If you have granular ItemChanged, ItemAdded etc. events, then you're effectively duplicating ObservableCollection<T> functionality. In this case, you can wrap your application in a proxy collection implementing INotifyCollectionChanged interface, but which understands your architecture and translates events into appropriate NotifyCollectionChangedAction.
Having ObservableCollection<T> in business layer isn't a bad idea at all. It is a specialized collection which provides common interface to notify about item changes, not some class designed specifically for WinForms or WPF or whatever.
You can implement the INotifyCollectionChanged interface, but if you want to use it in a way that you can hold on your implementation the collection in case implementing a class of your own also holding an implementation of IEnumerable will do a lot of the work for you a for instance is what follows, this is the base class i use for holding all the collections that will be updated, on this implementation there is also an ordering consideration in the variable _ordering:
public abstract class BaseINotifyCollectionChanged<T, K> : INotifyCollectionChanged, IEnumerable<T>
{
Func<T, K> _ordering;
bool _ascending;
public BaseINotifyCollectionChanged()
{
}
public BaseINotifyCollectionChanged(Func<T, K> ordering, bool ascending = true)
{
_ordering = ordering;
_ascending = ascending;
OnCollectionChanged();
}
protected abstract IList<T> GetCollection();
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected void OnCollectionChanged()
{
if (CollectionChanged != null)
CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
public void RaiseCollectionChanged()
{
OnCollectionChanged();
}
public IEnumerator<T> GetEnumerator()
{
return _ordering == null ? GetCollection().GetEnumerator() : _ascending ? GetCollection().OrderBy<T, K>(_ordering).GetEnumerator() :
GetCollection().OrderByDescending<T, K>(_ordering).GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _ordering == null ? GetCollection().GetEnumerator() : _ascending ? GetCollection().OrderBy<T, K>(_ordering).GetEnumerator() :
GetCollection().OrderByDescending<T, K>(_ordering).GetEnumerator();
}
}
}
When you have this implementation you can use as you wish and through out all the collections you need on your app, working on some dry for yourself here is one example of the use you can give to this abstract class:
public class Categories : BaseINotifyCollectionChanged<Category, string>
{
long _onCategoryRoot;
public void SetOnCategoryRoot(long categoryId)
{
_onCategoryRoot = categoryId;
RaiseCollectionChanged();
}
protected override IList<Category> GetCollection()
{
Category category = new Category();
return _onRoot ? category.GetRootCategories() : category.GetSubCategoriesOnRoot(_onCategoryRoot);
}
}
When you set a _onCategoryRoot in the class the collection you will be displaying will be updated via the RaiseCollectionChanged() method, so you need to add in your viewmodel a property with the class Categories and set the binding in the XAML.

Do a check when List.Add method is called

I have a composite property called Items of type List to an order class. On the GUI the user fills out some fields like Name, Description, Price, Quantity, etc... and then clicks the Add Item button which of course adds the item to the order's list of items. What I'd like to do is create a method that checks the item's IsComplete property which does a check to ensure the required properties are set so that way someone can't just call order.Items.Add(item) if it isn't complete. If it's not I'd like an exception to be thrown if the item's IsComplete property returns false... What would be an easy way to go about this?
This can be achieved by sub-classing List<T> into a derived class, and then overriding the Add method, like so.
public class MyItemCollection : List<MyItem>
{
public override void Add(MyItem item)
{
if (item.IsComplete)
{
base.Add(item);
}
else
{
throw new InvalidOperationException("Unable to add an incomplete item");
}
}
}
Your order class would then have the property MyItemCollection rather than List<T>, like so:
public class Order
{
public MyItemCollection Items { get; set; }
}
You can also use ObservableCollection<T>: http://msdn.microsoft.com/en-us/library/ms668604.aspx
It implements INotifyCollectionChanged: http://msdn.microsoft.com/en-us/library/System.Collections.Specialized.INotifyCollectionChanged.aspx
Since the method Add(T) is not virtual you can't override, it.
ObservableCollection allow to throw an event when an element was added but not to undo this add.
You can implement the interface IList<T> with a List<T> storred internaly and add the desired verification in the method Add(T item) before calling the _list.Add(item) like in the exemple below :
public class MyItemCollection : IList<MyItem>
{
private List<MyItem> _list;
public MyItemCollection()
{
_list = new List<MyItem>();
}
public void Add(MyItem item)
{
if (item.IsComplete)
{
_list.Add(item);
}
else
{
throw new InvalidOperationException("Unable to add an incomplete item");
}
}
//Then you have to implement all the IList interface members...
}
The only problem with this solution is that it require to write a lot of boilerplate code.
If only one class is responsible of the manipulation of your List, you can also decide to implement a method AddToMyItemCollection(MyItem item) in the responsible class. It is even a good practive as it's respect the GRASP pattern protected variation (Instance.getC() is preferable to Instance.getA().getB().getC())

Categories

Resources