How to call method when Collection has given count of items - c#

I would like to invoke a method when into my List property were added specific count of items. Actually I need to find way how to automatically check if a Collection has given count of items and after that call a method.
Ok, one example. I have a class MyClass
public class MyClass
{
public List<string> Url { get; set; }
public void fullList()
{
//some work with the List
}
}
And now somewhere in main function I create instance of MyClass and add some items.
MyClass mc = new MyClass();
mc.Url = new List<string>();
mc.Url.Add("www.1.com");
mc.Url.Add("www.2.com");
//now on 3rd added item I want to invoke method fullList from class MyClass
mc.Url.Add("www.3.com");

You should probably create have a class that encapsulates the URL list and the behavior you want. I guess the behavior is that the list gets "full" when it hits 3 items and then you want an event to be raised that MyClass can listen on.
class MyUrls
{
List<string> _urls = new List<string>();
public void Add(string url)
{
_urls.Add(url);
if (_urls.Count == 3 && OnFull != null)
OnFull.Invoke();
}
public IEnumerable<string> Urls
{
get
{
return _urls;
}
}
public event Action OnFull;
}
and use it like this:
public class MyClass
{
public MyClass()
{
Urls.OnFull += fullList;
}
public MyUrls Urls { get; }
public void fullList()
{
//some work with the List
}
}
MyClass mc = new MyClass();
mc.Urls = new List<string>();
mc.Urls.Add("www.1.com");
mc.Urls.Add("www.2.com");
//now on 3rd added item I want to invoke method fullList from class MyClass
mc.Urls.Add("www.3.com");

Don't reinvent the wheel - use the ObservableCollection<T>.
You can attach a handler to the CollectionChanged event that gets called when the collection is modified.
ObservableCollection<string> collection = new ObservableCollection<string>();
collection.CollectionChanged += (sender, args) => {
if(args.Action == NotifyCollectionChangedAction.Add &&
collection.Count == 3)
HandleItemsAdded(args.NewItems.Cast<string>());
};
collection.Add("www.1.com");
collection.Add("www.2.com");
collection.Add("www.3.com");
Handler:
public static void HandleItemsAdded(IEnumerable<string> newItems)
{
foreach(var item in newItems)
Console.WriteLine(item);
}
Working Fiddle: http://dotnetfiddle.net/eIeilP

Related

INotifyPropertyChanged updating properties that refer to it via Linq

