I am writing plugin based application.
Host application:
namespace CSK
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
LoadPlugins();
}
public void LoadPlugins()
{
DirectoryInfo di = new DirectoryInfo("./Plugins");
foreach (FileInfo fi in di.GetFiles("*_Plugin.dll"))
{
Assembly pluginAssembly = Assembly.LoadFrom(fi.FullName);
foreach (Type pluginType in pluginAssembly.GetExportedTypes())
{
if (pluginType.GetInterface(typeof(MainInterface.PluginHostInterface).Name) != null)
{
MainInterface.PluginHostInterface TypeLoadedFromPlugin = (MainInterface.PluginHostInterface)Activator.CreateInstance(pluginType);
MainInterface.IMother mother = new ApiMethods(this);
TypeLoadedFromPlugin.Initialize(mother);
}
}
}
}
}
Interface:
namespace MainInterface
{
public interface PluginHostInterface
{
void Initialize(IMother mother);
}
public interface IMother
{
MenuItem addMenuItem(String header, String name);
MenuItem addSubMenuItem(MenuItem menu, String header, String name);
Boolean receiveMessage(String message, String from);
Boolean addContact(String name, String status, String proto, String avatar = "av");
}
}
Test plugin:
namespace Plugin_Test
{
public class MainClass : MainInterface.PluginHostInterface
{
private MainInterface.IMother CSK;
public void Initialize(MainInterface.IMother mainAppHandler)
{
CSK = mainAppHandler;
}
}
}
And now, i want to execute some methods in Plugin Test from my host application. Of course, there will be many plugins, and not every of them will contain specified methods. I was trying to use events but with no success. Any idea how to do it?
Class with Events:
public class EventProvidingClass {
public event EventHandler SomeEvent;
public void InvokeSomeEvent() {
if(SomeEvent != null) SomeEvent.Invoke(this, new EventArgs());
}
}
Your Plugin Interface:
public interface PluginHostInterface
{
void Initialize(IMother mother);
void InitializeEvents(EventProvidingClass eventProvider);
}
Plugin Class:
public class MainClass : MainInterface.PluginHostInterface
{
private MainInterface.IMother CSK;
public void Initialize(MainInterface.IMother mainAppHandler)
{
CSK = mainAppHandler;
}
public void InitializeEvents(EventProvidingClass eventProvider)
{
eventProvider.SomeEvent += someEventHandler;
}
private void someEventHandler(object sender, EventArgs e)
{
}
}
and then call InitializeEvents after the Initialize function.
Of course you can put the events where you want them, you just need to make sure that you make them available for the Plugin so that the Plugin can assign its EventHandlers
Related
Hello i want to add a feature to my app but i just can't figure out.
i want to pass some arguments or at least 1 argument to EventHandler using subscriber.
That argument will allow me to do some check and then trigger event based on that argument.
public class Client
{
public Client()
{
GameAPI api = new GameAPI();
api.AddedPlayerEvent += Api_ClarkAdded;
api.Do();
}
private void Api_ClarkAdded(object sender, GameAPI.AddedPlayerEvents e)
{
Console.WriteLine("User Clark found");
}
}
public class GameAPI
{
public event EventHandler<AddedPlayerEvents> AddedPlayerEvent;
List<AddedPlayerEvents> AddedPlayers = new List<AddedPlayerEvents>();
public GameAPI()
{
// some code to simulate generating some data
AddedPlayers.Add(new AddedPlayerEvents("Player1","James"));
AddedPlayers.Add(new AddedPlayerEvents("Player2", "Clark"));
AddedPlayers.Add(new AddedPlayerEvents("Player3", "Steve"));
}
public void Do()
{
// simulating code ...
//trigger event
if (AddedPlayers.Any(f => f.Name == "Clark")) /*value Clark should come from client using subsciber or something else*/
{
OnPlayerAdded(AddedPlayers.First(f => f.Name == "Clark"));
}
}
protected virtual void OnPlayerAdded(AddedPlayerEvents e)
{
EventHandler<AddedPlayerEvents> handler = AddedPlayerEvent;
if (handler != null)
{
handler(this, e);
}
}
public class AddedPlayerEvents
{
public string Pseudo { get; set; }
public string Name { get; set; }
public AddedPlayerEvents(string pseudo, string name)
{
Pseudo = pseudo;
Name = name;
}
}
}
This is a simplified version of what i want to do so i try to make it simple so you can deal with it without garbadge stuff.
I already made a search but all i can find is the parameters is visible only in the client in the Methode handler not transmitted to the EventHandler, i think t should be stored somewhere that he can fetch for them.
Thanks in advance.
Update:
Thanks for the clarification. I think you don't fully understand how the event subscription mechanism works. Let me explain this on example. I refactored your code a little bit:
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
Client client = new Client();
}
}
public class Client
{
public Client()
{
GameAPI api = new GameAPI();
api.PlayerAdded += Api_PlayerAdded;
api.AddPlayer(new Player("Player2", "Clark"));
}
private void Api_PlayerAdded(object sender, PlayerAddedEventArgs e)
{
Console.WriteLine($"API PlayerAdded event has triggered. Arguments: e.Player.Name = {e.Player.Name}, e.Player.Pseudo = {e.Player.Pseudo}");
}
}
public class PlayerAddedEventArgs: EventArgs
{
public PlayerAddedEventArgs(Player player)
{
Player = player;
}
public Player Player { get; }
}
public class Player
{
public Player(string pseudo, string name)
{
Pseudo = pseudo;
Name = name;
}
public string Pseudo { get; }
public string Name { get; }
}
public class GameAPI
{
private List<Player> players = new List<Player>();
public event EventHandler<PlayerAddedEventArgs> PlayerAdded;
public void AddPlayer(Player player)
{
players.Add(player);
OnPlayerAdded(new PlayerAddedEventArgs(player));
}
protected virtual void OnPlayerAdded(PlayerAddedEventArgs e)
{
PlayerAdded?.Invoke(this, e);
}
}
The two main classes here are GameAPI and Client.
GameAPI class:
Keeps track of all added players in the private players list.
Provides an AddPlayer method allowing clients to add players.
Provides a PlayerAdded event to notify clients that a player has been added.
Client class:
Subscribes to the PlayerAdded event exposed by the GameAPI.
Calls AddPlayer method to add the new player.
AddPlayer method adds the player to the internal players list and calls OnPlayerAdded which notifies all subscribed clients about the new player. This notification causes the Api_PlayerAdded method in all subscribed instances of the Client class to be called. The added player will be accessible as the Player property in the e argument passed to this method.
Original answer:
I don't see any issues with your code. I modified the Api_JamesAdded method to make sure it works properly, so the full code looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
static void Main(string[] args)
{
Client client = new Client();
}
}
public class Client
{
public Client()
{
GameAPI api = new GameAPI();
api.AddedPlayerEvent += Api_JamesAdded;
api.Do();
}
private void Api_JamesAdded(object sender, GameAPI.AddedPlayerEvents e)
{
Console.WriteLine($"Name: {e.Name}, Pseudo: {e.Pseudo}");
}
}
public class GameAPI
{
public event EventHandler<AddedPlayerEvents> AddedPlayerEvent;
List<AddedPlayerEvents> AddedPlayers = new List<AddedPlayerEvents>();
public GameAPI()
{
// some code to simulate generating some data
AddedPlayers.Add(new AddedPlayerEvents("Player1", "James"));
OnPlayerAdded(AddedPlayers.First(f => f.Pseudo == "Player1"));
AddedPlayers.Add(new AddedPlayerEvents("Player2", "Clark"));
OnPlayerAdded(AddedPlayers.First(f => f.Pseudo == "Player2"));
AddedPlayers.Add(new AddedPlayerEvents("Player3", "Steve"));
OnPlayerAdded(AddedPlayers.First(f => f.Pseudo == "Player3"));
}
public void Do()
{
// simulating code ...
//trigger event
if (AddedPlayers.Any(f => f.Name == "Clark")) /*value Clark should come from client using subsciber or something else*/
{
OnPlayerAdded(AddedPlayers.First(f => f.Name == "Clark"));
}
}
protected virtual void OnPlayerAdded(AddedPlayerEvents e)
{
EventHandler<AddedPlayerEvents> handler = AddedPlayerEvent;
if (handler != null)
{
handler(this, e);
}
}
public class AddedPlayerEvents
{
public string Pseudo { get; set; }
public string Name { get; set; }
public AddedPlayerEvents(string pseudo, string name)
{
Pseudo = pseudo;
Name = name;
}
}
}
This code prints:
Name: Clark, Pseudo: Player2
If this is not what you expected, please post the expected output.
I will admit, i am doing homework and i am stuck on this one question (Part A). How do i pass the notice method as reference to the railway signal ? Can't i just find out which class was called in the abstract constructor and then print the class name within the notify method? For example:
RailwayUser
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.Writeline(className);
}
RailwaySignal Class
public void Notify()
{
foreach(RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(State)
}
}
This kind of code / design is flawed, since what it does is RailwayUser, registers the object reference with the _watchers List in the RailWaySignal class, which in turn calls the public Notice method on each user when Notify is invoked, which is not how Event Signaling or Function Pointer works. In fact public _watchers is dangerous, as it can be cleared by any user, though that can be moderated using property access
Code with Issue
public void Notify()
{
foreach(RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(State)
}
}
Following shall be the actual code using events and delegates:
Correct Version
Code Snippet Online - https://www.jdoodle.com/embed/v0/uEc
void Main()
{
List<RailwayUser> railwayUsers = new List<RailwayUser>();
railwayUsers.Add(new RailwayUser());
railwayUsers.Add(new RailwayUser());
RailwayUser.TestNotification();
}
public enum Colour
{
Red,
Green,
NoSignal
}
public class RailwaySignal
{
public string Name {get; set;}
public RailwaySignal(string railwaySignalName)
{
Name = railwaySignalName;
}
// Delegate for handling event
public delegate void RailwaySignalEventHandler(object source, Colour e);
// Delagate object for handling event
private RailwaySignalEventHandler _railwaySignalEvent;
// Event Accessor
public event RailwaySignalEventHandler RailwaySignalEvent
{
add
{
lock (this)
{
_railwaySignalEvent += value;
}
}
remove
{
lock (this)
{
_railwaySignalEvent -= value;
}
}
}
// Invoke Event for subscribed clients
private void Notify()
{
if (_railwaySignalEvent != null)
_railwaySignalEvent.Invoke(this, Colour.Green);
}
// Test the Event Invocation
public void TestEvent()
{
Notify();
}
}
public class RailwayUser
{
private static RailwaySignal railwaySignal { get; set;} = new RailwaySignal("Signal1");
public RailwayUser()
{
railwaySignal.RailwaySignalEvent += this.Notice;
}
public static void TestNotification()
{
railwaySignal.TestEvent();
}
public void Notice(object sender, Colour color)
{
Console.WriteLine($"Notice Called, Colour is :: {color}, Sender is :: {((RailwaySignal)sender).Name}");
}
}
Result
Notice Called, Colour is :: Green, Sender is :: Signal1
Notice Called, Colour is :: Green, Sender is :: Signal1
Important Details
Signature of the event is, (object source, Colour e) which helps in passing the relevant information across to the RailwayUser called, We now know the RailwaySignal triggering the notification to the RailwayUser and its Colour value
Event / Delegate has same signature as called method (which is the basis of working of Delegate / function pointers)
For simplification RailwayUser is a non abstract class
Event is executed using Notify() method inside the RailwaySignal, we are calling it artificially using TestNotification() inside RailwayUser just for demo purpose, but ideally it shall be internally triggered and shall pass on current state like Colour
Pre-defined delegates like Func, Action are quite often used for similar notification mechanism, They internally works using similar mechanism, though declaring an explicit event which is internally a delegate is a well defined pattern, especially for the Ui controls
Standard events exposed by the .Net framework have the signature object sender, EventArgs e, where EventArgs can wrap all information from Event executor (RailwaySignal) to Event receiver (RailwayUser)
It seem like a Observer pattern.You can pass SubClass which inherit from RailwayUser object instance into RailwaySignal class
Your RailwayUser class need create public abstract void Notice(Colour state) method.
public abstract class RailwayUser
{
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.WriteLine(className);
}
public abstract void Notice(Colour state);
}
Driver class can inherit RailwayUser class then override Notice method.
public class Driver : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine($"Driver see the {state.ToString()}");
}
}
There are
List<RailwayUser> _watches contain observable object
use SubScript(RailwayUser user) subscription user on _watches List.
RailwayUser Notify() to invoke all your observable Notify method.
look like this.
public class RailwaySignal
{
private List<RailwayUser> _watches;
public Colour Stata { get; set; }
public RailwaySignal()
{
_watches = new List<RailwayUser>();
}
public void SubScript(RailwayUser user)
{
_watches.Add(user);
}
public void Notify()
{
foreach (RailwayUser u in _watches)
{
u.PrintClassName();
u.Notice(Stata);
}
}
}
sample:https://dotnetfiddle.net/GcdGMy
You can also use event to pass method into RailwaySignal then invoke Notify method.
public enum Colour
{
Green,
Red,
Disable
}
public abstract class RailwayUser
{
private string className;
public RailwayUser()
{
Type type = this.GetType();
className = type.Name;
}
public void PrintClassName()
{
Console.WriteLine(className);
}
public abstract void Notice(Colour state);
}
public class Driver : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine("Driver see the "+ state.ToString());
}
}
public class Controller : RailwayUser
{
public override void Notice(Colour state)
{
Console.WriteLine("Controller see the " + state.ToString());
}
}
public class RailwaySignal
{
public delegate void NoticeEvent(Colour state);
public event NoticeEvent Notifys;
public Colour Stata { get; set; }
public void Notify()
{
if (Notifys != null)
{
Notifys(Stata);
}
}
}
use like this.
RailwaySignal railway = new RailwaySignal() { Stata = Colour.Green};
railway.Notifys += new Driver().Notice;
railway.Notifys += new Controller().Notice;
railway.Notify();
sample : https://dotnetfiddle.net/GcdGMy
I have a parent class that is firing an event to derived classes. The problem is that the event handler is alway null.
Class Plugin()
{
public delegate void BufferReadyHandler(string str);
public event BufferReadyHandler OnBufferReady;
public ClassPlugin(eGuiType _guyType)
{
GuiType = _guyType;
}
protected void Sp_DataReceived_Parent(object sender, SerialDataReceivedEventArgs e)
{
strCommonBuffer += serial.ReadExisting();
if (strCommonBuffer.Contains("\r\n"))
{
if (OnBufferReady != null) <<-------NULL
OnBufferReady(strCommonBuffer);
strCommonBuffer = string.Empty;
}
}
}
then there are some derived classes that are linked to that event:
class ClassIO : ClassPlugin
{
public ClassIO(eGuiType _guyType) : base(_guyType)
{
...
OnBufferReady += ClassIO_OnBufferReady;
}
private void ClassIO_OnBufferReady(string str)
{
...
}
}
the problem is that the OnBufferReady event in the parent class is alway null and therefore never fired.
Thanks for any help.
I might be wrong but have you thought about making the event static?
public delegate void BufferReadyHandler(string str);
public static event BufferReadyHandler OnBufferReady;
I am not sure why you are having this problem, I suspect it has something to do with the code you have not shown us. However in this situation I would not have the child subscribe to the event at all, instead make a protected method that raises the event that the child can override.
Here is how I would implement the class.
public class BufferReadyEventArgs : EventArgs
{
public BufferReadyEventArgs(string commonBuffer)
{
CommonBuffer = commonBuffer;
}
public string CommonBuffer {get; private set;}
}
Class Plugin()
{
public event EventHandler<BufferReadyEventArgs> OnBufferReady;
public ClassPlugin(eGuiType _guyType)
{
GuiType = _guyType;
}
protected void Sp_DataReceived_Parent(object sender, SerialDataReceivedEventArgs e)
{
strCommonBuffer += serial.ReadExisting();
if (strCommonBuffer.Contains("\r\n"))
{
RaiseOnBufferReady(strCommonBuffer);
strCommonBuffer = string.Empty;
}
}
protected virtual void RaiseOnBufferReady(string commonBuffer)
{
var temp = OnBufferReady;
if(temp != null)
temp(this, new BufferReadyEventArgs(commonBuffer));
}
}
class ClassIO : ClassPlugin
{
public ClassIO(eGuiType _guyType) : base(_guyType)
{
...
}
protected override void RaiseOnBufferReady(string commonBuffer)
{
base.RaiseOnBufferReady(commonBuffer);
...
}
}
Here is a working example based on your code:
using System;
using System.Collections.Generic;
public class MyClass
{
public static void Main()
{
ClassIO c = new ClassIO();
c.DataReceived();
Console.ReadLine();
}
}
public class ClassPlugin
{
public delegate void BufferReadyHandler(string str);
public event BufferReadyHandler OnBufferReady;
public ClassPlugin()
{
}
public void DataReceived()
{
if (OnBufferReady != null) {
OnBufferReady("Calling OnBufferReady");
}
}
}
public class ClassIO : ClassPlugin
{
public ClassIO() : base()
{
OnBufferReady += ClassIO_OnBufferReady;
}
private void ClassIO_OnBufferReady(string str)
{
Console.WriteLine("Inside ClassIO_OnBufferReady");
}
}
I don't understand why you would like to work with events in the first place for communication between parent and derived class.
If you need this communication, you would be better of with an (abstract) method in your base class that you implement in your derived classes.
If you need communication to all instances of derived types, you should look into composition instead of inheritance. Make some sort of manager instance that holds references to a list of instances of that base type and invokes a certain method on each of them in case of an 'event'.
I have Singleton class that get's data from web. And i need to pass those data to classes FirstDerived and SecondDerived. In this case is my class Singleton anti-pattern? Is it normal to use Aggregation relationship between DataSocket and FirstDerived, SecondDerived. Maybe it exist better object oriented solution?
namespace WpfApplication
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new TestViewModel();
}
}
public class TestViewModel
{
public ObservableCollection<Base> Items { get; set; }
public TestViewModel()
{
DataSocket.Instance.SendDataAsync();
Items = new ObservableCollection<Base>();
Items.Add(new FirstDerived(1, DataSocket.Instance));
Items.Add(new SecondDerived(2, DataSocket.Instance));
}
}
public abstract class Base
{
}
public class FirstDerived : Base, IDisposable
{
public FirstDerived(int id, DataSocket socket)
{
socket.Client += ProcessDataFromSocket;
}
public void ProcessDataFromSocket(string arg)
{
Console.WriteLine("First Derived getting data: {0}", arg.ToString());
}
public void Dispose()
{
throw new NotImplementedException();
}
}
public class SecondDerived : Base, IDisposable
{
public SecondDerived(int id, DataSocket socket)
{
DataSocket.Instance.Client += ProcessDataFromSocket;
}
public void ProcessDataFromSocket(string arg)
{
Console.WriteLine("Second Derived getting data: {0}", arg.ToString());
}
public void Dispose()
{
throw new NotImplementedException();
}
}
public sealed class DataSocket
{
private static DataSocket instance;
public delegate void Messages(string info);
public event Messages Client;
private DataSocket()
{
}
public void SendDataAsync()
{
Action Send = new Action(SendData);
IAsyncResult result = Send.BeginInvoke(null,null);
}
public void SendData()
{
while(true)
{
if (Client != null)
{
System.Threading.Thread.Sleep(3000);
Client("Test");
}
}
}
public static DataSocket Instance
{
get
{
if (instance==null)
{
instance = new DataSocket();
}
return instance;
}
}
}
}
To me it looks like DataSocket is the class that deals with networking, so any code to interact with the network should go there. Don't pass it into a constructor.
Rather, do something more like this sequence;
DataSocket.Instance.SendDataAsync();
Items = new ObservableCollection<Base>();
var data1 = await DataSocket.ReadData();
var data2 = await DataSocket.ReadData();
Items.Add(new FirstDerived(1, data1));
Items.Add(new SecondDerived(2, data2));
This way, your classes don't take a dependency on a global, constantly-changing object.
You might also want to consider some kind of lock statement to make sure that different parts of the code -- say, many different simultaneous web requests -- can't interfere with each other.
I just wanted to ask if the following code is a valid method to access the GUI from another class, or if it is bad practice. What I want to do is to write log messages into a RichTextBox in Form1.
If it's bad practice, would it be better to pass a reference of my Form1 to the other class to be able to access the RichTextBox.
I have the following code to access the GUI in my Form1 from another class:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Logger.Init(this.rtbLog);
MyOtherClass myOtherClass = new MyOtherClass();
myOtherClass.DoSomething();
}
}
public class MyOtherClass
{
public void DoSomething()
{
Logger.AppendText("text...");
Logger.AppendText("text...");
Logger.AppendText("text...");
}
}
public static class Logger
{
private static RichTextBox _rtb;
public static void Init(RichTextBox rtb)
{
_rtb = rtb;
}
public static void AppendText(String text)
{
_rtb.AppendText(text);
_rtb.AppendText(Environment.NewLine);
}
}
With Events (thanks to Ondrej):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Logger.EntryWritten += Logger_EntryWritten;
MyOtherClass myOtherClass = new MyOtherClass();
myOtherClass.DoSomething();
}
void Logger_EntryWritten(object sender, LogEntryEventArgs args)
{
rtbLog.AppendText(args.Message);
rtbLog.AppendText(Environment.NewLine);
}
}
public class MyOtherClass
{
public void DoSomething()
{
Logger.AppendText("text...");
Logger.AppendText("text...");
Logger.AppendText("text...");
}
}
public static class Logger
{
public static event EventHandler<LogEntryEventArgs> EntryWritten;
public static void AppendText(string text)
{
var tmp = EntryWritten;
if (tmp != null)
tmp(null, new LogEntryEventArgs(text));
}
}
public class LogEntryEventArgs : EventArgs
{
private readonly String message;
public LogEntryEventArgs(String pMessage)
{
message = pMessage;
}
public String Message
{
get { return message; }
}
}
It's probably fine for a small throw-away project, but otherwise a logger should not know anything about used platform. Then it would be good to use events for example. Raise an event whenever there's a new log entry written and consumers interested in logged entries will subscribe to a delegate.
Also be careful with threads. If you log a message from a different thread than UI you will end up with an exception because you would access a GUI control from a different thread which is forbidden.
EDIT:
Something along these lines. LogEntryEventArgs is a type you have to create and you can give it properties like Message, TimeWritten, Severity, etc.
public static class Logger
{
public static event EventHandler<LogEntryEventArgs> EntryWritten;
public static void AppendText(string text)
{
var tmp = EntryWritten;
if (tmp != null)
tmp(null, new LogEntryEventArgs(text));
}
}
consumer:
Logger.EntryWritten += Logger_OnEntryWritten;
void Logger_OnEntryWritten(object sender, LogEntryEventArgs args)
{
_rtb.AppendText(args.Message);
_rtb.AppendText(Environment.NewLine);
}
Also, don't forget to invoke on a form/dispatch the body of Logger_OnEntryWritten in order to avoid cross-thread access exception (in case you are considering using threads).