Key-Value Observing not triggering on a UIViewController subclass? - c#

I'm trying to get notified when the title of a UIViewController changes.
I tried adding an observer to the title of a UIViewController subclass but it never gets triggered. What's strange about this, is that it works on a plain UIViewController. Am I doing something wrong?
Here's a code example explaining my issue (Xamarin.iOS C#):
using System;
using UIKit;
using System.Collections.Generic;
namespace ObserverTests
{
public partial class ViewController : UIViewController
{
List<UIViewController> viewControllers = new List<UIViewController>();
public override void ViewDidLoad()
{
UIViewController controller1 = new UIViewController() { Title = "Controller1" };
UIViewController controller2 = new Test() { Title = "Controller2" };
this.viewControllers.Add(controller1);
this.viewControllers.Add(controller2);
foreach(UIViewController viewController in viewControllers)
{
viewController.AddObserver("title", Foundation.NSKeyValueObservingOptions.New, (changes) =>
{
Console.WriteLine(viewController.Title);
Console.WriteLine("Title Changed!");
});
}
controller1.Title = "TitleChanged1"; // Works
controller2.Title = "TitleChanged2"; // Fails
}
private class Test : UIViewController
{
}
}
}

In Xamarin the best way might be using inheritance and adding such a feature. For this you derive from UIViewController
public class UIObserveTitleChangedViewController : UIViewController
{
public event TitleChangedEvent TitleChanged;
public override string Title
{
get
{
return base.Title;
}
set
{
var oldTitle = base.Title;
if (oldTitle == value)
return;
base.Title = value;
OnTitleChanged(new TitleChangedEventArgs(value, oldTitle));
}
}
protected virtual void OnTitleChanged(TitleChangedEventArgs args)
{
TitleChanged?.Invoke(this, args);
}
#region ctor
public UIObserveTitleChangedViewController() { }
public UIObserveTitleChangedViewController(NSCoder coder) : base(coder) { }
protected UIObserveTitleChangedViewController(NSObjectFlag t) : base(t) { }
protected internal UIObserveTitleChangedViewController(IntPtr handle) : base(handle) { }
public UIObserveTitleChangedViewController(string nibName, NSBundle bundle) : base(nibName, bundle) { }
#endregion
}
and implement missing event types
public delegate void TitleChangedEvent(object sender, TitleChangedEventArgs args);
public class TitleChangedEventArgs : EventArgs
{
public string NewTitle { get; set; }
public string OldTitle { get; set; }
public TitleChangedEventArgs(string newTitle, string oldTitle)
{
NewTitle = newTitle;
OldTitle = oldTitle;
}
}
You can then subscribe to this event and get notified of changes
public partial class ViewController : UIObserveTitleChangedViewController
{
public ViewController(IntPtr handle) : base(handle)
{
this.TitleChanged += ViewController_TitleChanged; // Subscribe to TitleChanged
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
Title = "Some title"; // triggers TitleChanged
Title = "Another new title"; // triggers TitleChanged
}
private void ViewController_TitleChanged(object sender, TitleChangedEventArgs args)
{
Debug.WriteLine("Title changed from {0} to {1}", args.OldTitle, args.NewTitle);
}
}

You could set the title like so and it will work:
controller2.SetValueForKey(new NSString("TitleChangedHAHA"), new NSString("title"));

You could do this. First define a new event argument that will hold the new title when it changes.
public class TitleChangedEventArgs: EventArgs
{
public string Title { get; private set; }
public TitleChangedEventArgs(string title)
{
Title = title;
}
}
In your test class, add a new Event Handler for TitleChanged and override Title to raise an event when the new title for the view controller.
public class Test : UIViewController
{
public event EventHandler<TitleChangedEventArgs> TitleChanged;
public override string Title {
get {
return base.Title;
}
set {
base.Title = value;
OnTitleChanged();
}
}
public virtual void OnTitleChanged()
{
if (TitleChanged != null) {
TitleChanged.Invoke(this, EventArgs.Empty);
}
}
}
and finally in your Main View Controller you can do something like this:
public class ViewController : UIViewController
{
private Test _test;
public override void ViewDidLoad()
{
_test = new Test();
base.ViewDidLoad();
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
_test.TitleChanged += Test_TitleChanged;
}
public override void ViewDidDisappear(bool animated)
{
_test.TitleChanged -= Test_TitleChanged;
base.ViewDidDisappear(animated);
}
void Test_TitleChanged(object sender, TitleChangedEventArgs e)
{
Console.WriteLine("Title Changed! " + e.Title);
}
public override void ViewWillDisappear(bool animated)
{
base.ViewWillDisappear(animated);
}
}

