I'm trying to write a program that could monitor multiple folders for file creations and launch the same action but with different settings for each folder. My problem is in specifying an extra parameter for the FileSystemEventHandler. I create a new FileWatcher for each directory to monitor and add the handler for the Created-action:
foreach (String config in configs)
{
...
FileWatcher.Created += new System.IO.FileSystemEventHandler(FileSystemWatcherCreated)
...
}
void FileSystemWatcherCreated(object sender, System.IO.FileSystemEventArgs e, MySettings mSettings)
{
DoSomething(e.FullPath, mSettings);
}
How could I get the 'mSettings' variable passed to FileSystemWatcherCreated()?
foreach (String config in configs)
{
...
MySettings mSettings = new MySettings(...); // create a new instance, don't modify an existing one
var handler = new System.IO.FileSystemEventHandler( (s,e) => FileSystemWatcherCreated(s,e,msettings) );
FileWatcher.Created += handler;
// store handler somewhere, so you can later unsubscribe
...
}
void FileSystemWatcherCreated(object sender, System.IO.FileSystemEventArgs e, MySettings mSettings)
{
DoSomething(e.FullPath, mSettings);
}
foreach (String config in configs)
{
...
FileWatcher.Created += (s,e) => DoSomething(e.FullPath, mSettings);
...
}
You can't ask for more information than what the FileWatcher handler provides. What you can do however is to create a small classes that have access to the configuration and also have a delegate that you can attach to the FileWatcher's Created event
class Program
{
static void Main(string[] args)
{
FileSystemWatcher watcher = new FileSystemWatcher("yourpath");
var configurations = new IConfiguration[]
{
new IntConfiguration(20),
new StringConfiguration("Something to print")
};
foreach(var config in configurations)
watcher.Created += config.HandleCreation;
}
private interface IConfiguration
{
void HandleCreation(object sender, FileSystemEventArgs e);
}
private class IntConfiguration : IConfiguration
{
public IntConfiguration(int aSetting)
{
ASetting = aSetting;
}
private int ASetting { get; set; }
public void HandleCreation(object sender, FileSystemEventArgs e)
{
Console.WriteLine("Consume your settings: {0}", ASetting);
}
}
public class StringConfiguration : IConfiguration
{
public string AnotherSetting { get; set;}
public StringConfiguration(string anotherSetting)
{
AnotherSetting = anotherSetting;
}
public void HandleCreation(object sender, FileSystemEventArgs e)
{
Console.WriteLine("Consume your string setting: {0}", AnotherSetting);
}
}
}
You need to understand what you are using. FileSystemEventHandler's definition is-
public delegate void FileSystemEventHandler(object sender, FileSystemEventArgs e);
You can't pass the third argument.
In order to pass the data 'mSettings', you might have to write your own extra code, I'm afraid.
Related
I have a windows service that basically monitors a folder, copies files to a local directory processes the files. The service starts up fine enough calls the Watcher Class and does all of the configuration like it should. So my code works for the setup. However I get 0 events firing. I am wondering if when my StartFolderWatcher() method goes out of scope that my object - even though declared at the class level is somehow getting disposed.
So do I need to run the object continuously in a separate thread ? If so can I get an example of how I should do that Task.Run(()=> myObject)
Relevant Code is below - if more is needed just ask and I will post it.
static class Program
{
static void Main(string[] args)
{
JobProcessor service = new JobProcessor();
if (Environment.UserInteractive)
{
service.RunAsConsole(args);
}
}
private static bool IsDebugMode(string[] args)
{
if (args == null) return false;
if (args[0].ToLower() == "/debug") return true;
return false;
}
}
public partial class JobProcessor : ServiceBase
{
string[] folders = new string[] {"InBoxFolder"};
HotFolderWatch HFW = new HotFolderWatch();
public JobProcessor()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
StartFolderWatcher();
}
public void StartFolderWatcher()
{
FileWatcherInfo[] ServiceWatchers = new FileWatcherInfo[4];
ServiceConfiguration sc = new ServiceConfiguration();
for (int i = 0; i < folders.Length; i++)
{
ServiceWatchers[i] = sc.GetWatchFolderSettings(folders[i]);
}
HFW = new HotFolderWatch(ServiceWatchers[0]);
HFW.ReadyToProcess += ReadyToProcess;
HFW.InBoxFolderDisconnected += OnInBoxFolderDisconnected;
HFW.LocalFolderDisconnected += OnLocalFolderDisconnected;
HFW.ProcessFolderDisconnected += OnProcessFolderDisconnected;
}
public void RunAsConsole(string[] args)
{
OnStart(args);
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
OnStop();
}
}
public HotFolderWatch(FileWatcherInfo inbox)
{
this.InboxCacheTimeMilliseconds = inbox.CacheTimeMilliseconds;
this.InBoxFolder = inbox.Folder.Trim();
this.InboxFileFilter = inbox.Filter.Trim();
SetInboxWatcher();
}
private void SetInboxWatcher()
{
InBoxWatcher = new FileSystemWatcher(InBoxFolder, InboxFileFilter);
InBoxWatcher.IncludeSubdirectories = false;
InBoxWatcher.NotifyFilter =
NotifyFilters.LastAccess | NotifyFilters.LastWrite;
InboxCache = MemoryCache.Default;
InboxCachePolicy = new CacheItemPolicy()
{
RemovedCallback = OnRemovedFromInBoxCache
};
InBoxWatcher.Created += new FileSystemEventHandler(OnInBoxChanged);
InBoxWatcher.EnableRaisingEvents = true;
}
private void OnInBoxChanged(object source, FileSystemEventArgs e)
{
InboxCachePolicy.AbsoluteExpiration =
DateTimeOffset.Now.AddMilliseconds(InboxCacheTimeMilliseconds);
InboxCache.AddOrGetExisting(e.Name, e, InboxCachePolicy);
}
}
If in my HotFolderWatch Class I create a FileSystemWatcher in a method as below the code works!
private void CreateWatcher()
{
FileSystemWatcher fsw = new FileSystemWatcher(#"C:\Tests\JobQueue\InFolder","*.txt");
fsw.Created += Fsw_Created;
fsw.EnableRaisingEvents = true;
}
private void Fsw_Created(object sender, FileSystemEventArgs e)
{
string ex = e.FullPath;
WatcherChangeTypes ctmp = e.ChangeType;
// throw new NotImplementedException();
}
Turns out the default constructor of FileSystemWatcher uses the filter FileName by default and I was thinking I would get a last Write or Last Access with out concerning myself with the Name.
I received good information from here :
https://www.codeproject.com/Articles/1220094/NotifyFilters-Enumeration-Explained-FileSystemWatc
The article posted in the link is what helped me to solve my issue.
I had to Ste the NotifyFilters.FileName as one of the filters.
I currently have an installation "framework" that does specific things. What I need now to do is be able to call my form in parallel with my script. Something like this:
InstallationForm f = new InstallationForm();
Application.Run(f);
InstallSoftware(f);
private static void InstallSoftware(InstallationForm f) {
f.WriteToTextbox("Starting installation...");
Utils.Execute(#"C:\temp\setup.msi", #"-s C:\temp\instructions.xml");
...
f.WriteToTextbox("Installation finished");
The current way I can do this is by adding the Form.Shown handler in InstallSoftware, but that seems really messy. Is there anyway I can do this better?
Your code will not work, because Application.Run(f) returns not until the form was closed.
You may use a simplified Model/View/Controller pattern. Create an InstallationFormController class that has several events, e.g. for textual notifications to be written to your textbox. The InstallationForm registers on these events in it's OnLoad() method and then calls InstallationFormController.Initialize(). That method starts your installation (on a worker thread/task). That installation callback method fires several text events.
InstallationForm f = new InstallationForm(new InstallationFormController());
Application.Run(f);
internal class InstallationFormController
{
public event EventHandler<DataEventArgsT<string>> NotificationTextChanged;
public InstallationFormController()
{
}
public void Initialize()
{
Task.Factory.StartNew(DoInstallation);
}
private void DoInstallation()
{
...
OnNotificationTextChanged(new DataEventArgsT<string>("Installation finished"));
}
private void OnNotificationTextChanged(DataEventArgsT<string> e)
{
if(NotificationTextChanged != null)
NotificationTextChanged(this, e);
}
}
public class DataEventArgsT<T> : EventArgs
{
...
public T Data { get; set; }
}
internal class InstallationForm : Form
{
private readonly InstallationFormController _controller;
public InstallationForm()
{
InitializeComponent();
}
public InstallationForm(InstallationFormController controller) : this()
{
if(controller == null)
throw new ArgumentNullException("controller")
_controller = controller;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
_controller.NotificationTextChanged += Controller_NotificationTextChanged;
_controller.Initialize();
}
protected virtual void Controller_NotificationTextChanged(object sender, DataEventArgsT<string> e)
{
if(this.InvokeRequired)
{ // call this method on UI thread!!!
var callback = new EventHandler<DataEventArgsT<string>>(Controller_NotificationTextChanged);
this.Invoke(callback, new object[] {sender, e});
}
else
{
_myTextBox.Text = e.Data;
}
}
...
}
I am building an app, where I will watching the files. I am pretty fresh in programming so I (maybe) have little issue, because I dont have some real practice. My program its working, but I dont know how is usally implemented in "real" software. Please overview and comment. Because I like to have much as is possible in DateFiles class and less is possible in MainViewModel.
Yes app in build on MVVM base.
My Current state:
In my MainViewModel
public static string ConfigurationFilesSourcePath2;
private void InitializeFiles()
{
// New instance of DateFiles
DF = new DataFiles();
// Path to DateFiles
DF.ConfigurationFilesSourcePath = ConfigurationFilesSourcePath;
// Run Initialization method to establish "filewatching"
DF.InitializeFiles();
// Refresh ListView in View
RefreshFileList();
// Assign a Handler to PropertyChanged event
DF.PropertyChanged += DF_PropertyChanged;
}
// If something change inside DateFiles
private void DF_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
RefreshFileList();
}
// Refresh file list
public void RefreshFileList()
{
fileslist = new ObservableCollection<Files>();
foreach (var item in DF.fileslist)
{
fileslist.Add(item);
}
NotifyPropertyChanged("fileslist");
}
DataFiles class:
public class DataFiles : ViewModelBase
{
public FileSystemWatcher filewatcher;
public string ConfigurationFilesSourcePath;
public ObservableCollection<Files> fileslist { get; set; } = new ObservableCollection<Files>();
public void InitializeFiles()
{
// Create a new FileSystemWatcher
filewatcher = new FileSystemWatcher();
// Set filter to only catch XAL files
filewatcher.Filter = "*.txt";
// Set the path
filewatcher.Path = ConfigurationFilesSourcePath;
// Subscribe to the Created event
filewatcher.Created += new FileSystemEventHandler(FileOnchanged);
filewatcher.Changed += new FileSystemEventHandler(FileOnchanged);
filewatcher.Deleted += new FileSystemEventHandler(FileOnchanged);
filewatcher.Renamed += new RenamedEventHandler(FileOnRenamed);
// Enable the FileSystemWatcher events
filewatcher.EnableRaisingEvents = true;
RefreshFilesList();
}
private void FileOnchanged(object sender, FileSystemEventArgs e)
{
RefreshFilesList();
}
private void FileOnRenamed(object sender, RenamedEventArgs e)
{
RefreshFilesList();
}
public void RefreshFilesList()
{
fileslist.Clear();
DirectoryInfo dir = new DirectoryInfo(ConfigurationFilesSourcePath);
string[] extensions = new[] { ".txt" };
int nof = 0;
foreach (FileInfo file in dir.GetFiles().Where(f => extensions.Contains(f.Extension.ToLower())).ToArray())
{
nof++;
fileslist.Add(new Files()
{
FileId = nof,
FileName = file.Name,
FileChanged = file.LastWriteTime.ToString(),
FileCreated = file.CreationTime.ToString(),
OnlyNameWithoutExtension = Path.GetFileNameWithoutExtension(file.Name)
});
NotifyPropertyChanged("fileslist");
}
}
There are several issues with your code. Among most important ones - you rebuild whole file list on every change (while FileSystemEventArgs arguments provide you with info about what has really changed and where) and you don't update your ObservableCollection on UI thread. Look at the following code (but keep in mind that is just a sample for you to spot some problems):
public class DataFiles : IDisposable {
public FileSystemWatcher filewatcher;
private readonly object fileListLock = new object();
// you don't need public setter on this
public ObservableCollection<Files> Fileslist { get; } = new ObservableCollection<Files>();
// pass path here, no need to use property
public void InitializeFiles(string path) {
// dispose existing watcher, if any
DisposeWatcher();
// Create a new FileSystemWatcher
filewatcher = new FileSystemWatcher();
// Set filter to only catch XAL files
filewatcher.Filter = "*.txt";
// Set the path
filewatcher.Path = path;
// Subscribe to the Created event
filewatcher.Created += new FileSystemEventHandler(FileOnchanged);
filewatcher.Changed += new FileSystemEventHandler(FileOnchanged);
filewatcher.Deleted += new FileSystemEventHandler(FileOnchanged);
filewatcher.Renamed += new RenamedEventHandler(FileOnRenamed);
// don't RefreshFilesList on UI thread, that might take some time and will block UI
Task.Run(() => RefreshFilesList());
// Enable the FileSystemWatcher events
filewatcher.EnableRaisingEvents = true;
}
private void FileOnchanged(object sender, FileSystemEventArgs e) {
// lock here to avoid race conditions with RefreshFilesList
lock (fileListLock) {
// better use dictionary to avoid looping over all files
// but looping is still much better than rebuilding whole list
var file = Fileslist.FirstOrDefault(c => String.Equals(c.FullPath, e.FullPath, StringComparison.OrdinalIgnoreCase));
if (file != null) {
if (e.ChangeType == WatcherChangeTypes.Deleted)
; // delete
else
; // update file properties
}
else {
// add new, unless event is delete
}
}
}
private void FileOnRenamed(object sender, RenamedEventArgs e) {
lock (fileListLock) {
// better use dictionary to avoid looping over all files
var file = Fileslist.FirstOrDefault(c => String.Equals(c.FullPath, e.OldFullPath, StringComparison.OrdinalIgnoreCase));
if (file != null) {
file.FullPath = e.FullPath;
}
else {
// add new
}
}
}
public void RefreshFilesList() {
// you need to lock here, because there is a race condition between this method and FileOnRenamed \ FileOnChanged,
// and you might lose some updates or get duplicates.
lock (fileListLock) {
// update ObservableCollection on UI thread
OnUIThreadDo(() => {
Fileslist.Clear();
});
DirectoryInfo dir = new DirectoryInfo(filewatcher.Path);
int nof = 0;
var files = new List<Files>();
// just use EnumerateFiles
foreach (FileInfo file in dir.EnumerateFiles("*.txt")) {
nof++;
int tmp = nof;
// if you are working with UI (that is most likely the case if you use ObservableCollection) -
// you need to update that collection from UI thread if you have bound controls
files.Add(new Files() {
FileId = tmp,
FullPath = file.FullName,
FileChanged = file.LastWriteTime,
FileCreated = file.CreationTime,
});
// don't do that
// NotifyPropertyChanged("fileslist");
}
// publish them all to collection on UI thread
OnUIThreadDo(() => {
foreach (var file in files)
Fileslist.Add(file);
});
}
}
private void OnUIThreadDo(Action a) {
if (Application.Current.CheckAccess())
a();
else
Application.Current.Dispatcher.BeginInvoke(a);
}
public void Dispose() {
DisposeWatcher();
}
private void DisposeWatcher() {
if (filewatcher != null) {
filewatcher.EnableRaisingEvents = false;
filewatcher.Created -= FileOnchanged;
filewatcher.Deleted -= FileOnchanged;
filewatcher.Changed -= FileOnchanged;
filewatcher.Renamed -= FileOnRenamed;
filewatcher.Dispose();
}
}
}
public class Files : INotifyPropertyChanged
{ // implement INotifyPropertyChanged, because you need to reflect property changes in UI
public int FileId { get; set; }
public string FullPath { get; set; }
public string FileName => Path.GetFileName(FullPath);
public DateTime FileChanged { get; set; }
public DateTime FileCreated { get; set; }
public string OnlyNameWithoutExtension => Path.GetFileNameWithoutExtension(FullPath);
}
Definittion of ProgressChanged:
// Summary:
// Event called whenever the progress of the upload changes.
public event Action<IUploadProgress> ProgressChanged;
public void insertFile(String filePath)
{
//.. some code
insertRequest.ProgressChanged += Upload_ProgressChanged;
}
public void Upload_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
//.. I need filePath from insertFile() here!
}
How to pass additional paramtres to Upload_ProgressChanged ?
I did the following:
public void insertFile(String filePath)
{
//.. some code
ProgressChangedEventArgs args = new ProgressChangedEventArgs()
{
path = filePath
};
insertRequest.ProgressChanged += Upload_ProgressChanged;
}
static void Upload_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
}
public class ProgressChangedEventArgs : EventArgs
{
public string path { get; set; }
}
And I have mistake Can not implicitly convert type 'void' to 'System.Action<Google.Apis.Upload.IUploadProgress>'
Instead of using an event you can capture the variable inside a closure
insertRequest.ProgressChanges += progress => { /* Do something with filePath here */ };
Firstly, define the EventArgs class - this will let you have whatever information you like...
public class ProgressChgEventArgs : System.EventArgs
{
public string Name { get;set; }
public int InstanceId { get;set; }
public ProgressChgEventArgs(string name, int id)
{
Name = name;
InstanceId = id;
}
}
Next, create the event that consumes these arguments:
public event EventHandler<ProgressChgEventArgs> ProgressChanged;
Then, have an 'On....' method that invokes the handlers
public void OnProgressChanged(ProgressChgEventArgs e)
{
var handler = new EventHandler<ProgressChgEventArgs>();
if (handler != null)
handler(this, e);
}
Now, at the relevant point in your code (presumably when the progress changes!) you call OnProgressChanged(), passing in an appropriate instance of the ProgressChgEventArgs:
private void Progress(string caller, int callerId)
{
var arguments = new ProgressChgEventArgs(caller, callerId);
OnProgressChanged(arguments);
}
I'm trying to implement simple Event Bus
I started like this:
public class RegistrationData
{
public object RegisteredObject { get; set; }
public Type eventType { get; set; }
public EventHandler MethodToInvoke;
}
public class EventBus
{
private static EventBus instance;
private static readonly object lockObject = new object();
private static List<RegistrationData> registrationList;
private EventBus()
{
}
public static EventBus Instance
{
get
{
lock (lockObject)
{
registrationList = new List<RegistrationData>();
return instance ?? new EventBus();
}
}
}
public void Subscribe(RegistrationData registrationData)
{
if(!registrationList.Contains(registrationData)) registrationList.Add(registrationData);
}
public void Unsubscribe(object objectToUnregister, Type eventType)
{
foreach(RegistrationData data in registrationList)
if (data.RegisteredObject == objectToUnregister && data.eventType == eventType) registrationList.Remove(data);
}
public void UnregisterAllMessages(object objectToUnregister)
{
foreach(RegistrationData data in registrationList)
if(data.RegisteredObject == objectToUnregister) registrationList.Remove(data);
}
public void PublishEvent(object sender, EventArgs eventArgs)
{
foreach (RegistrationData data in registrationList)
if (EventArgs is typeof(data.Type)) data.MethodToInvoke(sender, eventArgs);
}
}
But I have problem in PublishEvent method. I'm unable to determine type of event argument.
And I'm suspicious that all this is pretty wrong.
Can someone point out what I do wrong, how should I implement this?
Or how event bus is generally implemented, or some framework which I can use instead of implementing my own and spending time on that.
Um, not really sure how your Eventbus should behave. Short of knowing where you are heading it can be useful to look at how other people implemented the problem.
There is a down-to-earth event aggregator in the caliburn.micro project
I like to use MemBus when I need an event aggregator, partly because I wrote it myself, partly because it covers all my needs in that respect. It is more involved than caliburn's one, but then it has more features
I think you should start by defining Event Bus. What do you see as the difference between an Event Bus and the built-in .NET mechanisms for firing and sinking events? What you have so far looks like it implements not much more than the equivalent of .NET events. .NET intrinsically supports event handling so you wouldn't need an event bus if you don't need more than what .NET already provides:
class Program
{
static void Main(string[] args)
{
BusinessObject1 bo = new BusinessObject1("First Value");
// Subscribe
bo.Publish += new BusinessObject.PublishObject(bo_Publish);
bo.Update("Second Value");
// UnSubscribe
bo.Publish -= new BusinessObject.PublishObject(bo_Publish);
bo.Update("Third Value");
// Subscribe multiple
bo.Publish += new BusinessObject.PublishObject(bo_Publish);
bo.Publish += new BusinessObject.PublishObject(bo_Publish2);
bo.Update("Fourth Value");
// UnregisterAllMessages
bo.UnsubcribeAll();
bo.Update("Fifth Value");
}
static void bo_Publish(BusinessObject sender, EventArgs args)
{
if (sender is BusinessObject1)
{
BusinessObject1 bo1 = (BusinessObject1)sender;
BusinessObject1.PublishBusinessObject1EventArgs args1 =
(BusinessObject1.PublishBusinessObject1EventArgs)args;
Console.WriteLine("Updated {0} to {1}", args1.oldValue, bo1.Value);
}
}
static void bo_Publish2(BusinessObject sender, EventArgs args)
{
if (sender is BusinessObject1)
{
BusinessObject1 bo1 = (BusinessObject1)sender;
BusinessObject1.PublishBusinessObject1EventArgs args1 =
(BusinessObject1.PublishBusinessObject1EventArgs)args;
Console.WriteLine("Second handler detected updated of {0} to {1}", args1.oldValue, bo1.Value);
}
}
}
abstract class BusinessObject
{
public delegate void PublishObject(BusinessObject sender, EventArgs args);
public event PublishObject Publish;
// PublishEvent
protected void Update(EventArgs args)
{
if (Publish != null)
Publish(this, args);
}
public void UnsubcribeAll()
{
Publish = null;
}
}
class BusinessObject1 : BusinessObject
{
public class PublishBusinessObject1EventArgs : EventArgs
{
public string oldValue;
public PublishBusinessObject1EventArgs(string oldValue)
{
this.oldValue = oldValue;
}
}
public delegate void PublishBusinessObject1(BusinessObject1 sender, PublishBusinessObject1EventArgs args);
public string Value {get; private set;}
public BusinessObject1(string value)
{
this.Value = value;
}
public void Update(string newValue)
{
PublishBusinessObject1EventArgs args = new PublishBusinessObject1EventArgs(Value);
Value = newValue;
base.Update(args);
}
}
Edit:
If you don't want your business objects to have to inherit from a common base class (as you suggested in your comment) you can make a few modifications so that EventBus stands more independently, but you still don't need to re-implement all the event registration framework to do this:
class Program
{
static void Main(string[] args)
{
BusinessObject1 bo = new BusinessObject1("First Value");
// Subscribe
EventBus.Publish += new EventBus.PublishObject(EventBus_Publish);
bo.Update("Second Value");
// UnSubscribe
EventBus.Publish -= new EventBus.PublishObject(EventBus_Publish);
bo.Update("Third Value");
// Subscribe multiple
EventBus.Publish += new EventBus.PublishObject(EventBus_Publish);
EventBus.Publish += new EventBus.PublishObject(EventBus_Publish2);
bo.Update("Fourth Value");
// UnregisterAllMessages
EventBus.UnsubcribeAll();
bo.Update("Fifth Value");
}
static void EventBus_Publish(object sender, EventArgs args)
{
if (sender is BusinessObject1)
{
BusinessObject1 bo1 = (BusinessObject1)sender;
BusinessObject1.PublishBusinessObject1EventArgs args1 =
(BusinessObject1.PublishBusinessObject1EventArgs)args;
Console.WriteLine("Updated {0} to {1}", args1.oldValue, bo1.Value);
}
}
static void EventBus_Publish2(object sender, EventArgs args)
{
if (sender is BusinessObject1)
{
BusinessObject1 bo1 = (BusinessObject1)sender;
BusinessObject1.PublishBusinessObject1EventArgs args1 =
(BusinessObject1.PublishBusinessObject1EventArgs)args;
Console.WriteLine("Second handler detected updated of {0} to {1}", args1.oldValue, bo1.Value);
}
}
}
static class EventBus
{
public delegate void PublishObject(object sender, EventArgs args);
public static event PublishObject Publish;
// PublishEvent
public static void Update(object sender, EventArgs args)
{
if (Publish != null)
Publish(sender, args);
}
public static void UnsubcribeAll()
{
Publish = null;
}
}
class BusinessObject1
{
public class PublishBusinessObject1EventArgs : EventArgs
{
public string oldValue;
public PublishBusinessObject1EventArgs(string oldValue)
{
this.oldValue = oldValue;
}
}
public delegate void PublishBusinessObject1(BusinessObject1 sender, PublishBusinessObject1EventArgs args);
public string Value { get; private set; }
public BusinessObject1(string value)
{
this.Value = value;
}
public void Update(string newValue)
{
PublishBusinessObject1EventArgs args = new PublishBusinessObject1EventArgs(Value);
Value = newValue;
EventBus.Update(this, args);
}
}
Edit 2: By the way, if you want more control over the subscription process, you can get more control there too by defining custom event accessors as described at http://msdn.microsoft.com/en-us/library/bb882534.aspx:
static class EventBus
{
public delegate void PublishObject(object sender, EventArgs args);
private static List<PublishObject> subscribers = new List<PublishObject>();
public static event PublishObject Publish
{
add
{
subscribers.Add(value);
Console.WriteLine("Added subscriber {0}.{1}", value.Method.DeclaringType.Name, value.Method.Name);
}
remove
{
bool result = subscribers.Remove(value);
Console.WriteLine("Removed subscriber {0}.{1} ({2})", value.Method.DeclaringType.Name, value.Method.Name, result ? "success" : "failure");
}
}
// PublishEvent
public static void Update(object sender, EventArgs args)
{
foreach (PublishObject p in subscribers)
{
Console.WriteLine("Publishing to {0}.{1}", p.Method.DeclaringType.Name, p.Method.Name);
p.Invoke(sender, args);
}
}
public static void UnsubcribeAll()
{
subscribers.Clear();
}
}
Well, as a first suggestion, it looks to me as if you're trying to implement this as a singleton. Otherwise, what would the
private static EventBus instance;
be good for? But the private instance member is never assigned anywhere, that's one thing I'd suggest you should fix. For reference, here's a really good article on various implementations of singletons. If you've got access to .net4 I'd suggest you use the LazySingleton3 approach.
The only other thing that comes to mind is, this looks like it could be a use-case for Generics. Have a look at the EventHandler<TEventArgs> Delegate.
Apart from that, I can't recommend much more since I don't exactly understand what you're trying to do.
EDIT
Have a look at the accepted answer on this question. It contains a link to a blog post of someone who implemented this several years ago. Seems like you don't need to re-invent the wheel.