Xamarin Forms - Singleton with a List - c#

Can somebody help me implement a singleton with a list from Carting module.
My error:
'Cart' does not contain a definition for 'Add' and the best extension method overload 'SettersExtensions.Add(IList, BindableProperty, object)' requires a receiver of type 'IList'
here's what i have for now
Cart.cs
public sealed class Cart
{
public static Cart Instance { get; } = new Cart();
static Cart() { }
private Cart() { }
public void GetAddedMeals()
{
}
}
QuantityPopUp.xaml.cs
private void btnOK_Clicked(object sender, EventArgs e)
{
Cart.Instance.Add(tempcodeofmenu, int.Parse(entQuantity.Text));
Navigation.PushAsync(new OrderCart());
}
OrderCart.cs
public OrderCart ()
{
InitializeComponent ();
MyCart.ItemsSource = Cart.Instance.GetAddedMeals();
}

You need to return something from:
public ObservableCollection<YourItemClass> GetAddedMeals()
{
... // Fill in the blanks according to your implementation. Return a collection.
}
An ObservableCollection can be useful as a source for your list for monitoring changes to that list.
And then you need to allow this to be added to. Perhaps you meant Cart to have a collection as a base class? That way an "Add" may be implemented that way?
public Cart : ObservableCollection<YourItemClass>
But considering your question I'd avoid that for now and go straight for your Cart class owning an ObservableCollection as a member:
private ObservableCollection<YourItemClass> myCollection;
And implement your own Add class:
public void Add(YourItemClass item)
{
myCollection.Add(item);
}

Related

Save references to generic ObservableCollections with common BaseClass

