Opinion wanted: Intercepting changes to lists/collections - c#

Although BindingList<T> and ObservableCollection<T> provide mechanisms to detect list changes, they don't support mechanisms to detect/intercept changes before they happen.
I'm writing a couple of interfaces to support this, but I want to canvas your opinion.
Option 1: Lists raise events for each type of action
Here, consumers might write code like this:
public class Order : Entity
{
public Order()
{
this.OrderItems = new List<OrderItem>();
this.OrderItems.InsertingItem += new ListChangingEventHandler<OrderItem>(OrderItems_InsertingItem);
this.OrderItems.SettingItem += new ListChangingEventHandler<OrderItem>(OrderItems_SettingItem);
this.OrderItems.RemovingItem += new ListChangingEventHandler<OrderItem>(OrderItems_RemovingItem);
}
virtual public List<OrderItem> OrderItems { get; internal set; }
void OrderItems_InsertingItem(object sender, IOperationEventArgs<OrderItem> e)
{
if (!validationPasses)
{
e.Cancel = true;
return;
}
e.Item.Parent = this;
}
void OrderItems_SettingItem(object sender, IOperationEventArgs<OrderItem> e)
{
if (!validationPasses)
{
e.Cancel = true;
return;
}
e.Item.Parent = this;
}
void OrderItems_RemovingItem(object sender, IOperationEventArgs<OrderItem> e)
{
if (!validationPasses)
{
e.Cancel = true;
return;
}
e.Item.Parent = null;
}
}
Option 2: Lists raise a single event, and the action is determined from the event args
Here, consumers might write code like this:
public class Order : Entity
{
public Order()
{
this.OrderItems = new List<OrderItem>();
this.OrderItems.ListChanging += new ListChangingEventHandler<OrderItem>(OrderItems_ListChanging);
}
virtual public List<OrderItem> OrderItems { get; internal set; }
void OrderItems_ListChanging(object sender, IOperationEventArgs<OrderItem> e)
{
switch (e.Action)
{
case ListChangingType.Inserting:
case ListChangingType.Setting:
if (validationPasses)
{
e.Item.Parent = this;
}
else
{
e.Cancel = true;
}
break;
case ListChangingType.Removing:
if (validationPasses)
{
e.Item.Parent = null;
}
else
{
e.Cancel = true;
}
break;
}
}
}
Background: I'm writing a set of general purpose interfaces/classes that represent the core components of DDD, and I'm making the source code available (hence the need to create friendly interfaces).
This question is about making the interface as cohesive as possible, so that consumers can derive and implement their own collections without losing the core semantics.
PS: Please don't suggest using AddXYZ() and RemoveXYZ() methods for each list, because I've already discounted that idea.
PPS: I must include developers using .NET 2.0 :)
Related question.

I would suggest creating something that parallels the ObservableCollection<T> where appropriate. Specifically, I would suggest following the existing techniques for notification of change of collection. Something like:
class MyObservableCollection<T>
: INotifyPropertyChanging, // Already exists
INotifyPropertyChanged, // Already exists
INotifyCollectionChanging, // You'll have to create this (based on INotifyCollectionChanged)
INotifyCollectionChanged // Already exists
{ }
This will follow established patterns so that clients are already familiar with the exposed interfaces-- three of the interfaces already exist. The use of existing interfaces will also allow more proper interaction with other already existing .NET technologies, such as WPF (which binds against the INotifyPropertyChanged and INotifyCollectionChanged interfaces.)
I would expect the INotifyCollectionChanged interface to look something like:
public interface INotifyCollectionChanged
{
event CollectionChangingEventHandler CollectionChanging;
}
public delegate void CollectionChangingEventHandler(
object source,
CollectionChangingEventArgs e
);
/// <remarks> This should parallel CollectionChangedEventArgs. the same
/// information should be passed to that event. </remarks>
public class CollectionChangingEventArgs : EventArgs
{
// appropriate .ctors here
public NotifyCollectionChangedAction Action { get; private set; }
public IList NewItems { get; private set; }
public int NewStartingIndex { get; private set; }
public IList OldItems { get; private set; }
public int OldStartingIndex { get; private set; }
}
If you wish to add cancellation support, simply add a writable bool Cancel property to CollectionChangingEventArgs that the collection will read to determine whether to execute the change that's about to occur.
I suppose this falls under your Option 2. This is the way to go because, to interoperate properly with other .net technologies that monitor changing collections, you're going to have to implement it anyway for INotifyCollectionChanged. This will definitely follow the policy of "Least Surprise" in your interface.