The Title property in UIViewController is marked virtual...so as an alternative solution, you could define a BaseViewController class and override Title and call a method in the Setter:
Public class BaseViewController : UIViewController
{
....
public override string Title {
get {
return base.Title;
}
set {
base.Title = value;
OnTitleChanged();
}
}
protected virtual void OnTitleChanged()
{
......
}
}
Then you can override OnTitleChanged on any of your UIViewControllers to have a callback when the Title is changed.

Related

How to call RefreshProcess(SaveEventTriggerModelArgs obj) in Window_Loaded?

I have the following code, I would like to call the function RefreshProcess(SaveEventTriggerModelArgs obj) from MainWindow_Loaded.
However the problem I am running into due to lack of knowledge working with window apps I calling this method inside.
It will not let me because of the arguments SaveEventTriggerModelArgs obj and if I add those into RefreshProcess, they are different from void MainWindow_Loaded(object sender, RoutedEventArgs e). How to do it?
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded+= Window_Loaded;
}
private void RefreshProcess(SaveEventTriggerModelArgs obj)
{
var rect = new Rect();
Dispatcher.Invoke(() =>
{
obj.CurrentEventTriggerModel.ProcessInfo = new ProcessInfo()
{
ProcessName = "Nox" != null ? $"Nox" : "",
Position = rect,
};
});
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
}
public class SaveEventTriggerModelArgs : INotifyEventArgs
{
public Model CurrentEventTriggerModel { get; set; }
}
public class MousePointEventArgs : INotifyEventArgs
{
public ViewModel MousePointViewMode { get; set; }
}
public class ViewModel
{
}
public class Model
{
public ProcessInfo ProcessInfo { get;set;}
}
public class ProcessInfo
{
public string ProcessName { get;set;}
public Rect Position { get;set;}
}

How to use dependency injection for generic interfaces?

How to use dependency injection for generic interfaces? I want the IDrawView interface to be created in DrawPresenter, and it controls the view.
I do not know what to use, Ninject or something else. I am using WinForms.
Which is better to choose?
class Program
{
static void Main(string[] args)
{
IDrawPresenter prisenter = new DrawPresenter(new DrawWindow());
prisenter.Show();
Console.ReadLine();
}
}
public interface IView
{
void Show();
}
public interface IDrawView : IView
{
object GetGridDraw { get; }
}
public interface IPrisenter<TView> where TView : IView
{
void Show();
}
public interface IDrawPresenter : IPrisenter<IDrawView>
{
object SelectedDraws { get; }
}
public class DrawWindow : IDrawView
{
public object GetGridDraw => 1;
public void Show()
{
Console.WriteLine("Show Window");
}
}
public abstract class BasePresenter<TView> : IPrisenter<TView>
where TView : IView
{
protected BasePresenter(TView view)
{
View = view;
}
protected TView View { get; private set; }
public void Show()
{
View.Show();
}
}
public class DrawPresenter : BasePresenter<IDrawView>, IDrawPresenter
{
public DrawPresenter(IDrawView view): base(view)
{
}
public object SelectedDraws => View.GetGridDraw;
}
Can DI implement this?
IDrawPresenter prisenter = new DrawPresenter();
public DrawPresenter()
{
}
What I need to do for Presenter to manage the form.
Here is what I want to get. But this does not work ...
public class NinjectProgram
{
//Gets the inject kernal for the program.
public static IKernel Kernel { get; protected set; }
}
public class DependencyModule : NinjectModule
{
public override void Load()
{
Bind<IDrawView>().To<DrawWindow>();
}
}
static void Main(string[] args)
{
StandardKernel Kernel = new StandardKernel();
Kernel.Load(new DependencyModule());
IDrawPresenter prisenter = new DrawPresenter();
prisenter.Show();
Console.ReadLine();
}
public abstract class BasePresenter<TView> : IPrisenter<TView>
where TView : IView
{
protected BasePresenter()
{
View = NinjectProgram.Kernel.Get<TView>();
}
protected TView View { get; private set; }
public void Show()
{
View.Show();
}
}
Thank you all, that’s what I wanted to do. Perhaps this will help someone in the future.
static void Main(string[] args)
{
CompositionRoot.Wire(new DependencyModule());
IDrawPresenter prisenter = new DrawPresenter();//kernel.Get<IDrawPresenter>();
prisenter.Show();
Console.ReadLine();
}
public class CompositionRoot
{
private static IKernel _ninjectKernel;
public static void Wire(INinjectModule module)
{
_ninjectKernel = new StandardKernel(module);
}
public static T Resolve<T>()
{
return _ninjectKernel.Get<T>();
}
}
public class DependencyModule : NinjectModule
{
public override void Load()
{
Bind<IDrawView>().To<DrawWindow>();
}
}
public abstract class BasePresenter<TView> : IPrisenter<TView>
where TView : IView
{
protected BasePresenter()
{
View = CompositionRoot.Resolve<TView>();//NinjectProgram.Kernel.Get<TView>();
}
protected TView View { get; private set; }
}
Also include the presenter in the container and resolve it.
public class DependencyModule : NinjectModule {
public override void Load() {
Bind<IDrawView>().To<DrawWindow>();
Bind<IDrawPresenter>().To<DrawPresenter>();
}
}
All its dependencies, if registered, will also be resolved and injected into the presenter
static void Main(string[] args) {
var kernel = new StandardKernel();
kernel.Load(new DependencyModule());
IDrawPresenter presenter= kernel.Get<IDrawPresenter>();
presenter.Show();
Console.ReadLine();
}
The above is based on
public abstract class BasePresenter<TView> : IPrisenter<TView> where TView : IView {
protected BasePresenter(TView view) {
View = view;
}
protected TView View { get; private set; }
public void Show() {
View.Show();
}
}
public class DrawPresenter : BasePresenter<IDrawView>, IDrawPresenter {
public DrawPresenter(IDrawView view): base(view) {
}
public object SelectedDraws => View.GetGridDraw;
}

