How to intercept Navigation Bar Back Button Clicked Prism Xamarin Forms? - c#

Basically Its the same question from Here but the solution dosen't work for Prism MVVM because the OnOptionsItemSelected(IMenuItem item) in the MainActivity never get raised.
For the hardware button I'm using on that page:
protected override bool OnBackButtonPressed()
{
return !PageUtilities.CanNavigate(this, null);
}

Found the answer here https://xamarindevelopervietnam.wordpress.com/2016/11/19/how-to-handle-hardwaresoftware-back-button-from-viewmodel-in-xamarin-forms/
works well

you can do like this
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{
e.Cancel = !PageUtilities.CanNavigate(this, null);
base.OnBackKeyPress(e);
}
Cancel the event when navigation neeeds to be stopped

I guess there is no straight or single answer to this.
Based on your requirement you must need to provide MVVM implementation.
Latest Prism Library update has the OnNavigatedFrom method (you'll require to override by implementing INavigationAware interface)
public virtual void OnNavigatedFrom(NavigationParameters parameters)
{
// here is the place you would require to handle the Back button event,
// this is fired every-time, user tries to leave the view.
}
public virtual void OnNavigatedTo(NavigationParameters parameters)
{
// fired upon view load
}
You may require a static (boolean or something) variable to keep a check if the user is leaving the view and optionally display a message or invalidate the action.
Hope this helps.
Regards,
N Baua

Related

OnBackPressed Xamarin PCL where do i override the method

I am trying to override the OnBackPressed() method in a pcl project. I do not know where i must ovveride it. In the portable project or in the android? I want to navigate to another page when i click the back button. Any suggestions?
You need to override OnBackButtonPressed in your PCL project on your custom Page classes.
protected override bool OnBackButtonPressed()
{
// call your custom navigation code here
// returning true because you are handling the navigation
return true;
}
Note that this event is raised when the hardware back button is pressed but will not be raised on iOS.

Custom handling of OnBackPressed MvvmCross - don't close app

I'd like to add custom handling of method OnBackPressed in Xamarin Android with MvvmCross framework. I've tried something like this:
[Activity(Label = "Table", NoHistory = true)]
public class Table: MvxActivity
{
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
SetContentView(Resource.Layout.View_Table);
}
public override void OnBackPressed()
{
//base.OnBackPressed();
SetContentView(Resource.Layout.View_MainMenu);
}
}
and the app went to MainMenuView but one single button (which directs to TableView) inside this view was disabled.
I've been trying with something like:
protected override void OnResume()
{
SetContentView(Resource.Layout.View_MainMenu);
}
in MainMenuView but it doesn't work. Should I add some piece of code into ViewModels instead of in Views? Or bind somehow events in a layout? If it is possible to handle that kind of behavior, how to achieve that?
You are not using MvvmCross features if you write your code like this.
Just remove "History = true", remove your OnBackPressed() and OnResume() handlers and MvvmCross will handle your back to MainMenu as expected.
When using MvvmCross, you need to use your ViewModels and not your Views to navigate (at least, in a classic scenario).

Using data from a ViewModel in a ModalDialog even though it lives in the Shell

I'm writing a UWP and using Template 10.
I've created a ModalDialog that should show the user some data that was just calculated in a ViewModel.
Here's where I'm lost:
#1, the ModalDialog needs data from my ViewModel. #2, the ModalDialog needs to call 1+ method(s) on the ViewModel depending on which button the user clicks.
My Shell.xaml.cs:
public sealed partial class Shell : Page
{
public static Shell Instance { get; set; }
public static HamburgerMenu HamburgerMenu => Instance.MyHamburgerMenu;
public Shell()
{
Instance = this;
InitializeComponent();
if (App.MobileService.CurrentUser == null)
LoginModal.IsModal = true;
}
public Shell(INavigationService navigationService) : this()
{
SetNavigationService(navigationService);
}
public void SetNavigationService(INavigationService navigationService)
{
MyHamburgerMenu.NavigationService = navigationService;
}
#region Login
private void LoginLoggedIn(object sender, EventArgs e)
{
MyHamburgerMenu.NavigationService.Navigate(typeof(Views.MainPage));
LoginModal.IsModal = false;
}
#endregion
}
}
Shell.xaml
<Controls:ModalDialog x:Name="ScoreModal" Grid.RowSpan="3"
CanBackButtonDismiss="False"
DisableBackButtonWhenModal="True">
<Controls:ModalDialog.ModalContent>
<myControls:QuizScorePart
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</Controls:ModalDialog.ModalContent>
</Controls:ModalDialog>
What I've tried:
I tried just putting the control for the ModalDialog in the View that uses the ViewwModel I wish to speak to, but that doesn't work; that view lives inside the shell, meaning everything underneath the ModalDialog is NOT disabled. It HAS to be in the Shell, from what I know.
I tried setting up a method in the Shell.xaml.cs file that sets IsModal for my dialog to true/false; this works, but it doesn't solve my problem of interacting with my ViewModel.
I'm lost. Thanks anyone for the help.
reference the Search example, there are delegates to handle in the actual part, look at the codebehind for the LoginPart....
What I specified below in comments is how I do it with a LoginPage, not a usercontrol. Which has a LoginPageViewModel which in-turn has a the SettingsService instance referenced.
Edit
Think of it this way...ScoreModal isn't anything other than another view control. QuizScorePart is your View, I assume that QuizScorePartViewModal exists. From there it becomes a message passing exercise between viewmodels. At least that is what I see after the last comment. You need to know what the user clicked on button wise. Assuming that the above is true, then QuizViewModel would react to that message it was listening for. Shell is merely a holding location for a complete screen cover for the it only reacts to IsModal. If that is an issue consider a Service to hold the button selection, similar to how SettingsService works. Nothing says that QuizScorePart couldn't have its datacontext set to that of the QuizViewModel, but it might be a testing issue at that point.