I would recomend seperate events. It seems more clear to me.
EDIT:
You might want to cosider a before and after event such as Inserting,Inserted or as the VB guys have it BeforeInsert, AfterInsert. This will give the user more flexability.

Have a look at this link, maybe that is what you are looking for, a Generic List based object that acts as a List but with built-in events such as BeforeItemAdded, ItemAdded, BeforeItemRemoved, ItemRemoved and ItemsCleared.
Hope this helps, Tom. :)

Actually, you will be surprised how easily you can create a collection like that.
Take a look at System.Collections.ObjectModel.Collection<T>. That is a class which is intended to be used for such things. It has a few virtual methods (one for every operation) which you can override and control very well.
I would recommend Option 1, since it is more clear and straightforward.
Here is an example which you can use for such purposes:
using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Linq;
namespace TestGround
{
public class MyCollection<T> : Collection<T>
{
public class ListChangeEventArgs : EventArgs
{
public IEnumerable<T> ItemsInvolved { get; set;}
public int? Index { get; set;}
}
public delegate void ListEventHandler(object sender, ListChangeEventArgs e);
public event ListEventHandler Inserting;
public event ListEventHandler Setting;
public event ListEventHandler Clearing;
public event ListEventHandler Removing;
public MyCollection() : base() { }
public MyCollection(IList<T> innerList) : base(innerList) { }
protected override void ClearItems()
{
Clearing(this, new ListChangeEventArgs()
{
Index = null,
ItemsInvolved = this.ToArray(),
});
base.ClearItems();
}
protected override void InsertItem(int index, T item)
{
Inserting(this, new ListChangeEventArgs()
{
Index = index,
ItemsInvolved = new T[] { item },
});
base.InsertItem(index, item);
}
protected override void RemoveItem(int index)
{
Removing(this, new ListChangeEventArgs()
{
Index = index,
ItemsInvolved = new T[] { this[index] },
});
base.RemoveItem(index);
}
protected override void SetItem(int index, T item)
{
Setting(this, new ListChangeEventArgs()
{
Index = index,
ItemsInvolved = new T[] { item },
});
base.SetItem(index, item);
}
}
}
You could also modify the ListChangeEventArgs to have a bool property with the name "Cancel", and control wheter to do the change or not in the collection.
The after events could also be useful, if you need such functionality.
Of course, you won't have to use all events of every collections, or if it is really necessary, there may be other ways to solve the problem depending on why do you need this functionality.
EDIT:
If you really only want to validate the items and set their Parent property to an entity instance, you can actually write a collection which does exactly that, or something that generalizes the problem in another way. You could pass it a delegate which validates the item, and and another which tells it what to do when an item is added or removed.
For example, you can achieve this using the Action delegate.
You could consume it this way:
class Order : Entity
{
public Order()
{
OrderItems = new MyCollection2<OrderItem>(
//Validation action
item => item.Name != null && item.Name.Length < 20,
//Add action
item => item.Parent = this,
//Remove action
item => item.Parent = null
);
}
...
}
The major benefit of this approach is that you don't have to bother with event handlers or delegates, beacuse all that you need can be written using lambda expressions, however if you need something more advanced, you can always use a real delegate instead of them.
This is an example of the collection:
public class MyCollection2<T> : Collection<T>
{
public Func<T, bool> Validate { get; protected set; }
public Action<T> AddAction { get; protected set; }
public Action<T> RemoveAction { get; protected set; }
public MyCollection2(Func<T, bool> validate, Action<T> add, Action<T> remove)
: base()
{
Validate = Validate;
AddAction = add;
RemoveAction = remove;
}
protected override void ClearItems()
{
foreach (var item in this)
{
RemoveAction(item);
}
base.ClearItems();
}
protected override void InsertItem(int index, T item)
{
if (Validate(item))
{
AddAction(item);
base.InsertItem(index, item);
}
}
protected override void RemoveItem(int index)
{
RemoveAction(this[index]);
base.RemoveItem(index);
}
protected override void SetItem(int index, T item)
{
if (Validate(item))
{
RemoveAction(this[index]);
AddAction(item);
base.SetItem(index, item);
}
}
}
For such purposes, I think this is the cleanest way to go.