In Xamarin.Forms, how to notify the changes of the same viewmodel back to the previous page? (can pass to the second page, but not back)

I got two pages, "HomePage", "SettingPage", including the same "MyView" (some Pickers there).
When I click "Go Setting"(or show more settings) Button from Homepage, the values syncs to the setting page. But When I click "Apply" on the setting page, the values did not come back.
I am new in c# and Xamarin and tried to search online and Microsoft docs. But I couldn't find a way to fix this issue.
Also I was following this link: How to set BindingContext of multiple pages to the same ViewModel in Xamarin.Forms?
and did the same global value in my code.
MyView (ContentView)
public MyView()
{
InitializeComponent();
BindingContext = GlobalVar.MyViewModel;
Setting1.SetBinding(Picker.ItemsSourceProperty, "ObList1");
Setting1.ItemDisplayBinding = new Binding("obj_text");
Setting1.SetBinding(Picker.SelectedItemProperty, "SelectedItem1");
//also other pickers
}
HomePage (including the MyView)
public SearchPage ()
{
InitializeComponent ();
BindingContext = GlobalVar.MyViewModel;
}
private async void Click_GoSetting(object sender, EventArgs e)
{
await Navigation.PushAsync(new SettingPage());
}
SettingPage (including the same MyView)
public partial class SettingPage : ContentPage
{
MyViewModel viewModel { get; set; } = GlobalVar.MyViewModel;
public SettingPage ()
{
BindingContext = viewModel;
}
private async void Click_ApplySetting(object sender, EventArgs e)
{
await Navigation.PopAsync(true);
}
//some other method deal with viewModel
}
GLobalVar.cs
private static MyViewModel _myViewModel = new MyrViewModel();
public static MyViewModel MyViewModel
{
get
{
return _myViewModel;
}
}
ViewModel
public class MyViewModel : BaseViewModel
{
public ObservableCollection<obj> ObList1 { get; set; }
public ObservableCollection<obj> ObList2 { get; set; }
public ObservableCollection<obj> ObList3 { get; set; }
public obj SelectedItem1 { get; set; }
public obj SelectedItem2 { get; set; }
public obj SelectedItem3 { get; set; }
public MyViewModel()
{
ObList1 = new ObservableCollection<obj>();
ObList2 = new ObservableCollection<obj>();
ObList3 = new ObservableCollection<obj>();
}
}
Maybe I should notify the changes on my SettingPage to viewmodel? or do something in the "set" in viewmodel?
The confusing point is that two pages embed the same view using the same viewmodel, but notify the change from Page1 to Page2 only, not Page2 to Page1.
Any ideas, thx in advance.
Solution One:
Using Event can pass value back to Previous Page.
Define Event in SecondPage :
public delegate void EventHandler(string status);
public event EventHandler EventPass;
Invoke Event when Page disappear:
protected override void OnDisappearing()
{
base.OnDisappearing();
EventPass("Back Code");
}
In FirstPage, when Naviagtion place need to add the Event here:
string title = "PageSecondParamater";
PageSecond pageSecond = new PageSecond(title);
pageSecond.EventPass += PageSecond_EventPass; ;
Navigation.PushAsync(pageSecond);
Now value will be passed here:
private void PageSecond_EventPass(string status)
{
Title = status;
Console.WriteLine("---" + status);
}
Solution Two:
Using Properties Dictionary to store easy and small size data in Application, when enter in page will invoke it to get data from which has been stored.
In Second Page Where you want to store data, writing as bellow:
Application.Current.Properties ["value"] = valuedata;
When back to First Page, override OnAppearing method to update UI:
protected override void OnAppearing()
{
base.OnAppearing();
if (Application.Current.Properties.ContainsKey("value"))
{
var ValueGet = Application.Current.Properties ["value"] as DataType;
// do something with other things
}
}
Note: ViewModel if want to dynamic update data , need to use INotifyPropertyChanged .
Sample Implementation:
public class ObservableProperty : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModelBase suggest implementing ICommand as a Dictionary structure like:
public abstract class ViewModelBase : ObservableProperty
{
public Dictionary<string,ICommand> Commands { get; protected set; }
public ViewModelBase()
{
Commands = new Dictionary<string,ICommand>();
}
}
So all todo in your ViewModel is just inherit the ViewModelBase class and use it:
class LoginViewModel : ViewModelBase
{
string userName;
string password;
public string UserName
{
get {return userName;}
set
{
userName = value;
OnPropertyChanged("UserName");
}
}
public string Password
{
get{return password;}
set
{
password = value;
OnPropertyChanged("Password");
}
}
#endregion
#region ctor
public LoginViewModel()
{
//Add Commands
Commands.Add("Login", new Command(CmdLogin));
}
#endregion
#region UI methods
private void CmdLogin()
{
// do your login jobs here
}
#endregion
}
Solved.
MyViewModel (updated)
public class MyViewModel : BaseViewModel
{
public ObservableCollection<obj> ObList1 { get; set; }
public ObservableCollection<obj> ObList2 { get; set; }
public ObservableCollection<obj> ObList3 { get; set; }
private obj _selectedItem1 = new obj();
public obj SelectedItem1
{
get { return _selectedItem1; }
//this is the line solved the problem
//but still not understood thoroughly
set { SetProperty(ref _selectedItem1, value); }
}
//same for _selectedItem2 _selectedItem3
}
ps: BaseViewModel codes here (not changed, from template codes)
public class BaseViewModel : INotifyPropertyChanged
{
//some other attributes
//...
protected bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName]string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
}
It seems that by calling SetProperty, OnPropertyChanged will also be revoked.
But still a little bit confusing about why the previous codes go like kind of "one-way" binding.

