I'm currently building an application for iOS and Android using Xamarin and MonoTouch. In the application there is going to be a lot of data loaded from JSON, and therefore I wanted to incorporate a unified loader, an object that runs on application start to check whether it needs to re-download information or not.
The loading class is done and is fully functional, and has the following methods that I want to be able to bind events to. See below:
BeginLoading
ReloadPosts
ReloadLayers
ReloadRunners
FinishedLoading
These are all self contained and run in the loader class which I initiate in ViewDidLoad in my main screen (MainScreen.cs) using the following code:
var loader = new UnifiedLoader();
This starts the process of checking the local cache, last reload time etc and either starts the reloading process - posts, layers, runners or jumps straight to FinishedLoading.
What I'd like to be able to do is to listen for these "events" in some fashion, and I have no idea how to go about doing so. Please look below for an example.
var loader = new UnifiedLoader();
loader.LoadingDidBegin += () => {
Console.Out.WriteLine("Loading started");
// Display spinner or something...
};
loader.DidReloadPosts += () => {
Console.Out.WriteLine("Posts were reloaded");
// Update reloading percentage, show user...
};
loader.DidReloadLayers += () => {
Console.Out.WriteLine("Layers were reloaded");
// Update reloading percentage, show user...
};
loader.DidReloadRunners += () => {
Console.Out.WriteLine("Runners were reloaded");
// Update reloading percentage, show user...
};
loader.LoadingDidFinish += () => {
Console.Out.WriteLine("Loading finished");
// Remove spinner, proceed...
};
As of now I have no idea how I would go about implementing these events in the loading class. I've been searching and going through the API documentation but found nothing to aid me.
I would be more than thankful if someone could help me solve this.
Thanks in advance,
Jonathan
The preferred way would be to just write:
public EventHandler LoadingDidBegin;
This saves you from declaring the delegates and conforms to coding guidelines: http://msdn.microsoft.com/en-us/library/w369ty8x.aspx
I solved it by finding the Microsoft documentation for C# events. It was as simple as using the following code to register the event delegates and events.
This code goes outside of the class:
public delegate void LoadingDidBegin();
And this code goes inside the class:
public event LoadingDidBegin LoadingDidBegin;
And in the method where you want to invoke the event, call this:
// Trigger event:
if (this.CheckingDidBegin != null){
this.CheckingDidBegin ();
}
And last, in the class where you bind the event, bind the delegate like this:
var loader = new UnifiedLoader ();
loader.LoadingDidBegin += delegate {
// Do something here, show a HUD for instance...
};
loader.InitiateLoader ();
That's pretty much it, just remember to register the delegates before initiating the methods that carry the event triggers, otherwise they will just return null and you will get no feedback.
Good luck!
Related
Ok so I am aware there are some similar questions such as:
Adding and Removing Anonymous Event Handler
Unsubscribe anonymous method in C#
But I don't understand the concept of delegates.
I am starting to use the Plugin.BLE in a .Net Maui app.
The scanning operation is started from a button and then either times out (by use of a Timer) or is stopped by pressing the button again.
However in my button command (MVVM) I have the following snippet of code:
...
adapter.DeviceDiscovered += (s, a) =>
{
if (a.Device.Name != null && a.Device.Name != String.Empty)
{
...
}
};
await adapter.StartScanningForDevicesAsync();
...
I note that each time I hit the button I get two more discovered items (I'm not sure why I'm getting 2 yet?) (This is from Pixel 5 emulator)
This makes some kind of sense as I am adding another event to the same adapter!
So I need to convert the anonymous function
adapter.DeviceDiscovered += (s, a) =>
{
}
into a non anonymous function, so that I can add the handler and then remove it when the timer stops or I stop the function.
I have no idea how to go about this, especially in dealing with the s and the a.
I'd be grateful for any pointers, code.
Thanks, G.
Edit: link to Plguin.BLE https://github.com/xabre/xamarin-bluetooth-le
Well, I am truly astonished by Visual Studio.
After hacking away for a while trying to create functions and delegates I commented out the code and typed in
adapter.DeviceDiscovered +=
and visual studio created the rest of the code and the event handler for me.
So I have:
adapter.DeviceDiscovered += Adapter_DeviceDiscovered;
...
private void Adapter_DeviceDiscovered(object s, Plugin.BLE.Abstractions.EventArgs.DeviceEventArgs a)
{
Debug.WriteLine("Got here");
}
I changed the original sender to s and the original e to a to match the code I was using.
Well so far it seems to work.
Now all I need to do is figure out why this is getting called twice! :sigh. :)
I am using Unity 3D, however, that information should be irrelevant for solving this problem as the core problem is System.Delegate (I wanted to let you know as I'll be linking to some Unity docs for clarification).
I have a custom window that has a custom update function DirectorUpdate. I need this function to run every editor update regardless of what the user/window is doing.
In order for this to be called every editor update, I Combine my method with the Delegate EditorApplication.update:
protected void OnEnable()
{
// If I do the below, base EditorApplication.update won't be called anymore.
// EditorApplication.update = this.DirectorUpdate;
// So I need to do this:
EditorApplication.update = (EditorApplication.CallbackFunction)System.Delegate.Combine(new EditorApplication.CallbackFunction(this.DirectorUpdate), EditorApplication.update);
... // Other stuff
}
Note that this is done inside a window's OnEnable.
The problem is that OnEnable can be called more than once during a single run (for example, when closing the window and then reopening the window during a single editor session) causing
EditorApplication.update = (EditorApplication.CallbackFunction)System.Delegate.Combine(new EditorApplication.CallbackFunction(this.DirectorUpdate), EditorApplication.update);
to be called multiple times, meaning my update method (this.DirectorUpdate) will eventually get called multiple times per update, which is causing some serious bugs.
So, the question is how do I check if EditorApplication.update already has my method "inside" of it. (By inside of it, I of course mean it has already been System.Delegate.Combine(d) to the delegate.)
I am aware that there could be other solutions, for example restoring EditorApplication.update to what it was prior when the window closes, however that won't account for all situations (for example, OnEnable also gets called during window refresh and such) and the bug will persist. (Also, what if another window Concatenates with EditorApplication.update while this window is open?) As such, the best solution would be to check if EditorApplication.update is already callin this method BEFORE Delegate.Combine-ing it.
I think you took the complicated road ;)
Subscribing and unsubscribing events and delegates is as simple as using the operators += and -= like
protected void OnEnable()
{
// You can substract your callback even though it wasn't added so far
// This makes sure it is definitely only added once (for this instance)
EditorApplication.update -= DirectorUpdate;
// This basically internally does such a Combine for you
EditorApplication.update += DirectorUpdate;
... // Other stuff
}
private void OnDisable()
{
// Remove the callback once not needed anymore
EditorApplication.update -= DirectorUpdate;
}
This way you can also have multiple instances of this window open and they all will receive the callback individually.
Btw if this is actually about an EditorWindow then afaik you should not use OnEnabled but you would rather use Awake
Called as the new window is opened.
and OnDestroy.
I am not familiar with what System.Delegate.Combine(d) does, but you can consider instead of enabling/disabling your window, destroying and instantiating it every time, and move your code to the Start or the Awake for it to be called only once per window "activation".
Last but not least, use a mighty boolean in the OnDisable so that you can handle the combine execution if your component was ever disabled. Like so:
bool omgIWasDisabled;
protected void OnEnable()
{
if (!omgIWasDisabled) {
EditorApplication.update = (EditorApplication.CallbackFunction)System.Delegate.Combine(new EditorApplication.CallbackFunction(this.DirectorUpdate), EditorApplication.update);
}
... // Other stuff
}
void OnDisable() {
omgIWasDisabled = true;
}
Hope any of those works out.
I have two projects, one of them is my mainApplication and another is a classLibrary, where communicates with Hardware.
In the classLibrary I have an event, which is fired when x hardware is disconnected, on my mainApplication, I add that event to custom function, where
my custom control located on my mainApplication won't show up, but when call something like MessageBox, it shows ... Probably if I add that mainApplication to references on classLibrary it will work, but I don't want to do that.
Follow sample code
Class Library sat
public event ConnectedSATChanged connectionChanged;
MainApp
sat.connectionChanged += new
SAT.SATInternalFunctions.ConnectedSATChanged(Sat_connectionChanged);
private void Sat_connectionChanged()
{
MessageDialog.Show("WARNING"); // Wont show Or any other custom control
MessageBox.Show("Warning"); // Will Show ...
}
UPDATE
The follow code made it
var thread = new Thread(() =>
{
MessageDialog.Show("Warning!");
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
Without adding reference you need to find a way to trigger an event in the class library. Either WCF, web server or database can be used here.
I am dynamically instantiating a Form. I cannot interact with the components (such as a TextBox) on the Form until the handle has been created (else, an exception will be thrown).
Currently I block the thread using a while loop:
public void OutputDesktopFrame(MessagingService service, DesktopFrame desktopFrame)
{
IRemoteDesktopView view = GetView(service);
view.UpdateFrame(desktopFrame);
}
private IRemoteDesktopView GetView(MessagingService service)
{
T view;
bool viewExists = _views.TryGetValue(service, out view);
if (viewExists == false)
{
view = CreateAndShowView(service);
}
return view;
}
private T CreateAndShowView(MessagingService service)
{
T remoteDesktopView = new T();
_views.Add(service, remoteDesktopView);
Thread pumpThread = new Thread(() => remoteDesktopView.ShowDialog());
pumpThread.Start();
while (remoteDesktopView.IsHandleCreated == false)
{
//Do not return until the handle has been created!
}
return remoteDesktopView;
}
I do not like this mechanism. I am looking for an elegant solution.
Please take into account that I am coding against an interface. I thought about using a ManualResetEvent or something of the like but having to implement and handle the ManualResetEvent within each Form that implements the interface doesn't sound appealing to me. If you don't agree with me. that's just fine. I merely suspect my current solutions are not the most elegant.
You can add code to a HandleCreated event handler like this:
private void Form1_HandleCreated(object sender, EventArgs e){
//your code
}
The event is not listed in Properties window, you have to register the event handler using code:
HandleCreated += Form1_HandleCreated;
You have to wait for the handle to be created somehow.
So you will end up with something like while (form.IsHandleCreated == false) { ... } somewhere in your code.
The only question is where to put it.
If you do it like in your example above, you need to code the while loop every time you create a form If you choose the alternative you mentioned, using an event raised by the form, you need to implement it in each form (and create an event handler and hook it up).
I don't know if CreateAndShowForm() is a framework method, or something you can change yourself. If you can change it, that's where I would put the waiting. That way you only need to code it once.
Another approach to avoid the code duplication would be handling it in the form, implementing it in your own abstract form base class, and deriving you actual forms from that class. In my opinion, that is complete overkill for this issue - way too much work for very little gain.
If you can't change CreateAndShowForm(), I recommend going with the example above - yes, it definitely isn't elegant, but it gets the work done, the source code is easy to understand, and it doesn't require the additional work of event handling.
I am maintaining some code which has two FileSystemWatcher events that makes it difficult to debug (and it has an error). So my idea is to simplify the code by making the execution sequential. Pretty much like this:
Main method
1) normal code here
2) enable event 1, let it check for files, disable it when it is done running once
3) enable event 2, let it check for files, disable it when it is done running once
Then the database logs would make more sense. I would be able to see which part of the program that is doing something wrong.
private void InitializeFileSystemWatcher()
{
this.MessageMonitor = new FileSystemWatcher(this.MessagePath, this.MessageFilter);
this.MessageMonitor.IncludeSubdirectories = true; // Recursive.
this.MessageMonitor.Created += new FileSystemEventHandler(OnMessageReceived);
this.MessageMonitor.EnableRaisingEvents = true;
}
From the main, I can set the EnableRaisingEvents=true to EnableRaisingEvents=false. Both events indexes the files in some folder and enacts a callback method.
My question is this: If the event is currently executing and I set EnableRaisingEvents=false, will it pause or continue to execute until it finishes?
If it does continue, I figure just to have a bool doRUN variable set at beginning and the end of the event as a check for the main method.
You should just detach the event handler after you check to make sure that it is working properly and then instantiate the second FileSystemWatcher.
Inside of the OnMessageReceived you could od something like
public void OnMessageRecieved(Object sender, Events e) //Not the real signature
{
MessageMonitor.Created -= OnMessageReceived();
//Do Your things
OtherMessageMonitor.Created += OnMessageReceived();
}