Related

How to sync data in MVVM when using collections

Recently I have started to dig into MVVM to structure a WPF application I am working on. I am struggling to understand how I can keep collections in sync between Model and ViewModel, and in conjunction with that, how to validate information the user will enter.
Suppose I have a (theoretical) class Building, the model, that will store a building layout, during runtime in memory, and otherwise in xml via serialization. Building has a member List, and each entry Floor in that list can have other Lists, like List and List, which could again have members which are Lists (ie. List).
The model:
namespace TestMVVM
{
public class Building
{
public string strName { get; set; }
public List<Floor> floors { get; set; }
}
public class Floor
{
public int iNumber { get; set; }
public List<Room> rooms { get; set; }
}
public class Room
{
public int iSize { get; set; }
public string strName { get; set; }
public List<Door> doors { get; set; }
}
public class Door
{
public bool bIsLocked { get; set; }
}
}
In the View, the List of type Floor will be editable in a DataGrid. The user can enter a new row in the DataGrid to add a Floor to the Building class. In another DataGrid, Rooms could be added to a Floor. This is quite easy when I make all Lists into ObservableCollections, and directly couple them with the View. However, this also means there is no proper separation of concerns, and it gets messy once validation comes into play.
So I wrote a ViewModel class, BuildingViewModel. It will hold a reference to an instance of the model. This is where I run into trouble: the ViewModel will hold an ObservableCollection of type FloorViewModel. But when the user adds an entry, how do I also add an entry to the List in the model? And mostly, keep the data in sync? What if a Room is added to a Floor, or a Door to a Room, how to know where in the Model to update which data? Ie. how to sync nested List member data?
Subsequently I would to make sure no duplicate Floors can be created; ie. if the user adds a floor with a number that is already in the List, the DataGrid must report an error. Same if an existing floor is edited, and same for Room names. I would think that kind of error checking cannot happen within the FloorViewModel class, because it has no access to other instances of itself.
I have searched a lot but found no clear answer to this. It would seem like a rather common situation? Maybe I am simply going in the wrong direction with this?
This is the current ViewModel, where ViewModelBase is a generic class holding implementations of INotifyProretyChanged and INotifyDataErrorInfo.
namespace TestMVVM
{
public class BuildingViewModel : ViewModelBase
{
private Building building;
public string strName
{
get { return building.strName; }
set
{
building.strName = value;
if (value == "") AddError("strName", "Name cannot be empty.");
OnPropertyChanged("strName");
}
}
public ObservableCollection<FloorViewModel> floors
{
// what goes here? how to sync members of floor to the model, and validate data?
}
public BuildingViewModel(Building b)
{
building = b;
}
}
public class FloorViewModel : ViewModelBase
{
public ObservableCollection<Room> rooms
{
// what goes here? how to sync members of room to the right Floor of the model, and validate data?
}
}
// etc
}
There is a problem in the classes, that You provided. Try to apply the law of Demeter, watch this video about how to structure correctly the House object (even same example), than You only call the correct level's addX() method, that will validate.
Look you need to read again MVVM concept.. All the idea is to have one view model per each view. In our situation try this:
namespace TestMVVM
{
public class BuildingViewModel : ViewModelBase
{
private Building building;
private ObservableCollection<Floor> _floors;
public string strName
{
get { return building.strName; }
set
{
//building.strName = value;
if (String.IsNullOrEmpty(value))
{
AddError("strName", "Name cannot be empty.");
return;
}
building.strName = value;
OnPropertyChanged("strName");
}
}
public ObservableCollection<Floor> floors
{
get
{
return _floors;
}
set
{
_floors = value;
}
}
public BuildingViewModel(Building b)
{
building = b;
}
public void AddNewFloor(Floor)
{
// valid your floor
// floors.Add(floor);
}
}
Now I suggest you to add function that will validate your changes in floors and not in the setter of the property.
Or override/create ObservableCollection class and redefine all methods :
public class MyObservableCollection<T> : ICollection<T>, INotifyCollectionChanged, INotifyPropertyChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public event PropertyChangedEventHandler PropertyChanged;
public int Count { get { return _reference.Count; } }
public bool IsReadOnly { get { return _reference.IsReadOnly; } }
private readonly IList<T> _reference;
public MyObservableCollection(IList<T> reference)
{
_reference = reference;
}
public IEnumerator<T> GetEnumerator()
{
return _reference.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(T item)
{
_reference.Add(item);
SendNotification();
}
public void Clear()
{
_reference.Clear();
SendNotification();
}
public bool Contains(T item)
{
return _reference.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex)
{
_reference.CopyTo(array, arrayIndex);
}
public bool Remove(T item)
{
var result = _reference.Remove(item);
SendNotification();
return result;
}
private void SendNotification()
{
if (CollectionChanged != null)
{
CollectionChanged(this, new NotifyCollectionChangedEventArgs(new NotifyCollectionChangedAction()));
}
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("..."));
}
}
}
Why you don't change type (List to ObservableCollection) on Model ?
In this case :
public ObservableCollection<FloorViewModel> floors
{
get{return building.floors;}
}