Xamarin passing context from OnClick event

I m a beginner in android dev, I m struggling with passing string Clicked_Message from Click event in Recycle Adapter Class to the other activity. Is it a good way to use Intent? If so how can I pass context to click event? Thanks
public class RecyclerAdapter : RecyclerView.Adapter
{
private RecyclerView mRecyclerView;
private List<NotificationClass> mEmails;
public RecyclerAdapter(List<NotificationClass> emails, RecyclerView recyclerView)
{
mEmails = emails;
mRecyclerView = recyclerView;
}
public class MyView : RecyclerView.ViewHolder
{
public View mMainView { get; set; }
public TextView mName { get; set; }
public TextView mSubject { get; set; }
public TextView mMessage { get; set; }
public MyView(View view) : base(view)
{
mMainView = view;
}
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
View row = LayoutInflater.From(parent.Context).Inflate(Resource.Layout.row, parent, false);
TextView txtName = row.FindViewById<TextView>(Resource.Id.txtName);
TextView txtSubject = row.FindViewById<TextView>(Resource.Id.txtSubject);
TextView txtMessage = row.FindViewById<TextView>(Resource.Id.txtMessage);
MyView view = new MyView(row) { mName = txtName, mSubject = txtSubject, mMessage = txtMessage };
return view;
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
MyView myHolder = holder as MyView;
int indexPosition = (mEmails.Count - 1) - position;
myHolder.mMainView.Click += mMainView_Click;
myHolder.mName.Text = mEmails[position].Name;
myHolder.mSubject.Text = mEmails[position].Subject;
myHolder.mMessage.Text = mEmails[position].Message;
}
public override int ItemCount
{
get { return mEmails.Count; }
}
public void OnClick(int position)
{
if (ItemClick != null)
ItemClick(this, position);
}
public void mMainView_Click(object sender, EventArgs e,Context context)
{
int position = mRecyclerView.GetChildPosition((View)sender);
int indexPosition = (mEmails.Count - 1) - position;
Console.WriteLine(mEmails[indexPosition].Message);
string Clicked_Message = (mEmails[indexPosition].Message);
var activity2 = new Intent(context, typeof(ContactActivity));
activity2.PutExtra("MyData", Clicked_Message);
context.StartActivity(activity2);
}
}
You don't need to pass a context. Just use an intent and put the information you want to pass as extras into the intent.
In case your adapter needs a context, pass it in through the constructor and store it as a field member.
This is my typical implementation of the RecyclerView.Adapter with a view holder...
public class ContactsAdapter : V7.RecyclerView.Adapter
{
private List<Contact> _contacts;
public event EventHandler ItemClick;
public void OnItemClick(ContactViewHolder holder)
{
if (ItemClick != null)
{
ItemClick(holder, EventArgs.Empty);
}
}
public ContactsAdapter(List<Contact> contacts)
: base()
{
_contacts = contacts;
}
public override void OnBindViewHolder(V7.RecyclerView.ViewHolder holder, int position)
{
var contactHolder = (ContactViewHolder)holder;
contactHolder.BindUI(_contacts[position]);
}
public override V7.RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
var view = LayoutInflater.FromContext(parent.Context).Inflate(Resource.Layout.ContactsListItem, parent, false);
return new ContactViewHolder(view)
{
Adapter = this
};
}
public override int ItemCount
{
get
{
return _contacts.Count;
}
}
}
View Holder (typically in the same file as the adapter)
public class ContactViewHolder : V7.RecyclerView.ViewHolder, View.IOnClickListener
{
public TextView ContactNameTextView { get; set; }
public TextView ContactPhoneTextView { get; set; }
public TextView ContactIntialsTextView { get; set; }
public Contact Contact { get; set; }
private WeakReference _adapter;
public ContactsAdapter Adapter
{
get { return (ContactsAdapter)_adapter.Target; }
set { _adapter = new WeakReference(value); }
}
public ContactViewHolder(View view)
: base(view)
{
GetUI(view);
view.SetOnClickListener(this);
}
private void GetUI(View view)
{
ContactNameTextView = view.FindViewById<TextView>(Resource.Id.ContactName);
ContactPhoneTextView = view.FindViewById<TextView>(Resource.Id.ContactPhone);
ContactIntialsTextView = view.FindViewById<TextView>(Resource.Id.ContactInitialsTextView);
}
public void BindUI(Contact contact)
{
Contact = contact;
ContactNameTextView.Text = contact.ContactName;
ContactPhoneTextView.Text = contact.Phone1;
ContactIntialsTextView.Text = contact.Initials;
}
public void OnClick(View v)
{
Adapter.OnItemClick(this);
}
}
This encapsulates the functionality to the view holder. I also give the instance of the adapter to the view holder as a WeakReference. This allows me to call the OnItemClick, passing the instance of the view holder. If you will notice that the view holder also has an instance of the object that it is representing. This means I don't have to worry about the index that was chosen. I already have the object data available to me.
So the implementation in the Activity/Fragment is like this...
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
_contacts = Contact.GetAllContacts();
_adapter = new ContactsAdapter(_contacts);
_adapter.ItemClick += ContactSelected;
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = inflater.Inflate(Resource.Layout.ContactsFragment, container, false);
var layoutManager = new V7.LinearLayoutManager(this.Activity) { Orientation = V7.LinearLayoutManager.Vertical };
_contactsView = view.FindViewById<V7.RecyclerView>(Resource.Id.ContactList);
_contactsView.SetAdapter(_adapter);
_contactsView.HasFixedSize = true;
_contactsView.SetLayoutManager(layoutManager);
return view;
}
private void ContactSelected (object sender, EventArgs e)
{
var holder = (ContactViewHolder)sender;
var detailFragment = new ContactDetailsFragment(holder.Contact);
MainActivity.ShowFragment(detailFragment);
}
I give the Contact to a Fragment, but you could do something similar for an activity using an intent.
Now whether this is the most efficient way of handling a click of a row in a RecyclerView, I don't know. But this implementation has been working for me.