Caliburn.Micro and app resume

In my Windows Phone Silverlight 8.1 app I enabled the app resume (ActivationPolicy="Resume")
When the user re-open the app (using the main tile), the OnDeactivate method of the ViewModel is called after the OnActivation method.
Is it correct?
How can I handle the app resume with Caliburn.Micro?
Here is the code of the ViewModel:
public class Page2ViewModel: Screen
{
protected override void OnActivate()
{
base.OnActivate();
}
protected override void OnDeactivate(bool close)
{
base.OnDeactivate(close);
}
}
This does not seem right. OnDeactivate is called when navigating away from a View. In your ViewModel there is no way to directly differentiate if it was activated from normal navigation or because of resuming.
Take a look at your Bootstrapper, you can override OnActivate and OnLaunch methods there that correspond to Application_Launching and Application_Activated. OnActivate is the one you want.
The problem was in the AppBootstrapper.
To handle the app fast resume, the Navigated and Navigating events must be subscribed in the CreatePhoneApplicationFrame frame.
In this repository the fix (see commit list for the history).

Access method of an usercontrol from another usercontrol

On my form I have 2 UserControls (ButtonDiscount, ButtonAdvertisment) that inherit FadeControl. FadeControl inherits UserControl class and is used to do custom stuff on the controls like fade out.
My 2 UserControls each have just one button hence those two unimaginative names.
On click of that button on one of usercontrols, I need to access the method in FadeControl from the other UserControl. The other button does the opposite.
I've done the following, on the event click in the UserControl ButtonDiscount:
private void button1_Click(object sender, EventArgs e)
{
ButtonAdvertisment ba = (ButtonAdvertisment)this.Parent.Controls.Find("buttonAdvertisment1", true)[0];
ba.FadeOut(true);
}
It works like a charm, but I don't think this is the right way, is there another way to access the method from the parent class of the other UserControl?
I can't pass it thru a UserControl constructor, the designer breaks down every time.
You have 2 separate user controls which are not aware of each other, which is good and keeps your code loosely-coupled. Now what you are trying to do is to make them somehow know about each other and communicate. Making them aware of each other breaks the loose-coupling and is a bad design.
What I would suggest is creating a 3rd control which will hold the two together and will handle all the communication between them. Each of your original controls will have public events, to which the parent control can subscribe and handle appropriately.
Check mediator pattern for more info: http://en.wikipedia.org/wiki/Mediator_pattern
What you've done is fine - you could do it by exposing events that fired when you click the button in those controls, and then passing references to each other (subscribing to those, writing the code to fade 'this' control).
That might be a bit too much work for a simple solution, however.
What I would say about your solution is that if you were to change the name of control(s) then it stops working. You could instead do:
var ba = this.Parent.Controls.OfType<ButtonAdvertisement>().FirstOrDefault();
That way you're no longer tied to the control name - but the type of the control. You'll need a using System.Linq; in your code file for this to work. Of course, this relies on the fact that there is only ever one other instance of that control type in the parent.
If you're interested in the first solution I mentioned - then this code snippet should help demonstrate:
public class FadeControl {
public event EventHandler Clicked;
public void FadeOut(bool b){
}
public void AttachTo(FadeControl other){
//splitting this operation to a public and private allows us to
//initiate an attach publicly, but then recurse privately without
//causing a stack overflow
AttachToInternal(other);
other.AttachToInternal(this);
}
private void AttachToInternal(FadeControl other){
other.Clicked += Attached_Clicked;
}
protected virtual void Attached_Clicked(object sender, EventArgs e)
{
//fade me out
FadeOut(true);
}
// provides a way for the deriving class to raise the Clicked event
protected void OnButtonClicked(){
if(Clicked != null) Clicked(this, null);
}
}
public class ButtonDiscount : FadeControl {
Button _button;
//omitted: designer code
//this is your handler for the _button clicked event
private void _button_Clicked(object sender, EventArgs e){
//call the base class' OnButtonClicked method - to raise the event
OnButtonClicked();
//TODO: do work.
}
}
//omitted - code for the ButtonAdvertisement class
Once you have that done - in your form, assuming you have _buttonAdvertisement and _buttonDiscount members in your form and after they're initialised - you simply do:
_buttonAdvertisement.AttachTo(_buttonDiscount);
And that will immediately bind both controls to each other.
Note - in response to a comment below - I've made the event handler in FadeControl for another FadeControl's Clicked event protected and virtual - so you can override it.

Categories