How to implement an event using Reactive Extensions

The Reactive Extensions allow you to easily subscribe to an event using Observable.FromEventPattern, but I can't find anything on how you might implement an event when you have an IObservable.
My situation is this: I need to implement an interface which contains an event. That event is supposed to be called whenever a certain value of my object changes, and for thread safety reasons I need to call this event on a certain SynchronizationContext. I am also supposed to call each event handler with the current value on registration.
public interface IFooWatcher
{
event FooChangedHandler FooChanged;
}
Getting an observable that does what I want is rather easy with Rx using BehaviorSubject:
public class FooWatcher
{
private readonly BehaviorSubject<Foo> m_subject;
private readonly IObservable<Foo> m_observable;
public FooWatcher(SynchronizationContext synchronizationContext, Foo initialValue)
{
m_subject = new BehaviorSubject<Foo>(initialValue);
m_observable = m_subject
.DistinctUntilChanged()
.ObserveOn(synchronizationContext);
}
public event FooChangedHandler FooChanged
{
add { /* ??? */ }
remove { /* ??? */ }
}
}
Now I am looking for an easy way to have the add and remove functions subscribe and unsubscribe the passed FooChangedHandler as an Observer<Foo> on m_observable. My current implementation looks similar to this:
add
{
lock (m_lock)
{
IDisposable disp = m_observable.Subscribe(value);
m_registeredObservers.Add(
new KeyValuePair<FooChangedHandler, IDisposable>(
value, disp));
}
}
remove
{
lock (m_lock)
{
KeyValuePair<FooChangedHandler, IDisposable> observerDisposable =
m_registeredObservers
.First(pair => object.Equals(pair.Key, value));
m_registeredObservers.Remove(observerDisposable);
observerDisposable.Value.Dispose();
}
}
However, I hope to find an easier solution, because I need to implement several of these events (of differing handler types). I tried to roll my own generic solution but it creates some additional problems that need to be worked around (in particular, how you generically work with a delegate that takes a parameter of T), so I would prefer to find an existing solution that bridges the gap in this direction - just as FromEventPattern does the reverse.
You could do this:
public event FooChangedHandler FooChanged
{
add { m_observable.ToEvent().OnNext += value; }
remove { m_observable.ToEvent().OnNext -= value; }
}
However, on the remove, I think perhaps you just may want to dispose of the subscription ... or perhaps get the Action from ToEvent() and store that as a member. Untested.
EDIT: You'll have to use Action instead of a FooChangedHandler delegate, however.
EDIT 2: Here's a tested version. I suppose you need to use FooChangedHandler, however, since you have a bunch of these pre-existing handlers?
void Main()
{
IObservable<Foo> foos = new [] { new Foo { X = 1 }, new Foo { X = 2 } }.ToObservable();
var watcher = new FooWatcher(SynchronizationContext.Current, new Foo { X = 12 });
watcher.FooChanged += o => o.X.Dump();
foos.Subscribe(watcher.Subject.OnNext);
}
// Define other methods and classes here
//public delegate void FooChangedHandler(Foo foo);
public interface IFooWatcher
{
event Action<Foo> FooChanged;
}
public class Foo {
public int X { get; set; }
}
public class FooWatcher
{
private readonly BehaviorSubject<Foo> m_subject;
public BehaviorSubject<Foo> Subject { get { return m_subject; } }
private readonly IObservable<Foo> m_observable;
public FooWatcher(SynchronizationContext synchronizationContext, Foo initialValue)
{
m_subject = new BehaviorSubject<Foo>(initialValue);
m_observable = m_subject
.DistinctUntilChanged();
}
public event Action<Foo> FooChanged
{
add { m_observable.ToEvent().OnNext += value; }
remove { m_observable.ToEvent().OnNext -= value; }
}
}
Given that you are already mixing the boundaries between reactive and more normal code, you could do a less reactive version. To start simply declare a normal event pattern
public event FooChangedHandler FooChanged;
protected void OnFooChanged(Foo)
{
var temp = FooChanged;
if (temp != null)
{
temp(new FooChangedEventArgs(Foo));
}
}
and then simply connect the observable to it in the constructor
m_Observable.Subscribe(foo => OnFooChanged(foo));
It's not very Rx but it is incredibly simple.