I have a collection of classes contained in a ObservibaleCollection<MyObj> and MyObj implements INotifyPropertyChanged, but I need a property located outside of it that references a property in the collection via linq and creates its own collection to update on both the collection change and any of its content linq bound properties changing.
For sake of argument and simplicity lets say my class MyObj contains a property called IsVisible. I want a property that implements its own INotifyPropertyChanged to get a list of MyObj where IsVisible == true and keep it up to date regardless id the collection of MyObj changes or the IsVisible property does.
Is this possible without attaching to the collection changed event and subsequently just directly attaching to each child MyObj.IsVisible property? Is there a way to get INotify to bubble up through linq?
public class MyObj:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public bool IsVisible
{
get { return _IsVisible; }
protected set { if (value != _IsVisible) { _IsVisible= value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsVisible")); } }
}
private bool _IsVisible;
}
public class Foo
{
ObservableCollection<MyObj> myObjs = new ObservableCollection<MyObj>();
ObservableCollection<MyObj> myVisibleObjs {
get{
return myObjs.where(o => o.IsVisible);
}
}
}
I hope what I'm asking makes sense.
You could make use of Reactive Extensions, but for this specific requirement - maintaining a myVisibleObjs - I would use the dynamic data lib.
Try out the following:
static void Main(string[] args)
{
Foo f = new Foo();
f.BindToVisibleObjects();
// add more dummy data
f.Add(false);
f.Add(true);
// There will be 2 visible objects in MyVisibleObjects
foreach (var d in f.MyVisibleObjects)
{
Console.WriteLine(d.IsVisible);
}
Console.ReadKey();
}
public class Foo
{
ObservableCollection<MyObj> myObjs = new ObservableCollection<MyObj>();
public ReadOnlyObservableCollection<MyObj> MyVisibleObjects;
public Foo()
{
// add some dummy data
myObjs.Add(new MyObj() { IsVisible = true });
myObjs.Add(new MyObj() { IsVisible = false });
}
public void BindToVisibleObjects()
{
myObjs.ToObservableChangeSet()
.Filter(o => o.IsVisible)
.Bind(out MyVisibleObjects)
.DisposeMany()
.Subscribe();
}
public void Add(bool vis)
{
myObjs.Add(new MyObj() { IsVisible = vis });
}
}
The key here is that we bind the filtered observable changeset to a new collection that will be updated as your myObjs changes.

How to make a readonly C# List by non declaring its set attribute

I have a List<string> myList in my class that I want to be readonly for the class users.
List<strign> myList {get;}
private void SetListValue()
{
myList = new List<string>();
myList.Add("ss");
}
Like this I thought I could set the myList value inside my class in my private members and make it readonly for the class users. But I realised that declaring it that way I'm unable to set any value.
Try:
public List<string> myList {get; private set;}
This will let you set it inside your class, but not outside. Note that this will not stop external clients updating your list, only the reference to it.
A private setter for a List, Collection, etc. means that the entire list cannot be replaced by consumers, but it does nothing to protect the public members of the list.
For example:
public class MyClass
{
public IList<string> MyList {get; private set;}
public MyClass()
{
MyList = new List<string>(){"One","Two", "Three"};
}
}
public class Consumer
{
public void DoSomething()
{
MyClass myClass = new MyClass();
myClass.MyList = new List<string>(); // This would not be allowed,
// due to the private setter
myClass.MyList.Add("new string"); // This would be allowed, because it's
// calling a method on the existing
// list--not replacing the list itself
}
}
In order to prevent consumers from altering the members of the list you could expose it as a Read-only interface, such as IEnumerable<string>, ReadOnlyCollection<string>, or by calling List.AsReadOnly() within the declaring class.
public class MyClass
{
public IList<string> MyList {get; private set;}
public MyClass()
{
MyList = new List<string>(){"One","Two", "Three"}.AsReadOnly();
}
}
public class Consumer
{
public void DoSomething()
{
MyClass myClass = new MyClass();
myClass.MyList = new List<string>(); // This would not be allowed,
// due to the private setter
myClass.MyList.Add("new string"); // This would not be allowed, the
// ReadOnlyCollection<string> would throw
// a NotSupportedException
}
}
You want a public property with a private setter. As below.
public List<string> myList { get; private set; }

Casting List<A> to List<B> without making a new copy

Please observe the following code:
public class MyClass
{
List<object> _List = new List<object>();
public List<object> Objects { get { return _List; } }
public List<string> Strings { get { return _List.Cast<string>().ToList(); } }
public void Test()
{
Objects.Add ("Hello");
// actual contents
// Objects = object[] { "Hello" }
// Strings = string[] { "Hello" }
Strings.Add ("World");
// actual contents
// Objects = object[] { "Hello" }
// Strings = string[] { "Hello" }
// expected contents
// Objects = object[] { "Hello", "World" }
// Strings = string[] { "Hello", "World" }
}
}
The code shows a class with a list of objects. The two properties expose that list as List<object> and List<string> respectively. However, since _List.Cast<string>().ToList() creates a COPY of the actual list, the line Strings.Add ("World") doesn't affect the actual list. Is there a way to ensure a CASTED REFERENCE to the actual list is returned in the Strings property instead of a CASTED COPY of the actual list?
Note: The code can fail when an integer is added to Objects and then accessed from Strings but that's not what I'm worried about at the moment.
EDIT
The original question was as follows. It was changed to avoid confusion and simplify the problem at hand. Observe the following code:
public abstract class Foo
{
List<object> _List = new List<object>();
public List<object> ListObject { get { return _List; } }
}
public class Bar : Foo
{
public List<string> ListString
{
get { return ListObject.Cast<string>().ToList(); }
}
}
Bar oBar = new Bar();
Foo oFoo = oBar;
oFoo.ListObject.Add("Item");
// oFoo.ListObject= { "Item" }
// oBar.ListString = { "Item" }
oBar.ListString.Add("NewItem");
// oFoo.ListObject= { "Item" }
// oBar.ListString = { "Item" }
As you can see, using the base class object works fine (an item is added to the internal list), but using the derived class object does not work. I know the reason is because casting a List to List actually creates a new copy of the list and returns that instead. I'm wondering if it is possible to write these classes such that it works both ways.
You can try something like this:
public abstract class Foo<T>
{
List<T> _List = new List<T>();
public List<T> ListObject { get { return _List; } }
}
public class Bar : Foo<string>
{
public List<string> ListString
{
get { return ListObject; }
}
}
Result:
I was hoping that someone else would come up with a reasonably good answer to this, but the reality is that there's probably not a good answer to this one.
However, there are several ways to skin your average cat, many of them pretty ugly.
One ugly solution to this problem is to implement a list class that encapsulates n List<object> object and attempts to access the objects as whatever type you choose. This type of proxy class can be awkward to get right, but might be a way to do what you're trying to do.
public class StringObjectList : IList<string>
{
private List<object> _list;
public StringObjectList(List<object> src)
{
_list = src;
}
// IList Implementation...
public string this[int index]
{
get
{
object obj = _list[index];
if (obj == null)
return null;
return obj.ToString();
}
set
{
_list[index] = value;
}
}
// ... plus 3 more IList<string> methods (IndexOf, Insert, RemoveAt)
// ICollection<string> implementation (5 methods, 2 properties)
// IEnumerable<string> implementation (1 method)
// IEnumerable implementation (1 method)
}
Some of the implementation details are a little tricky. Mostly though the implementations are simple proxy methods, since the underlying list is happy to accept strings as well as any other object. The ICollection<string>.Add method for instance can be as simple as:
public void Add(string item)
{
_list.Add(item);
}
Where you might have trouble is with the IEnumerable<string> and IEnumerable implementations, which might require you to create a couple of supporting classes.
Not simple, not elegant, but potentially doable.
If you don't like the generic solution above, you could make the List member abstract.
public abstract class Foo
{
public abstract IList ListObject { get; }
}
public class Bar : Foo
{
public override IList ListObject
{
get { return new List<string>(); }
}
}
public abstract class Foo<T>
{
public abstract IList<T> MyList { get; }
// you can manipulate MyList in this class even if it is defined in inherited class
}
public class Bar : Foo<string>
{
private readonly IList<string> _myList = new List<string>();
public override IList<string> MyList
{
get { return _myList; }
}
}
[TestFixture]
public class TestFixture1
{
[Test]
public void Test()
{
Bar oBar = new Bar();
Foo<string> oFoo = oBar;
oFoo.MyList.Add("Item");
// oFoo.ListObject= { "Item" }
// oBar.ListString = { "Item" }
oBar.MyList.Add("NewItem");
// oFoo.ListObject= { "Item" }
// oBar.ListString = { "Item" }
}
}

Recursive Lookup of generic List C# using LINQ

I have generic list of a custom class which has 2 properties. These properties store the raltionship between on form and another form. The list is structured as a hierachical list (parent / child ) relationship. What I want to be able to do is get a list of all forms based on a parent form reference which will be passed to a function. I am thinking the best way to go about this is with a LINQ query using a recursive approach. I am sure someone can point me in the right direction
This is the class used in the list
class FormStack {
#region Declares
private Form _form;
private Form _parent;
#endregion
#region Constructor
static FormStack()
{
}
#endregion
public Form Form
{
get { return _form; }
set { _form = value; }
}
public Form Parent
{
get { return _parent; }
set { _parent = value; }
}
}
So I would like to be able to Call a method and pass a form reference to the function and get all the children form related to this parent.
This is some of the code I have been stumbling with
// create a lookup list
var list = formStack.ToLookup( p => object.ReferenceEquals( p.Parent, parent ) );
// create a function
Func<IEnumerable<Form>, IEnumerable<Form>> recurse = null;
recurse = qs =>
{
return
qs
.Concat(
from q in qs
from q2 in recurse( list[] )
select q2 );
};
// filter the list
var children = recurse( list[parent] ).ToList();
I have a winform application which has standard CRUD functionality. Lets say there is a list of customers and each customer can have multiple address and each one of these addresses have multiple buildings, the way I have structured the forms is there is a list of customers, from this list you can open a detail form for a particular customer. This form has the details of the customer and all the addresses in a list. This list allows the user to now selected an address in the list and open the address details form which has a list of buildings and so on.... My problem is I want to close the customer detail and all the related forms for this customer. My idea was to keep of the relationship between the forms, but maybe I there is a better way???
here is what I made up:
Create a base Form for all your Forms:
public class MyFormBase : Form
{
public MyFormBase()
{
FormRepository.RegisterForm(this);
}
public MyFormBase(MyFormBase parent)
: this()
{
Parent = parent;
}
public MyFormBase Parent { get; set; }
}
Each Form can only have one Parent that is passed in the constuctor.
Create a Repository (or something simiar) to store your forms -> I do not want to store all children in the Form itself
//infrastructure - simulated with a static class
public static class FormRepository
{
private static List<MyFormBase> _allForms = new List<MyFormBase>();
public static void RegisterForm(MyFormBase form)
{
_allForms.Add(form);
}
public static void CloseFormAndChildren(MyFormBase form)
{
_allForms.Where(x => x.Parent.Equals(form)).ToList().ForEach(x => CloseFormAndChildren(x));
form.Close();
}
}
Call CloseFormAndChildren on any form you want to close (including the children). This could be called in the closing event...
Okay, it sounds to me like you have two issues. One is a syntax error (recurse(list[]) is wrong), but the other is that your FormStack isn't really a stack. It's just two forms, with no way to create a recursive chain. I think you want this:
public class FormStack : IEnumerable<Form> // or just implement SelectMany
{
public Form Form { get; set; }
public FormStack Parent { get; set; }
//implement IEnumerable<Form> or the SelectMany method
}
Then I think you can just do this, but it seems like an awkward thing to do:
Func<FormStack, IEnumerable<Form>> recurse = qs =>
{
return from q in qs
select (new[] { qs.Form }).Concat(recurse(qs.Parent));
};
var list = recurse(formStack).ToList();
That's if you're insisting on the query syntax.
If I were you, though, I'd forget all that and implement an IEnumerator<Form> to do it all for you:
public class FormStack : IEnumerable<Form>
{
public Form Form { get; set; }
public FormStack Parent { get; set; }
public IEnumerator IEnumerable:GetEnumerator()
{
return (IEnumerator)GetEnumerator();
}
public IEnumerator<Form> GetEnumerator()
{
return new FormStackEnumerator(this);
}
}
public class FormStackEnumerator : IEnumerator<Form>
{
private FormStack _stack;
private FormStack _first;
public Form Current { get { return _stack.Form; } }
object IEnumerator.Current { get { return Current; } }
public FormStackEnumerator(FormStack stack)
{
_stack = stack;
_first = stack;
}
public bool MoveNext()
{
if (_stack.Parent == null)
{
return false;
}
_stack = _stack.Parent;
return true;
}
public void Reset() { _stack = _first; }
void IDisposable.Dispose() { }
}
Then all you'd need to do in your main code is this:
var list = new List<Form>();
foreach (var node in formStack)
{
list.Add(node.Form);
}
By the way, I just looked up the Form class (I'm not a WinForms developer) and the Forms themselves have a Parent member. So you don't really need to wrap them in a node-type construct; they're already nodes! That makes everything easy:
var list = new List<Form>();
Action<Control> recurse = target =>
{
var form = target as Form;
if (form != null)
{
list.Add(form);
recurse(target.Parent);
}
}

Threading problem when adding items to an ObservableCollection

I'm updating an ObservableCollection of a WPF ViewModel in a WCF Data Service asynchronous query callback method:
ObservableCollection<Ent2> mymodcoll = new ObservableCollection<Ent2>();
...
query.BeginExecute(OnMyQueryComplete, query);
...
private void OnMyQueryComplete(IAsyncResult result)
{
...
var repcoll = query.EndExecute(result);
if (mymodcoll.Any())
{
foreach (Ent c in repcoll)
{
var myItem = mymodcoll.Where(p => p.EntID == c.EntID).FirstOrDefault();
if (myItem != null)
{
myItem.DateAndTime = c.DateAndTime; // here no problems
myItem.Description = c.Description;
...
}
else
{
mymodcoll.Add(new Ent2 //here I get a runtime error
{
EntID = c.EntID,
Description = c.Description,
DateAndTime = c.DateAndTime,
...
});
}
}
}
else
{
foreach (Ent c in repcoll)
{
mymodcoll.Add(new Ent2 //here, on initial filling, there's no error
{
EntID = c.EntID,
Description = c.Description,
DateAndTime = c.DateAndTime,
...
});
}
}
}
The problem is, when a query result collection contains an item which is not present in the target collection and I need to add this item, I get a runtime error: The calling thread cannot access this object because a different thread owns it. (I pointed out this line of code by a comment)
Nevertheless, if the target collection is empty (on initial filling) all items have been added without any problem. (This part of code I also pointed out by a comment). When an item just needs to update some of its fields, there are no problems as well, the item gets updated ok.
How could I fix this issue?
First case: Here you a modifying an object in the collection, not the collection itself - thus the CollectionChanged event isn't fired.
Second case: here you are adding a new element into the collection from a different thread, the CollectionChanged event is fired. This event needs to be executed in the UI thread due to data binding.
I encountered that problem several times already, and the solution isn't pretty (if somebody has a better solution, please tell me!). You'll have to derive from ObservableCollection<T> and pass it a delegate to the BeginInvoke or Invoke method on the GUI thread's dispatcher.
Example:
public class SmartObservableCollection<T> : ObservableCollection<T>
{
[DebuggerStepThrough]
public SmartObservableCollection(Action<Action> dispatchingAction = null)
: base()
{
iSuspendCollectionChangeNotification = false;
if (dispatchingAction != null)
iDispatchingAction = dispatchingAction;
else
iDispatchingAction = a => a();
}
private bool iSuspendCollectionChangeNotification;
private Action<Action> iDispatchingAction;
[DebuggerStepThrough]
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!iSuspendCollectionChangeNotification)
{
using (IDisposable disposeable = this.BlockReentrancy())
{
iDispatchingAction(() =>
{
base.OnCollectionChanged(e);
});
}
}
}
[DebuggerStepThrough]
public void SuspendCollectionChangeNotification()
{
iSuspendCollectionChangeNotification = true;
}
[DebuggerStepThrough]
public void ResumeCollectionChangeNotification()
{
iSuspendCollectionChangeNotification = false;
}
[DebuggerStepThrough]
public void AddRange(IEnumerable<T> items)
{
this.SuspendCollectionChangeNotification();
try
{
foreach (var i in items)
{
base.InsertItem(base.Count, i);
}
}
finally
{
this.ResumeCollectionChangeNotification();
var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
this.OnCollectionChanged(arg);
}
}
}

Categories

Resources