This problem has been keeping me busy for half a day now and I start to lose my sanity:
I'm using Items for UI Logic stuff. There are "parent" Items, that can contain ObservableCollections of other Items. (Both inherit from the same ItemBase, picture nodes with nodes, sort of recursive)
For not having to recreate Observer logic on each "parent" item class, I wanted to add the functionality to the common baseclass, called ItemBase. The idea is, that the parent can just register its ObservableCollections and the baseclass takes care of the event routing and all. The problem is, that I can't seem to find a way to save a reference to these ObservableCollections (of different types with the same baseclass) for the way that generics work.
Here's the code:
public abstract class ItemBase : ViewModelBase
{
private List<ObservableItemCollection<ItemBase>> _trackedChildItemsList = new List<ObservableItemCollection<ItemBase>>();
public event EventHandler<ItemPropertyChangedEventArgs> ChildItemPropertyChanged;
public event EventHandler<IsDirtyChangedEventArgs> ChildItemIsDirtyChanged;
public override bool IsDirty
{
get { return base.IsDirty || AreAnyChildItemsDirty; }
set { base.IsDirty = value; }
}
private bool AreAnyChildItemsDirty
{
get
{
return _trackedChildItemsList.Any(i => i.Any(l => l.IsDirty));
}
}
protected void RegisterItemCollection<T>(ObservableItemCollection<T> collection)
where T : ItemBase
{
_trackedChildItemsList.Add(collection); // intellisense underlines 'collection'; cannot convert from 'ObservableItemCollection<T>' to ObservableItemCollection<ItemBase>:
collection.ItemPropertyChanged += Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged += Collection_ItemIsDirtyChanged;
}
public override void Dispose()
{
foreach (ObservableItemCollection<ItemBase> collection in _trackedChildItemsList)
{
collection.ItemPropertyChanged -= Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged -= Collection_ItemIsDirtyChanged;
}
base.Dispose();
}
private void Collection_ItemPropertyChanged(object sender, ItemPropertyChangedEventArgs e)
{
OnChildItemPropertyChanged(e);
}
protected virtual void OnChildItemPropertyChanged(ItemPropertyChangedEventArgs e)
{
ChildItemPropertyChanged?.Invoke(this, e);
}
private void Collection_ItemIsDirtyChanged(object sender, IsDirtyChangedEventArgs e)
{
OnItemIsDirtyChanged(e);
}
protected virtual void OnItemIsDirtyChanged(IsDirtyChangedEventArgs e)
{
ChildItemIsDirtyChanged?.Invoke(this, e);
}
}
As you can see, I'm using a derived, custom type of the ObservableCollection, namely ObservableItemCollection, which takes care of the ItemPropertyChanged and ItemIsDirtyChanged invokation for the collection itself. This allows one to catch those events from the outside.
Now, instead of having that 'catching the events' logic in each parent item itself (duplicated), I wanted it to be in a centralized spot, namely the baseclass.
Now the main problem is, that upon registering the ObservableItemCollections, I cannot possibly keep a reference to them since there's no common base. ObservableItemCollection<CustomItem> does not inherit from ObservableItemCollection<ItemBase>, since its a collection. I tried solving the whole thing with generics, however, the above is as far as I got. It fails to compile where i wrote the 'cannot convert from 'ObservableItemCollection' to ObservableItemCollection' comment.
I understand why it fails to compile, however, I can't seem to find a workaround/working solution.
I absolutely need a direct reference to the collections (casted as my custom type ObservableItemCollection), else the whole thingy won't work. You can see in the code that I'm accessing both the events of the collection itself, as well as properties of the ItemBase.
Either way, I can't seem to find a common base for the collections. I tried using dynamics and reflection based casting, Interfaces, a Custom generic ParentItem type, neither worked (i might have overlooked something) and even if it did, it would be rather ugly.
Is it really not possible to achieve what I want with a limited amount of hacking things together? I can't believe that I didn't find a good solution after all the time I've invested in this.
Additional info:
In the parent item i have the following ObservableCollections:
public ObservableItemCollection<SomeItem1> Collection1 { get; set; } = new ObservableItemCollection<SomeItem1>();
public ObservableItemCollection<SomeItem2> Collection2 { get; set; } = new ObservableItemCollection<SomeItem2>();
Where both item types inherit from ItemBase. Then i call the base method RegisterItemCollection in the parent item constructor like so:
RegisterItemCollection(Collection1);
RegisterItemCollection(Collection2);
WPF collection controls have the same problem: How do you define a property which can hold a reference to any kind of generic collection? Answer: Make the property a reference to a non-generic interface that all the collections implement. This is a very general question, and it's the reason why non-generic System.Collections.IEnumerable and System.Collections.IList are still in heavy use throughout the .NET framework, all these years after generics were introduced.
Nothing you're doing in RegisterItemCollection(), IsDirty, or Dispose() needs to care about the type of item in the collection. So take whatever methods and properties you need that code to interact with, and put it all in a non-generic interface or base class. Your base class is already generic (ObservableCollection<T>, I presume), so use an interface.
public interface IObservableItemCollection
{
event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
event EventHandler<IsDirtyChangedEventArgs> ItemIsDirtyChanged;
bool IsDirty { get; }
}
public interface IDirtyable
{
// I'm pretty sure you'll want this event here, and I think you'll want your collection to
// implement IDirtyable too.
//event EventHandler<IsDirtyChangedEventArgs> IsDirtyChanged;
bool IsDirty { get; }
}
public class ObservableItemCollection<T>
: ObservableCollection<T>, IObservableItemCollection
where T : IDirtyable
{
public bool IsDirty => this.Any(item => item.IsDirty);
public event EventHandler<ItemPropertyChangedEventArgs> ItemPropertyChanged;
public event EventHandler<IsDirtyChangedEventArgs> ItemIsDirtyChanged;
}
public class ViewModelBase : IDisposable, IDirtyable
{
public virtual bool IsDirty => true;
public virtual void Dispose()
{
}
}
public class ItemBase : ViewModelBase
{
private List<IObservableItemCollection> _trackedChildItemsList = new List<IObservableItemCollection>();
public override bool IsDirty
{
get
{
return base.IsDirty || _trackedChildItemsList.Any(coll => coll.IsDirty);
}
}
protected void RegisterItemCollection<T>(ObservableItemCollection<T> collection)
where T : ItemBase
{
_trackedChildItemsList.Add(collection);
collection.ItemPropertyChanged += Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged += Collection_ItemIsDirtyChanged;
}
public override void Dispose()
{
foreach (IObservableItemCollection collection in _trackedChildItemsList)
{
collection.ItemPropertyChanged -= Collection_ItemPropertyChanged;
collection.ItemIsDirtyChanged -= Collection_ItemIsDirtyChanged;
}
base.Dispose();
}
private void Collection_ItemIsDirtyChanged(object sender, IsDirtyChangedEventArgs e)
{
}
private void Collection_ItemPropertyChanged(object sender, ItemPropertyChangedEventArgs e)
{
}
}
public class ItemPropertyChangedEventArgs : EventArgs
{
}
public class IsDirtyChangedEventArgs : EventArgs
{
}
You could also do this by making _trackedChildItemsList a collection of IDisposable, and have the collections clear their own event handlers, but a class clearing its own event handlers is pretty gruesome. Shun reflection when conventional OOP can be used to do the job in a readable and maintainable way. And you'd still have to think of something for IsDirty.
You can not do this since if you could you could do something like
class A {}
class B : A { }
class C : A { }
var list = new List<List<A>>();
var sublist_b = new List<B>();
sublist_b.Add(new B());
list.Add(sublist_b);
var sublist = list.Single();
sublist.Add(new C()); // <- now a List<B> contains an object that ist not if type B or derived B
I would suggest that you only use ObservableItemCollection<ItemBase> to hold your objects.