Enforcing a business rule involving multiple items

Let's say that I got a simple todolist:
interface ITodoList
{
ITodoItem Create(title);
IEnumerable<ITodoItem> Items {get;}
}
interface ITodoITem
{
void StartTrackTime();
void StopTrackTime();
}
Now I want to enforce so that time is only tracked for one item at a time (per user).
Should I create a domain event like ItemTimeTrackingStarted that StartTrackTime generates. The event would be picked up by a ITodoService which checks if there are any other time tracked items for the current user (and stop them). Or are there a better way?
well if you have dependencies between the items, which in the case is the check, my proposal would be to move the track method into the todo list object, and away from item.
So you request a change from the object that holds all todo items, and there you locate the checks as well.
IMO I'd do it like this, I don't know all the details of the context, but for this specific functionality here it goes
public interface ITrackTime
{
void StartTrackTime();
void StopTrackTime();
}
public interface ITodoItem
{
int Id {get;}
//other stuff
}
public TodoItem:ITodoITem, ITrackTime {}
public class TodoList:ITodoList,ITrackItem
{
ITodoItem Create(title)
{
//create item and add it to collection
}
TodoItem _currentlyTracking;
void StartTrackTime(int itemId)
{
if (_currentlyTracking == null)
{
// getItem and call method for item ..
item.StartTrackTime();
_currentlyTracking=item;
}
else{
//get item and check to see if it is the same id
//throw exception if it is not, ignore it if it is
}
}
}
var list = new TodoList();
ITodoItem item= list.Create("titel");
list.StartTrackingTime(item.Id);
list.StartTrackingTime(otherId); //should throw or whatever handling
Everything is contained within the AR (TodoList). One again, this is a rough draft as I'm not fully aware about the context and the domain.
As stated, the ToDoList should enforce the constraint since the constraint is defined at the ToDoList level. (Unless it is defined at the user level as you indicated in which case the responsibility would shift there). You can leave the method on the item, but it can reference the parent todo list. The code could look like this:
public class ToDoList
{
public IList<ToDoListItem> Items { get; private set; }
// factory method creates items as required by ToDoList
public ToDoListItem Create(string title)
{
var item = new ToDoListItem(this, title);
this.Items.Add(item);
return item;
}
ToDoListItem currentItem;
public void StartTrackTimeFor(ToDoListItem item)
{
if (this.currentItem != null)
throw new Exception();
// could also throw different exception if specified item is current item being tracked
// start time tracking logic.
this.currentItem = item;
}
public void StopTrackTimeFor(ToDoListItem item)
{
if (this.currentItem != item)
throw new Exception();
// stop time tracking logic.
this.currentItem = null;
}
}
public class ToDoListItem
{
public ToDoListItem(ToDoList list, string title)
{
this.ToDoList = list;
this.Title = title;
}
public ToDoList ToDoList { get; private set; }
public string Title { get; private set; }
public void StartTrackTime()
{
this.ToDoList.StartTrackTimeFor(this);
}
public void StopTrackTime()
{
this.ToDoList.StopTrackTimeFor(this);
}
}

