public interface IConnector
{
void Connect();
event EventHandler<EventArgs> Received;
// and more
}
public class ConnectorA: IConnector
{
public event EventHandler<EventArgs> Received;
public void Connect(){
...
}
}
public class ConnectorB: IConnector
{
public event EventHandler<EventArgs> Received;
public void Connect(){
...
}
}
Now let's say I have a factory like this:
public interface IConnectorFactory
{
void Create(string type);
}
public class ConnectorFactory: IConnectorFactory
{
public void Create(string type)
{
switch(type)
{
case "A":
return new ConnectorA();
case "B":
return new ConnectorB();
}
}
}
I inject this factory via constructor to Manager class:
public class Manager
{
IConnectorFactory _factory;
IConnector _actualConnector;
string _type;
public string Type
{
get
{
return _type;
}
set
{
_type = value;
if (_actualConnector != null)
{
_actualConnector.Disconnect();
_actualConnector.Received -= ReceivedFunc;
}
_actualConnector = _factory.Create(Type);
_actualConnector.Received += ReceivedFunc;
}
}
public Manager(IConnectorFactory factory)
{
_factory = factory;
}
public void Connect()
{
_actualConnector.Connect();
}
public void DoSomethingElse()
{
_actualConnector.DoSomethingElse();
_actualConnector.Received += ReceivedFunc;
}
public void ReceivedFunc()
{
}
}
Type is property that is set external (for example binded to UI ComboBox). Problem is that every time it is changed I have to unsubscribe old event and subscribe to new event. Isn't there some pattern for subscribing to interface event?
When you assign a value to _actualConnector:
_actualConnector = _factory.Create(Type);
your're just changing a field and the old object has still its Received event handled by ReceivedFunc. This has nothing to do with interfaces, this is the way objects and references work in .NET
Related
I want to organize my code as well as I can, but I have some trouble with its organization. I try to use SOLID principles and make separate entities. I want to use MVVM to view (unity-weld as well), and DI container (zenject as well).
It's my first project and I'm trying to organize code.
My question is how to inject LoginViewModel into LoginController as static class using zenject container from GameInstaller class.
[Binding]
public class LoginViewModel : MonoBehaviour, INotifyPropertyChanged
{
private string _username = "";
private string _passsword = "";
private string _errorMessage = "";
private bool _autologin = false;
[Inject]
private LoginController _loginController;
[Binding]
public string Username {
get
{
return _username;
}
set
{
if (_username == value)
{
return; // No change.
}
_username = value;
Debug.Log($"SET username: {value}");
OnPropertyChanged("Username");
}
}
[Binding]
public string Password {
get
{
return _passsword;
}
set
{
if (_passsword == value)
{
return; // No change.
}
_passsword = value;
Debug.Log($"SET password: {value}");
OnPropertyChanged("Password");
}
}
[Binding]
public bool Autologin {
get
{
return _autologin;
}
set
{
if (_autologin == value)
{
return; // No change.
}
_autologin = value;
Debug.Log($"SET autologin: {value}");
OnPropertyChanged("Autologin");
}
}
[Binding]
public void LoginButtonClick()
{
Debug.Log("LoginButtonClick");
_loginController.Login(this);
//ErrorMessage = "blabla";
}
[Binding]
public string ErrorMessage
{
get
{
return _errorMessage;
}
set
{
if (_errorMessage == value)
{
return; // No change.
}
_errorMessage = value;
Debug.Log($"SET errorMessage: {value}");
OnPropertyChanged("ErrorMessage");
}
}
/// <summary>
/// Event to raise when a property's value has changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class LoginController
{
private readonly ApiController _apiController;
[Inject]
private readonly LoginViewModel _loginViewModel;
public LoginController(ApiController apiController)
{
_apiController = apiController;
}
public void Login(LoginViewModel loginViewModel)
{
try
{
string userJson = _apiController.PostLogin(loginViewModel.Username);
_loginViewModel.ErrorMessage = "bla bla trololo";
Debug.Log(userJson);
}
catch (Exception ex)
{
throw ex;
}
}
}
public class GameInstaller : MonoInstaller
{
[Inject]
Settings _settings = null;
public override void InstallBindings()
{
InstallViewModels();
InstallServices();
InstallSignals();
InstallControllers();
}
private void InstallViewModels()
{
Container.Bind<LoginViewModel>().AsSingle();
}
private void InstallControllers()
{
Container.Bind<LoginController>().AsSingle().NonLazy();
Container.Bind<ApiController>().AsSingle().NonLazy();
}
private void InstallServices()
{
}
private void InstallSignals()
{
}
[Serializable]
public class Settings
{
}
}
Let's start from here: https://unitylist.com/p/ja3/Unity-MVVM
A ViewModel (or VM) is what holds the data that will be presented on a view. It contains all the properties that can be bound to view elements. All ViewModels inherit from INotifyPropertyChanged which alerts the system when data changes and a UI element needs to be updated.
Since the ViewModel is in it's essence a simple object and not a service, I argue that it does not need go be injected anywhere.
What you could do, is inject a Factory and get your ViewModel from it.
In your code you are trying to inject the controller into your ViewModel and not the other way round.
I use C# and I want to trigger an event from within a class :
So if the Price property of a class was changed then an event onPriceChanged (outside the class) should be fired.
However, I get an error:
The name 'onPriceChanged' does not exist in the current context
How could I fix this?
(I guess that I could pass the eventhandler to the class via constructor...but if possible I would prefer not to pass the eventhandler to the class)
Here is my code :
using System;
public delegate void delEventHandler();
class clsItem
{
//private static event delEventHandler _show;
private delEventHandler _show;
private int _price;
public clsItem() //Konstruktor
{
_show += new delEventHandler(Program.onPriceChanged); // error here : The name 'onPriceChanged' does not exist in the current context
}
public int Price
{
set
{
_price = value;
_show.Invoke(); //trigger Event when Price was changed
}
}
}
class Program
{
static void Main()
{
clsItem myItem = new clsItem();
myItem.Price = 123; //this should trigger Event "onPriceChanged"
}
//EventHandler
public static void onPriceChanged()
{
Console.WriteLine("Price was changed");
}
}
You're doing this the wrong way round - you're trying to attach the event handler from the class, and clearly that cannot have access to the Program.onPriceChanged method!
You should expose your event, and attach the event handler from the client code (Program).
class clsItem
{
//private static event delEventHandler _show;
private delEventHandler _show;
private int _price;
public clsItem() //Konstruktor
{
}
public event delEventHandler Show
{
add { _show += value; }
remove { _show -= value; }
}
public int Price
{
set
{
_price = value;
_show?.Invoke(); //trigger Event when Price was changed
}
}
}
And:
clsItem myItem = new clsItem();
myItem.Show += onPriceChanged;
myItem.Price = 123; //this now does trigger Event "onPriceChanged"
Live example: http://rextester.com/WMCQQ40264
The way you're dealing with events is not a good practice. the reason why we use Events is to decouple the objects we create from the methods they need to call.
For example if you want to create another object of the same type(clsItem) and get it to call another method once its price changed, you get into trouble. So I'd suggest this code rather than the current one:
using System;
public delegate void delEventHandler();
class clsItem
{
public event delEventHandler PriceChanged;
private int _price;
public clsItem() //Konstruktor
{
}
public int Price
{
set {
if(value!=_price) // Only trigger if the price is changed
{
_price = value;
if(PriceChanged!=null) // Only run if the event is handled
{
PriceChanged();
}
}
}
}
}
class Program
{
static void Main()
{
clsItem myItem = new clsItem();
myItem.PriceChanged += new delEventHandler(onPriceChanged);
myItem.Price = 123; //this should trigger Event "PriceChanged" and call the onPriceChanged method
}
//EventHandler
public static void onPriceChanged()
{
Console.WriteLine("Price was changed");
}
}
Here is the more traditional way of doing what you want:
public delegate void delEventHandler();
class clsItem
{
public event delEventHandler Show;
private int _price;
public clsItem() //Konstruktor
{
}
public int Price
{
set
{
_price = value;
Show?.Invoke(); //trigger Event when Price was changed
}
}
}
class Program
{
static void Main()
{
clsItem myItem = new clsItem();
myItem.Show += onPriceChanged;
myItem.Price = 123; //this should trigger Event "onPriceChanged"
}
//EventHandler
public static void onPriceChanged()
{
Console.WriteLine("Price was changed");
}
}
Notice that clsItem no longer knows who is subscribing to its event. All it cares about is notifying any listeners who happens to be subscribed. There is no longer a dependency between clsItem and the onPriceChanged method.
Not very familiar with declaring and using events and received error,
Event must be of delegate type
Basically want to pass IMyInterface as a dependency to another class where that class can subscribe to receive MyClassEvent events and the event data is MyClass.
public interface IMyInterface
{
event MyClass MyClassEvent;
}
public class Implementation: IMyInterface
{
event MyClass MyClassEvent;
public void OnSomethingHappened
{
MyClassEvent?.Invoke(); // pass MyClass to subscribers
}
}
public class AnotherClass(IMyInterface ...)
{
OnMyClassEvent(MyClass args)
{
// do something
}
}
You need to declare the event correctly and define the event args:
public class MyClassEventArgs : EventArgs { }
public interface IMyInterface
{
event EventHandler<MyClassEventArgs> MyClassEvent;
}
public class Implementation : IMyInterface
{
public event EventHandler<MyClassEventArgs> MyClassEvent;
public void OnSomethingHappened()
{
MyClassEvent?.Invoke(this, new MyClassEventArgs());
}
}
And to subscribe to it:
var implementation = new Implementation();
implementation.MyClassEvent += MyClassEvent;
private void MyClassEvent(object sender, MyClassEventArgs e) { ... }
I have a C# WPF application built with Visual Studio 2015. I'm using MVVM and the Observer Pattern.
My Provider is a user control called 'ucClientFilter1ViewModel' that contains two text box controls where the user can search for a client(s):
namespace NSUCClientControls
{
public class ucClientFilter1ViewModel : ViewModelBase, IObservable<ClientFilterParameter>
{
private string filterLocation;
private string whereSearch1;
private string whereSearch2;
private List<IObserver<ClientFilterParameter>> observers;
public ucClientFilter1ViewModel()
{
observers = new List<IObserver<ClientFilterParameter>>();
}
public string FilterLocation
{
get { return filterLocation; }
set { filterLocation = value; }
}
public string WhereSearch1
{
get { return whereSearch1; }
set
{
whereSearch1 = value;
TestUpdateGrid(filterLocation);
}
}
public string WhereSearch2
{
get { return whereSearch2; }
set
{
whereSearch2 = value;
TestUpdateGrid(filterLocation);
}
}
private void TestUpdateGrid(string _filterLocation)
{
var filterInfo = new ClientFilterParameter(this);
foreach (var observer in observers)
{
observer.OnNext(filterInfo);
}
}
public IDisposable Subscribe(IObserver<ClientFilterParameter> observer)
{
// Check whether observer is already registered. If not, add it
if (!observers.Contains(observer))
{
observers.Add(observer);
// Provide observer with existing data
var filterInfo = new ClientFilterParameter(this);
observer.OnNext(filterInfo);
}
return new Unsubscriber<ClientFilterParameter>(observers, observer);
}
internal class Unsubscriber<ClientFilterParameter> : IDisposable
{
private IObserver<ClientFilterParameter> observer;
private List<IObserver<ClientFilterParameter>> observers;
public Unsubscriber(List<IObserver<ClientFilterParameter>> _observers, IObserver<ClientFilterParameter> _observer)
{
observers = _observers;
observer = _observer;
}
public void Dispose()
{
if (observers.Contains(observer))
{
observers.Remove(observer);
}
}
}
}
}
My Observer is a user control called 'ucClientGrid1ViewModel' that contains a datagrid where the search results are displayed.
namespace NSUCClientControls
{
public class ucClientGrid1ViewModel : ViewModelBase, IObserver<ClientFilterParameter>
{
private IDisposable cancellation;
private ObservableCollection<Client> clientsMultiple;
public ucClientGrid1ViewModel()
{
}
public ObservableCollection<Client> ClientsMultiple
{
get
{
var myClientDataAccess = new ClientDataAccess();
clientsMultiple = myClientDataAccess.GetClientListFromSQL_Test2();
return clientsMultiple;
}
set
{
}
}
public virtual void Subscribe(ucClientFilter1ViewModel provider)
{
cancellation = provider.Subscribe(this);
}
public void OnNext(ClientFilterParameter myFilter)
{
OnPropertyChanged("ClientsMultiple");
var myDummyWindow = new dummyWindow();
myDummyWindow.Show();
myDummyWindow.Close();
}
public void OnError(Exception error)
{
throw new NotImplementedException();
}
public void OnCompleted()
{
throw new NotImplementedException();
}
}
}
This all works and I get the search results that I am expecting. But what I don't understand is why the inclusion of the following lines actually speed things up!
var myDummyWindow = new dummyWindow();
myDummyWindow.Show();
myDummyWindow.Close();
I'm new to MVVM and the observer pattern, so as I was writing the code I had included message boxes at various points to help me to follow the flow of it. It was all working as expected. Then I removed the message boxes and it still worked but the application was pausing at the end before you could continue to keep searching.
Putting a message box back in at the end prevented this pause. Replacing the message box with a "DummyWindow" that just opens and closes has the same affect and prevents the pause at the end. This is what I currently have but I'd rather not leave this in there.
Presumably opening the window causes something else to happen which stops some redundant process, and this then prevents the pause? What else could I do to prevent the pause at the end, without using this DummyWindow?
I've tried searching on here and with Bing with no luck.
Thanks in advance!
Edit:
ViewModelBase...
namespace NSCommon
{
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable
{
protected ViewModelBase()
{
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
public void Dispose()
{
OnDispose();
}
protected virtual void OnDispose()
{
}
}
}
ClientFilterParameter...
namespace NSCommon
{
public class ClientFilterParameter
{
public ClientFilterParameter(ucClientFilter1ViewModel myFilter)
{
FilterLocation = myFilter.FilterLocation;
WhereSearch1 = myFilter.WhereSearch1;
WhereSearch2 = myFilter.WhereSearch2;
}
private string filterLocation;
private string whereSearch1;
private string whereSearch2;
public string FilterLocation
{
get { return filterLocation; }
set { filterLocation = value; }
}
public string WhereSearch1
{
get { return whereSearch1; }
set { whereSearch1 = value; }
}
public string WhereSearch2
{
get { return whereSearch2; }
set { whereSearch2 = value; }
}
}
}
well ive read a lot of posts here and there about why it isnt reliable to raise events via reflection.. my problem is this.. im using PostSharp to define an interface which allows a class to notify before and after a property is changed..
the NotifyAttribute ive created needs to be able to raise the PropertyBeforeChange and PropertAfterChange events.. thing is, even though i can retrieve the event, its GetRaiseMethod() returns null and hence i cannot raise the said events.. how can i go about doing that?
using PostSharp.Aspects;
namespace Core
{
public delegate void PropertyBeforeChangeEventHandler(object sender, string sPropertyName, object oCurrentValue, ref object oProposedValue, ref bool bCancel);
public delegate void PropertyAfterChangeEventHandler(object sender, string sPropertyName, object oOldValue, object oNewValue);
public interface INotify
{
event PropertyBeforeChangeEventHandler PropertBeforeChange;
event PropertyAfterChangeEventHandler PropertyAfterChange;
}
[Serializable]
public sealed class NotifyAttribute : LocationInterceptionAspect, IInstanceScopedAspect
{
bool _NotifyBefore { get; set; }
bool _NotifyAfter { get; set; }
public NotifyAttribute() { _NotifyAfter = true; }
public NotifyAttribute(bool bNotifyBefore, bool bNotifyAfter) { _NotifyBefore = bNotifyBefore; _NotifyAfter = bNotifyAfter; }
public override void OnSetValue(LocationInterceptionArgs args)
{
INotify oNotify = args.Instance as INotify;
if (oNotify == null) return;
object oCurrentValue = args.GetCurrentValue();
object oProposedValue = args.Value;
if (object.Equals(oCurrentValue, oProposedValue)) return;
bool bCancel = false;
if (_NotifyBefore)
{
var oObj = args.Instance.GetType().GetEvent("PropertyBeforeChange");
// RAISE EVENT HERE
}
if (bCancel) return;
args.Value = oProposedValue;
args.ProceedSetValue();
if (_NotifyAfter)
{
var oObj = args.Instance.GetType().GetEvent("PropertyAfterChange");
// RAISE EVENT HERE
}
}
public object CreateInstance(AdviceArgs adviceArgs) { return this.MemberwiseClone(); }
public void RuntimeInitializeInstance() { }
}
}
having defined this interface and this attribute, i can use it as follows..
public class Test : INotify
{
public event PropertyBeforeChangeEventHandler PropertyBeforeChange;
public event PropertyAfterChangeEventHandler PropertyAfterChange;
[Notify]
public string Name { get; set; }
}
Test oTest = new Test();
oTest.PropertyBeforeChange += Test_PropertBeforeChange;
oTest.PropertyAfterChange += Test_PropertyAfterChange;
oTest.Name = "Asim";
void Test_PropertBeforeChange(object sender, string sPropertyName, object oCurrentValue, ref object oProposedValue, ref bool bCancel)
{
}
void Test_PropertyAfterChange(object sender, string sPropertyName, object oOldValue, object oNewValue)
{
}