Capturing the OnExit event in WPF with MVVMCross - c#

I need to explicitly close and dispose of a SerialPort object when my MVVMCross WPF application exits, i.e. when the red X is clicked. In order to do this, I need to call a method of the current ViewModel.
I have been trying to do this by following the MVVMCross n=42 video and adding an IKillable interface with a public abstract void KillMe() method in an abstract BaseViewModel class that implements IKillable. Then, in the WPF project I added a BaseView class, exactly as he does in the video. I am able to access methods of the current view model at this point.
In the video, the examples are IOS, Droid, and Windows Phone, where one can override events such as OnNavigatedTo, etc. In WPF, there is no OnExit() which I could override available in the BaseView class.
I am wondering, is there any way that I can call this method of the current view model when the application exits? I am able to do this:
protected override void OnExit(ExitEventArgs e)
{
// Do Something
base.OnExit(e);
}
in the App.Xaml.Mvx.cs class that came with the MVVMCross Nuget package, and I believe that this is the right place to put the code I want to execute when the program exits. The problem is that I do not have any reference to the current viewmodel.
If anyone could help me out I would really appreciate it. Thanks!
EDIT/UPDATE:
I found a work around, it seems somewhat "hacky" but it gets the job done. The class which holds the reference to the SerialPort I need to close is registered as a singleton within MVVMCross. So, I added a field of the interface type of that singleton to the App.Xaml.Mvx.cs class, then at the end of DoSetup(), I call Mvx.Resolve<>(); on that type and assign that result to the field I created. Then, I have the protected override void OnExit(ExitEventArgs e), where I can call the Close() and Dispose() methods on the field. This does what I need it to do.
I am still interested if there is a better, "more correct" way to do it with MVVMCross, however.

If you close an app the OnSuspending method is called and there I would write code for a SerialPort and because you want to fire a method of current ViewModel I would just have object representing current ViewModel in the App.xaml.cs so I can call method of it inside OnSuspending()
I would do it this way, but I don't know if it's better or can do what you need. But maybe it will give you an idea.

Related

C# Xamarin - AddTarget to UILongPressGestureRecognizer passing it a Selector implemented in a parent UIViewController