Delegates as Properties: Bad Idea?

Consider the following control (snipped for brevity):
public partial class ConfigurationManagerControl : UserControl
{
public Func<string, bool> CanEdit { get; set;}
public Func<string, bool> CanDelete { get; set; }
public Dictionary<string, string> Settings
{
get { return InnerSettings; }
set
{
InnerSettings = value;
BindData();
}
}
private Dictionary<string, string> InnerSettings;
private void OnListIndexChanged(object sender, EventArgs e)
{
this.EditButton.Enabled = false;
this.DeleteButton.Enabled = false;
var indices = this.List.SelectedIndices;
if (indices.Count != 1)
{
return;
}
var index = indices[0];
var item = this.List.Items[index];
if (this.CanEdit != null)
{
this.EditButton.Enabled = this.CanEdit(item.Text);
}
if (this.CanDelete != null)
{
this.DeleteButton.Enabled = this.CanDelete(item.Text);
}
}
}
There's more to this control, but suffice it to say that it allows a user to add, edit, and delete the entries in a Dictionary<string, string>. In order to determine whether or not it should allow the user to edit or delete the entries, it uses the delegate method properties, CanDelete and CanEdit, which are provided by the form or control that hosts it:
public class SetupWizard : Form
{
public SetupWizard()
{
InitializeComponent();
this.SettingManager.CanEdit = CanEditSetting;
this.SettingManager.CanDelete = CanDeleteSetting;
}
private static bool CanEditSetting(string item)
{
var lockedSettings = new[] { "LicenseHash", "ProductHash" };
return !lockedSettings.Contains(item.ToLower());
}
private static bool CanDeleteSetting(string item)
{
var lockedSettings = new[] {
"LicenseHash",
"ProductHash",
"UserName",
"CompanyName"
};
return !lockedSettings.Contains(item.ToLower());
}
}
I find that this design is both satisfactory and worrisome at the same time. On the one hand, it seems to solve the problem using the simplest solution that works (it certainly separates the concerns nicely). On the other hand, I have this nagging concern that I am using delegates improperly and should be using an event, instead (even though I do not need multiple listeners, and only need the caller to tell me if the item is editable).
And then, on the other other hand, there's the chance that there's a completely different design that I haven't even considered that might solve the problem in a vastly superior way.
So. Is this design technically correct, maintainable, and flexible? Or should I be doing something better?
I suggest the use of an interface with these two methods. That's a lot cleaner:
interface ICantThinkOfAGoodName
{
bool CanEdit(string item);
bool CanDelete(string item);
}
You could create something similar to the RelayCommand used in many MVVM frameworks:
public class RelayObject : ICantThinkOfAGoodName
{
public RelayObject() : this(null, null) {}
public RelayObject(Func<string, bool> canEdit, Func<string, bool> canDelete)
{
if(canEdit == null) canEdit = s => true;
if(canDelete == null) canDelete = s => true;
_canEdit = canEdit;
_canDelete = canDelete;
}
public bool CanEdit(string item)
{
return _canEdit(item);
}
public bool CanDelete(string item)
{
return _canDelete(item);
}
}
Use it like this:
public SetupWizard()
{
InitializeComponent();
this.SettingManager.PropertyName = new RelayObject(CanEditSetting,
CanDeleteSetting);
// or (all can be deleted)
this.SettingManager.PropertyName = new RelayObject(CanEditSetting, null);
// or (all can be edited)
this.SettingManager.PropertyName = new RelayObject(null, CanDeleteSetting);
// or (all can be edited and deleted)
this.SettingManager.PropertyName = new RelayObject();
}
BTW: I am using Property injection here, because it is a control. Normally, I would pass the ICantThinkOfAGoodName dependency in the constructor of the ConfigurationManagerControl.
It may be this is what #Daniel Hilgarth is suggesting when he says "use an interface" (n.b. - his answer now reflects a more general/flexible approach to implementing the interface). Instead of assigning delegates to your method directly, why not give the control a property, such as DataState or whatever you want to call it, using an interface that encapsulates the information you need, and leave it up to the owner to decide how to implement that.
interface IDataState
{
bool CanEdit(string item);
bool CanDelete(string item);
}
public partial class ConfigurationManagerControl : UserControl
{
public IDataState DataState {get;set;}
// your code checks DataState.CanEdit & DataState.CanDelete
}
public class SetupWizard : Form, IDataState
{
public SetupWizard()
{
InitializeComponent();
SettingManager.DataState =this;
}
public bool CanEdit(string item)
{
... implement directly or return from your private function
}
public bool CanDelete(string item)
{
}
}
But this gives you the flexibility to implement that interface any way you choose, with another object, etc. and it makes it easy to also just pass the owner itself (implementing the interface).