Stop use of Add and Remove methods in subclass on the abstract class' List property

I have an abstract class with a int List field and property I want to control because it needs to always be sorted.
public abstract partial class FindAndReplace : Form {
private readonly List<int> _columnsToSearch = new List<int>();
public List<int> ColumnsToSearch { get { return _columnsToSearch; } }
public void AddToColumnsToSearch(int intToAdd) {
_columnsToSearch.Add(intToAdd);
_columnsToSearch.Sort();
}
protected abstract void ColumnCheckBox_CheckChanged(object sender, EventArgs e);
...
}
Here is the subclass of FindAndReplace. I want to force it to use FindAndReplace's method AddToColumnsToSearch, but in the subclass, the Add method on ColumnsToSearch is still available. I was under the impression that making _columnsToSearch readonly and only giving the get method on ColumnsToSearch would hide away any methods like Add and Remove on ColumnsToSearch.
public partial class StepsTableFindAndReplace : FindAndReplace {
protected override void ColumnCheckBox_CheckChanged(object sender, EventArgs e) {
CheckBox cb = sender as CheckBox;
//get the columnIndex (removed the actual "get" code to keep simpler)
int colIndex = 0;
//can still use Add and Remove, want it to be
//AddToColumnsToSearch(colIndex) and RemoveFromColumnsToSearch(colIndex)
if (cb.Checked) ColumnsToSearch.Add(colIndex);
else ColumnsToSearch.Remove(colIndex);
}
...
}
Is there something I'm doing wrong/not understanding?
The answer for this question gave me this solution (that isn't working)... C# get and set properties for a List Collection
The readonly modifier only prevents reassignment, and using a ReadOnlyCollection<T> does not do what you want as you want to alter it at runtime.
You can instead change the the private List<int> to a protected SortedSet<int>, which sorts itself upon modification:
public abstract partial class FindAndReplace : Form
{
protected readonly SortedSet<int> _columnsToSearch = new SortedSet<int>();
// ...
Then you can access it from the derived class directly:
if (cb.Checked)
{
_columnsToSearch.Add(colIndex);
}
else
{
_columnsToSearch.Remove(colIndex);
}
And then return a new collection upon getter access, for example through ToArray():
public IEnumerable<int> ColumnsToSearch
{
get
{
return _columnsToSearch.ToArray();
}
}
Note that the latter creates a disconnected scenario: accessing ColumnsToSearch gives you a copy of _columnsToSearch at that moment, contrary to a ReadOnlyCollection, which is a wrapper over the List<T>.
Try exposing ReadOnlyCollection<T> instead of List<T>.
The readonly modifier does not make collections read-only; it just prevents setting the field outside the constructor. The public property should be exposed as IReadOnlyCollection<int> and the backing field should be made protected to allow subclasses to modify the list.

Xamarin ObjC binding callback not called

I have a Xamarin-Objective C binding project (see here: https://github.com/bbhsu2/XamarinAdMarvelBinding). Fundamentally, it works and I can load clickable ads.
So I implement the AdMarvelDelegate interface on my ViewController class, but important implemented methods are not getting called:
public class CategoryViewController : UITableViewController, IAdMarvelDelegate
{
/*Initializing stuff*/
public void GetAdSucceeded() //not called
{
Console.WriteLine("succeeded!");
}
public void GetAdFailed() //not called
{
Console.WriteLine("failed!");
AppDelegate.Shared.AddAdBanner();
}
}
Does anyone have any suggestions on why GetAdSucceeded and GetAdFailed are not called? In the binding project I have:
[Export("getAdSucceeded")]
void GetAdSucceeded();
[Export("getAdFailed")]
void GetAdFailed();
which I believe are correct
If the corresponding members in AdMarvelDelegate are optional (i.e. they don't have the [Abstract] attribute in the binding), you'll need the [Export] attribute on those methods:
public class CategoryViewController : UITableViewController, IAdMarvelDelegate
{
[Export ("getAdSucceeded")]
public void GetAdSucceeded()
{
Console.WriteLine("succeeded!");
}
[Export("getAdFailed")]
public void GetAdFailed()
{
Console.WriteLine("failed!");
AppDelegate.Shared.AddAdBanner();
}
}

Log manager class accessible to all classes

I've been struggling with this for a while... I have a programm written using the MVP pattern, I want to have a LogHandler class that must retrieve a string that corresponds to an ID provided in one of these methods, but it also needs to update the GUI, adding items to a listbox. So to simplyfy, imagine this:
if (name != "Peter")
{
Log.RegisterError(31, 4) //errorType, errorID
}
So in the Log class it would then get the string that matches the type and IDs provided and MessageBox it, but what if I want to add that string to a control on the form? I'm using views implemented by the forms to accomplish GUI updating, but since this is a static class I can't...
Also where should errors be checked and raised? Presenter? View? Model?
Thanks in advance
You could add callbacks in you Log class that other object could subscribe to.
Example:
In this example the Presenter can listen for an error code to be logged then receive the error string from the Log from the Model class
public class Logger
{
private static Dictionary<int, List<Action<string>>> _callbacks = new Dictionary<int,List<Action<string>>>();
public static void RegisterLoggerCallback(int errorType, Action<string> callback)
{
// Just using errortype in this exaple, but the key can be anything you want.
if (!_callbacks.ContainsKey(errorType))
{
_callbacks.Add(errorType, new List<Action<string>>());
}
_callbacks[errorType].Add(callback);
}
public static void RegisterLog(int errorType, int errorID)
{
// find error sring with codes
string error = "MyError";
// show messagebox
MessageBox.Show(error);
// tell listeners
if (_callbacks.ContainsKey(errorType))
{
_callbacks[errorType].ForEach(a => a(error));
}
}
}
public class Model
{
public Model()
{
}
public void DoSomething()
{
Logger.RegisterLog(1, 2);
}
}
public class Presenter
{
public Presenter()
{
Logger.RegisterLoggerCallback(1, AddToListbox);
}
private void AddToListbox(string error)
{
// add to listbox when errortype 1 is called somewhere
}
}
This is a very simple example but should give you an idea of a way to achive this.

C# Events handling problem for base and derived classes

I have this case where I'm creating 2 different event handlers placed in a base class and subscribing to them accordingly from Quotes and Charts classes. Problem I'm having is that the first subscription triggers fine for the first event but any following subscriptions don't get executed. I have included an example of 2 different handlers, Quotes and Charts, Quotes executes first time with no problems, but Charts does not trigger when data is received.
Base Class:
public abstract class MyBaseClass
{
protected virtual void RaiseOnQuoteData(string item) { }
protected virtual void RaiseOnChartData(string item) { }
void OnDataReceived(object sender, DataEventArgs e)
{
if (e.Item == "QUOTE")
RaiseOnQuoteData(e.Item);
else if (e.Item == "CHART")
RaiseOnChartData(e.Item);
}
}
Quote and Chart Classes:
public class Quote : MyBaseClass
{
public event EventHandler<DataEventArgs<quoteRecord>> OnQuoteData;
protected override void RaiseOnQuoteData(string item)
{
OnQuoteData.Raise<DataEventArgs<quoteRecord>>(this, new DataEventArgs<quoteRecord>(item));
}
}
public class Chart : MyBaseClass
{
public event EventHandler<DataEventArgs<chartRecord>> OnChartData;
protected override void RaiseOnChartData(string item)
{
OnChartData.Raise<DataEventArgs<chartRecord>>(this, new DataEventArgs<chartRecord>(item));
}
}
Subscription:
public class QuoteSubscription
{
public static void SubscribetoQuoteData()
{
Quote Q = new Quote();
Q.OnQuoteData += new EventHandler<DataEventArgs<quoteRecord>>(q_OnQuoteData);
}
static void q_OnQuoteData()
{
//Executes fine
}
}
public class ChartSubscription
{
public static void SubscribetoChartData()
{
Chart C = new Chart();
C.OnChartData += new EventHandler<DataEventArgs<chartRecord>>(q_OnChartData);
}
static void q_OnChartData()
{
//Does not execute
}
}
This is implemented in ASP.NET 4.0, Is there any chance that instantiating the derived classes could be the problem since both classes do share the same base class? Any help pointing to the cause would be greatly appreciated.
What is there in Raise? This must be an extension method, since EventHandler per se doesn't define such a method. Therefore, you can put a breakpoint inside and see, what's going on. (And you could perhaps put a breakpoint inside RaiseOnChartData as well.)
Could it be that you are creating the object within the method scope and has gone out of scope. You may get the first message by coincidence just because it hasn't been GC-ed. Try creating the quote and chart objects as static class member object

Categories