I am having difficulties raising an event from a UICollectionViewCell to a handler which exists within a parent UIViewController. I am trying to detect a LongPress upon a UICollectionViewCell and fire a method in the UIViewController - placing the view into an "Edit Mode" show/hiding UI elements and reloading etc. To do so my UIViewController method must call a data access layer and thus it makes sense the method is placed at this level in the view hierarchy.
I have followed the official documentation and techniques from Xamarin and the great advice from Adam Kemp here.
There are so many conflicting methodologies for achieving this in Xamarin.IOS. Alas, at present, none seem to work in my case. I must be doing something fundamentally wrong!
I have a custom class which inherits from UICollectionViewCell which contains a private UILongPressGestureRecognizer _recognizer; and method named UpdateCell as follows:
public virtual void UpdateCell(bool CanEdit)
{
// Other properties and logic omitted for brevity (no relevance to question)
this._recognizer = new UILongPressGestureRecognizer();
this._recognizer.MinimumPressDuration = 0.5;
this._recognizer.AddTarget(this, new ObjCRuntime.Selector("OnEditModeActivated"));
this.AddGestureRecognizer(_recognizer);
}
I want the Long press to fire the following method within my UIViewController:
[Export("OnEditModeActivated")]
public async void OnEditModeActivated()
{
if (recognizer.State == UIGestureRecognizerState.Ended)
{
this._canEdit = true;
// call Data Access layer using await
_source.Dispose(); // dispose of current UICollectionViewSource object
_source = new UICollectionViewSource(_data, _canEdit);
UICollectionView.Source = _source;
}
}
The line causing issues is
`this._recognizer.AddTarget(this, new ObjCRuntime.Selector("OnEditModeActivated:"));`
The selector should point to the parent UIViewController which has a public void OnEditModeActivated decorated with [Export("OnEditModeActivated:")].
I can see the compiler running into the "unrecognized selector" exception due to this (1st arg passed to AddTarget) obviously being the UICollectionViewCell rather than the UIViewController and thus it will look to resolve AddTarget by UICollectionViewCell (instance) -> OnEditActivated (does not exist).
Instead of this I need to reference the parent UIViewController but I don't want to pass it by reference to the UpdateCell method - this would mean passing the reference through many layers thus my code becoming somewhat spaghetti like.
If anyone can explain a better way, by all means feel free. I looked at raising an event within the UICollectionViewCell instance but to no avail. Moreover I can't seem to get the delegation pattern right in Xamarin!
The problem is the hierarchy I am using being: UIViewController -> UICollectionView -> UICollectionViewSource -> UICollectionViewCell.
I am sure it is possible to raise an event/call delegate/call method and resolve at the view controller level...
Thanks for taking the time to help on this.
All the best,
John
Wiring up the _recognizer to the ContentView and declaring a method within this fixed my issue.
I changed:
this.AddGestureRecognizer(_recognizer);
to:
cell.ContentView.AddGestureRecognizer(_recognizer);
and created a OnLongPressed method at the same level decorated with Export["OnLongPressed:"] which raises an event resolved at theUIViewController` level.
As an aside, the UILongPressGestureRecognizer raises mutliple events and one should check the long press has actually ended otherwise any further events / method calls will occur more than once, for example:
if(recognizer.State == UIGestureRecognizerState.Ended)
{
// Raise events call methods here only when the long press ended
}
UILongPressGestureRecognizers will fire with multiple states which you can test using the State property documentation here.

ViewModel's constructor is being called again on navigation, and so messenger subscriptions are being subscribed again

I am building a cross-platform mobile application using MvvmCross framework.
Since I would like to share information between ViewModels, I am registering notifications inside the ViewModel's constructor, using the built in MvxMessenger.
Let's assume a message named ShowAdsMsg, and then the ViewModel looks as follows:
public class AdsViewModel : BaseLookersViewModel, IAdsViewModel
{
private MvxSubscriptionToken _showAdsMsgToken;
public AdsViewModel()
{
_showAdsMsgToken = MvxMessenger.Subscribe<ShowAdsMsg>(message => onShowAdsNavigation(), MvxReference.Weak);
MyMessenger.PublishLastMessage();
}
private void onShowAdsNavigation()
{
//Do Stuff
}
}
About the MyMessenger thing:
The actual navigation to the ViewModel is performed from MainViewModel.
Since that at the very moment of the navigation itself the AdsViewModel does not exist yet, messages published from the MainViewModel cannot reach it.
So, my idea was to naively "remember" the message and publish it when the new ViewModel is ready.
So now the navigation call from the MainViewModel looks like that:
private void navigate()
{
MyMessenger.RememberMessage(new ShowAdsMsg(this));
ShowViewModel<AdsViewModel>( );
}
I am now able to navigate to the ViewModel, and all the notifications are successfully caught.
However...
When I press the BACK button on the device and re-navigate to the same ViewModel,
The constructor is being called again, and so the message subscription re-occur.
As a result, when a message arrives the onShowAdsNavigation() handler is being fired twice!
I found this similar post, discussing the question of how to properly dispose a ViewModel,
but it does not contain a direct solution to my problem.
What I need is a solution. It can be either one of the following:
Idea how Not to subscribe to messages on the ViewModel's ctor.
Guidance about how and when to correctly dispose the ViewModel.
Explanation on why the constructor is being called again, and how to avoid that.
A complete different approach to ViewModel information messaging.
Thanks in advance for you help!
Edit:
I found this SO Answer, which basically answers item number 3 in the list above.
Still, I am wondering what approach should I take regarding the messenger issue.
Another Edit:
I verified that the same behavior exists with MvvmCross tutorial N-05-MultiPage. I simply added a ctor to SecondViewModel, and I hit a breakpoint inside it after each BACK+Renavigate.
Explanation on why the constructor is being called again, and how to avoid that.
The ctor is not called twice on the same object - instead what might happen is that a new View and a new ViewModel are created each time.
By default I would expect a new ViewModel to be created on every forwards navigation on every platform.
By default I would **not expect this to happen during a back button on WindowsPhone - it doesn't happen here for my test cases - but it could happen if:
WindowsPhone removes your first Page (and it's ViewModel) from memory - I guess this might happen if your app is tombstoned or if you are using a custom RootFrame - but I don't expect this to happen by default.
you somehow null the ViewModel (DataContext) in your first Page
Without seeing more of your code I can't guess any more about why this might happen.
I'd personally recommend you look deeper at why you are seeing new ViewModels created during Back, but if you just want a quick fix, then you could look at overriding the ViewModelLocator within MvvmCross - see MvvmCross: Does ShowViewModel always construct new instances?
Note that on WindowsStore, I would expect this to happen - WindowsStore doesn't hold Pages from the backstack in memory by default - but you can overriding this by setting NavigationCacheMode = NavigationCacheMode.Enabled; if you need to.

Hidden calling of a function

Let's say I have two objects, Master and Slave.
Slave has a method named Init();. The thing about Init() is, that I need it to be virtual, because it contains user's initialization code, but I also need it to get called automatically when the Slave is added to Master's List. But the method must not be callable by the user, it has to be automatic.
The first thing that I tried is an event - create an event SlaveInitialized that a Slave object could handle in its OnSlaveInitialized handler. This wouldn't work though, because there's a lot of Slave objects and I have no control over the order in which they get created and need to be initialized.
The second thing that I tried is internal method - internal Init() would be called when the object is added to Master's list and all seems okay, until I realized that by doing so I cannot inherit the method in a public class.
So the third thing I did and that worked is this - I created an internal method called _Init() that simply calls a protected virtual Init(), which solved my problem.
Now I want to ask - do I just have a major strike of being stupid, because I am missing the painfully obvious solution here, or is this the way it's normally done? What is the proper way? I hope I got the point of what I'm asking across, I tried my best to explain the problem.
Thanks for any help
This is the gist of the code I now have. Its point is to have Init() invisibly and automatically called when you add any Slave object to the Master's list via Master.AddSlave();
public class Master
{
private List<Slave> _slaves;
public void AddSlave(Slave slave)
{
// Call the "hidden" init
slave._Init();
_slaves.Add(slave);
}
}
public class Slave
{
internal void _Init()
{
// Call the topmost overloaded method.
Init();
}
protected virtual void Init()
{
}
}
public class SuperSlave : Slave
{
protected override void Init()
{
// Now this method gets called automatically
// when Master.AddSlave adds this object.
}
}
As far as I can tell, there are two basic ways to do this.
Like you already tried, an internal InitInternal() method that calls a protected virtual Init()
A protected internal virtual Init(), which outside of your assembly automatically becomes a protected method.
While this doesn't answer the question the way you might want it, however I feel this could be an approperiate solution.
I would recommend throwing an InvalidOperationException if the Init method gets called multiple times.
Description: The exception that is thrown when a method call is invalid for the object's current state.
In my opinion it should not be your responsibility to police your objects from being abused, so long as documentation is written properly, your objects properties and method names describe what they do, it should be enough for most users (developers) consuming your code to understand how it works.
For those who decide to call Init themselves and then add to the Master object, you can throw that exception so they will know that their method for using the class is incorrect.
EDIT:
Naming the method OnInit might be a good idea, that way the user of the class has an indication that it should not be directly called by themselves.

C# Trigger a method after another method (defined in a third party dll) is completed

This may fall into the category of method extending, overriding or polymorphism (I'm new to C# and OO so forgive me, I'm still learning :-) ).
I have an app that is utilizing a 3rd party API. Within the API there is a method for right click+select action on a custom control (the control is an image viewer where a user can right click and cycle to another image that exists within the parent group).
In my limited understanding I would think one of these 2 things would need to happen (whether either one can be done or whether either one is a good solution is up in the air!)
I don't want to override the existing method, I just want to append to it somehow.
If there was a way I could detect when the specific event was triggered and completed, then call my method. Set up some kind of listener if thats available.
Thanks!!
As you didn't post any reference, I'll try to outline some ways.
if there is an event
CustomControl cc = yourCustomControl;
cc.SelectionCompleted += (sender, args) => { YourMethod() };
This is using an anomynous event handler using a lambda.
Another way would be:
public class Form1 : Form
{
public Form1()
{
this.cc.SelectionCompleted += HandlerSelectionCompleted;
}
public void HandlerSelectionCompleted(object sender, EventArgs e)
{
YourCustomMethod();
}
}
there is a method to override
public class YourCustomControl : CustomControl
{
public override void Selection()
{
base.Selection(); // first call the original method
// now do some custom stuff
}
}
You can not override that method: that's right, if it's not protected/virtual/abstract whatever, or if you can not derive from that component's class.
You can search the component for the events and guess (if there is no any documentation) which event is fired after your desired action. And actually execute the code in that event handle.
There could be other "hacking" tricks, but I personally would avoid to do something like that, if not for personal passion, but focus on reachitecturing my program, in order to fit the requirements and support that component, as much as I can.
Regards.
What you are describing is a tenant of Aspect Oriented Programming AOP. If you want to instrument a 3rd party .NET dll, I would recommend PostSharp http://www.sharpcrafters.com/solutions/logging

How do I design View logic into c# projects

I have three c# projects in my solution. One is a console app that simply calls into a class library project. The class library project does all the processing for the application. Then there is a WinForm project that displays a form and then when a button is pressed, calls the same logic in the class library project. As a result, there are two ways to run the logic, via the Console or via a Windows UI (WinForm).
My problem is that part way through the class library logic, if the UI app is being used, I want a custom WinForm form to appear to ask the user a question.
In the Console app, I want the same place in the logic to simply write out to the Console. In my understanding of architecture, you don't want the class library project to contain WinForm logic and require it to have references to all the WinForm references. But how do I make a call to the WinForms project (or something else) to display the custom WinForm form? There would be a circular reference where the class library would reference the main WinForm app and the WinForm app would reference the class library project.
What is the standard way of doing this?
You could create an interface that your library defines to communicate back to the caller, then have both your calling apps define their own implementaions of this interface, the library calls the methods on this interface and knows nothing of the implmentation.
The caller processes the methods accordingly...
public interface IProgressReporter
{
void ReportMessage(string message);
}
public class WinFormsProgressReporter : IProgressReporter
{
public void ReportMessage(string message)
{
MessageBox.SHow(message);
}
}
public class ConsoleAppProgressReporter : IProgressReporter
{
public void ReportMessage(string message)
{
Console.WriteLine(message);
}
}
public class LibraryClass
{
public static void SomeMethod(IProgressReporter rep)
{
rep.ReportMessage("Wooooohooooo!");
}
}
Why not define an inteface, IOutputHandler, that has a method called DisplayOutput. You would have 2 implementations of it, one for your winforms app and one for the console. You'd call the correct version of it at runtime. You could modify your class library to have a private field instance for the IOutputHandler, and then plug in the proper one at runtime.
You could raise an event in the class library that is listened to/registered from whatever your UI/Console layer is. That way it can decide to act on the event if it is deemed necessary in as many places as you desire. It really depends on how your architecture is setup.
This is where you need clear definition between your logic layer and your UI layer. It's perfectly acceptable and normal to put this sort of logic in your UI, since that bit can't reasonably live within the logic layer, as it is UI dependent.
Your logic should never, ever refer to any kind of UI component. If it does, it's wrong, and you need to redesign it to remove UI dependencies completely.
While the interface answers are probably better solutions, if it's just one method you could just use a delegate to pass the Console or WinForm method to the class library.
Another solution
In your class lib..
public void MyLibMethod(Action<string> callBack)
{
callBack("Yeh baby...");
}
Then call
Class.MyLibMethod(s=> Console.WriteLine(s));

Categories

Resources