Controlling access to an internal collection in c# - Pattern required

This is kind of hard to explain, I hope my English is sufficient:
I have a class "A" which should maintain a list of objects of class "B" (like a private List). A consumer of class "A" should be able to add items to the list. After the items are added to the list, the consumer should not be able to modify them again, left alone that he should not be able to temper with the list itself (add or remove items). But he should be able to enumerate the items in the list and get their values. Is there a pattern for it? How would you do that?
If the question is not clear enough, please let me know.
To prevent editing the list or its items you have to make them immutable, which means you have to return a new instance of an element on every request.
See Eric Lippert's excellent series of "Immutability in C#": http://blogs.msdn.com/ericlippert/archive/tags/Immutability/C_2300_/default.aspx (you have to scroll down a bit)
As many of these answers show, there are many ways to make the collection itself immutable.
It takes more effort to keep the members of the collection immutable. One possibility is to use a facade/proxy (sorry for the lack of brevity):
class B
{
public B(int data)
{
this.data = data;
}
public int data
{
get { return privateData; }
set { privateData = value; }
}
private int privateData;
}
class ProxyB
{
public ProxyB(B b)
{
actual = b;
}
public int data
{
get { return actual.data; }
}
private B actual;
}
class A : IEnumerable<ProxyB>
{
private List<B> bList = new List<B>();
class ProxyEnumerator : IEnumerator<ProxyB>
{
private IEnumerator<B> b_enum;
public ProxyEnumerator(IEnumerator<B> benum)
{
b_enum = benum;
}
public bool MoveNext()
{
return b_enum.MoveNext();
}
public ProxyB Current
{
get { return new ProxyB(b_enum.Current); }
}
Object IEnumerator.Current
{
get { return this.Current; }
}
public void Reset()
{
b_enum.Reset();
}
public void Dispose()
{
b_enum.Dispose();
}
}
public void AddB(B b) { bList.Add(b); }
public IEnumerator<ProxyB> GetEnumerator()
{
return new ProxyEnumerator(bList.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
The downside of this solution is that the caller will be iterating over a collection of ProxyB objects, rather than the B objects they added.
EDIT: Added support for edition contexts. Caller can only add elements inside an edition context. You can aditionally enforce that only one edition context can be created for the lifetime of the instance.
Using encapsulation you can define any set of policies to access the inner private member. The following example is a basic implementation of your requirements:
namespace ConsoleApplication2
{
using System;
using System.Collections.Generic;
using System.Collections;
class B
{
}
interface IEditable
{
void StartEdit();
void StopEdit();
}
class EditContext<T> : IDisposable where T : IEditable
{
private T parent;
public EditContext(T parent)
{
parent.StartEdit();
this.parent = parent;
}
public void Dispose()
{
this.parent.StopEdit();
}
}
class A : IEnumerable<B>, IEditable
{
private List<B> _myList = new List<B>();
private bool editable;
public void Add(B o)
{
if (!editable)
{
throw new NotSupportedException();
}
_myList.Add(o);
}
public EditContext<A> ForEdition()
{
return new EditContext<A>(this);
}
public IEnumerator<B> GetEnumerator()
{
return _myList.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public void StartEdit()
{
this.editable = true;
}
public void StopEdit()
{
this.editable = false;
}
}
class Program
{
static void Main(string[] args)
{
A a = new A();
using (EditContext<A> edit = a.ForEdition())
{
a.Add(new B());
a.Add(new B());
}
foreach (B o in a)
{
Console.WriteLine(o.GetType().ToString());
}
a.Add(new B());
Console.ReadLine();
}
}
}
You basically want to avoid to give away references to the class B items. That's why you should do a copy of the items.
I think this can be solved with the ToArray() method of a List object. You need to create a deep-copy of the list if you want to prevent changes.
Generally speaking: most of the times it is not worthwhile to do a copy to enforce good behaviour, especially when you also write the consumer.
public class MyList<T> : IEnumerable<T>{
public MyList(IEnumerable<T> source){
data.AddRange(source);
}
public IEnumerator<T> GetEnumerator(){
return data.Enumerator();
}
private List<T> data = new List<T>();
}
The downside is that a consumer can modify the items it gets from the Enumerator, a solution is to make deepcopy of the private List<T>.
It wasn't clear whether you also needed the B instances themselves to be immutable once added to the list. You can play a trick here by using a read-only interface for B, and only exposing these through the list.
internal class B : IB
{
private string someData;
public string SomeData
{
get { return someData; }
set { someData = value; }
}
}
public interface IB
{
string SomeData { get; }
}
The simplest that I can think of is return a readonly version of the underlying collection if editing is no longer allowed.
public IList ListOfB
{
get
{
if (_readOnlyMode)
return listOfB.AsReadOnly(); // also use ArrayList.ReadOnly(listOfB);
else
return listOfB;
}
}
Personally though, I would not expose the underlying list to the client and just provide methods for adding, removing, and enumerating the B instances.
Wow, there are some overly complex answers here for a simple problem.
Have a private List<T>
Have an public void AddItem(T item) method - whenever you decide to make that stop working, make it stop working. You could throw an exception or you could just make it fail silently. Depends on what you got going on over there.
Have a public T[] GetItems() method that does return _theList.ToArray()

Categories