how to convert 'this' in "Self Referencing Generics"

I want to add an observer in my model, i try to generic delegate but here is problem when invoke.
Here is my code and it works when I use 'handler.DynamicInvoke(this)' instead of 'Invoke'
but I know DynamicInvoke is slow... I want to know is here a right way to use Invoke.
public class Model<T>
{
public delegate void UpdatePrototype<T>(T mdl);
private List<UpdatePrototype<T>> listeners = new List<UpdatePrototype<T>>();
public void Bind(UpdatePrototype<T> handler)
{
listeners.Add(handler);
}
public void Sync()
{
foreach(UpdatePrototype<T> handler in listeners)
{
handler.Invoke((T)this); // << ERROR: can not convert Model<T> to T
}
}
public string Name = "Model";
}
public class MyModel : Model<MyModel>
{
public string Name = "MyModel";
}
public class YourModel : Model<YourModel>
{
public string Name = "YourModel";
}
void Main()
{
MyModel mdl = new MyModel();
mdl.Bind(MyUpdate);
mdl.Sync();
YourModel your = new YourModel();
your.Bind(YourUpdate);
your.Sync();
}
void MyUpdate(MyModel mdl)
{
Debug.Log(mdl.Name);
}
void YourUpdate(YourModel mdl)
{
Debug.Log(mdl.Name);
}
============
thanks #IVAAAN123 i modify my code as follow.
it is fine for me, although mdl.Sync<MyModel>() has a little odd ;)
public class Model<T>
{
public delegate void UpdatePrototype<T>(T mdl);
private List<UpdatePrototype<T>> listeners = new List<UpdatePrototype<T>>();
public void Bind(UpdatePrototype<T> handler)
{
listeners.Add(handler);
}
public void Sync()
{
foreach(UpdatePrototype<T> handler in listeners)
{
handler.DynamicInvoke(this);
}
}
public void Sync<T>() where T : Model<T>
{
foreach(UpdatePrototype<T> handler in listeners)
{
handler.Invoke((T)this);
}
}
public string Name = "Model";
}
public class MyModel : Model<MyModel>
{
public string Name = "MyModel";
}
public class YourModel : Model<YourModel>
{
public string Name = "YourModel";
}
void Main()
{
MyModel mdl = new MyModel();
mdl.Bind(MyUpdate);
mdl.Sync<MyModel>();
mdl.Sync();
YourModel your = new YourModel();
your.Bind(YourUpdate);
your.Sync<YourModel>();
your.Sync();
}
void MyUpdate(MyModel mdl)
{
Debug.Log(mdl.Name);
}
void YourUpdate(YourModel mdl)
{
Debug.Log(mdl.Name);
}
}
public class Model<T>
{
public delegate void UpdatePrototype<S>(S mdl);
private List<UpdatePrototype<Model<T>>> listeners = new List<UpdatePrototype<Model<T>>>();
public void Bind(UpdatePrototype<Model<T>> handler)
{
listeners.Add(handler);
}
public void Sync()
{
foreach (UpdatePrototype<Model<T>> handler in listeners)
{
handler(this);
}
}
public virtual string Name
{
get
{
return "Model";
}
}
}
public class MyModel : Model<MyModel>
{
public override string Name
{
get
{
return "MyModel";
}
}
}
public class YourModel : Model<YourModel>
{
public override string Name
{
get
{
return "YourModel";
}
}
}
void main()
{
MyModel mdl = new MyModel();
mdl.Bind(MyUpdate);
mdl.Sync();
YourModel your = new YourModel();
your.Bind(YourUpdate);
your.Sync();
}
void MyUpdate(Model<MyModel> mdl)
{
Console.WriteLine("MY MODEL HANDLER");
Console.WriteLine(mdl.Name);
}
void YourUpdate(Model<YourModel> mdl)
{
Console.WriteLine("YOUR MODEL HANDLER");
Console.WriteLine(mdl.Name);
}

Categories