Adding a page before the hub page in a Windows Phone 8.1 store app - xaml

I'm building a Windows Universal Store App, concentrating first on the Windows Phone 8.1 app. I'm basing my app on a hub app. I want to add a splash page to the app startup to replace the static splash screen with a XAML based animation. I am confused by navigation since it all seems to be set up and owned by the hub page.
I have looked at
Mike Taulty's blog post about Windows/Phone 8.1–Frame, Page, NavigationHelper, SuspensionManager;
Quickstart: Navigating between pages;
How to extend the splash screen; and
Navigation Patterns
In fact the last of those explicitly states that "hub pages are the user's entry point to the app".
How do I add pages to my hub app that the user will encounter before the hub, like a splash page or a logon screen?

Typically no sooner had I posted the question than I saw where to find the answer. When NavigationHelper is added to the project along with the hub page it includes comments explaining how to use it:
To make use of NavigationHelper, follow these two steps or
start with a BasicPage or any other Page item template other than BlankPage.
1) Create an instance of the NavigationHelper somewhere such as in the
constructor for the page and register a callback for the LoadState and
SaveState events.
public MyPage()
{
this.InitializeComponent();
var navigationHelper = new NavigationHelper(this);
this.navigationHelper.LoadState += navigationHelper_LoadState;
this.navigationHelper.SaveState += navigationHelper_SaveState;
}
private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
}
private async void navigationHelper_SaveState(object sender, LoadStateEventArgs e)
{
}
2) Register the page to call into the NavigationHelper whenever the page participates
in navigation by overriding the Windows.UI.Xaml.Controls.Page.OnNavigatedTo
and Windows.UI.Xaml.Controls.Page.OnNavigatedFrom events.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
navigationHelper.OnNavigatedTo(e);
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
navigationHelper.OnNavigatedFrom(e);
}
That boiler-plate code only required slight changes in my page: take out the async, and make navigationHelper an instance variable.
Then to start in another page follow the instructions in Set start page in Windows Phone 8.1 universal app to edit App.xaml.cs thus:
#if WINDOWS_PHONE_APP
if (!rootFrame.Navigate(typeof(SplashPage), e.Arguments))
{
throw new Exception("Failed to create initial page");
}
#endif
#if WINDOWS_APP
if (!rootFrame.Navigate(typeof(HubPage), e.Arguments))
{
throw new Exception("Failed to create initial page");
}
#endif
Then in SplashPage.xaml.cs when I need to navigate to the hub page I add
if (rootFrame.Navigate(typeof(HubPage)))
{
Window.Current.Content = rootFrame;
}
else
{
throw new Exception("Failed to create hub page");
}

Related

Xamarin.Forms Communication Between Two Pages Within Same App on Different Devices

Technologies, frameworks and devices I'm using:
Framework: Xamarin.Forms
IDE: Visual Studio 2022
Physical Device (smartphone): Zebra TC26 (Android 10)
Physical Device (smartwatch): Samsung Galaxy Watch4 (Android 11)
Problem definition
Currently I have a test Xamarin.Forms project that consists of two different UIs (XAML files):
User Interface 1: HomePage.XAML - This screen should be displayed on the smartphone
User Interface 2: WatchScreen.XAML - This screen should be displayed on the smartwatch
With code below I make sure HomePage.XAML is deployed to a smartphone and watchscreen is deployed to a smartwatch:
Page homePage = new NavigationPage(new HomePage());
// BuildVersionCodes.R is a reference to Android version 11 (mostly now used by Wear OS 3.x)
if (Build.VERSION.SdkInt == BuildVersionCodes.R)
{
// SM-R870 is a reference to the Samsung Galaxy Watch4
// Note: This is needed to ensure the UI is specific to the UI of a smartwatch
if (Build.Model == "SM-R870")
{
Page watchScreen = new NavigationPage(new WatchScreen());
MainPage = watchScreen;
}
}
else
{
MainPage = homePage;
}
Now I want to make these pages on different devices communicate with each other. HomePage.xaml exists within the main Xamarin.Forms project as well as WatchScreen.xaml.
The way I want them to communicate with each other is by sending a message or something. A Xamarin.Forms project also comes with a native project. In this native Xamarin.Android project I try to retrieve inside the MainActivity.cs the button that exists within the main project by using (in WatchScreen.xaml this button exists and in WatchScreen.xaml.cs I have a method that gives this button back).
Method in WatchScreen.xaml.cs that gives button back:
public Button GetSendButtonFromWearableUI() => btnSendMessage;
In MainActivity.cs I get this method by using:
Button button = (App.Current.MainPage.Navigation.NavigationStack.LastOrDefault() as WatchScreen)
.GetSendButtonFromWearableUI();
Whenever I click on the button by doing this:
button.Clicked += delegate
{
SendData();
};
Some data should be sent from MainActivity.cs and catched by HomePage.xaml and displayed on it. I tried several approaches but I didn't succeed in achieving what needs to happen.. Therefore, I'm wondering if you guys could help me out with this and would be much appreciated.
In the meantime I've been investigating this issue and came up with a solution. Follow steps below to get the same result. To make this solution work I've combined the Wearable Data Layer API from Google and MessagingCenter from Microsoft.
Also the example below shows only the communication from the smartwatch to the smartphone. In order to reverse processes you can put the send button on the HomePage instead of the smartwatch screen and make sure to subscribe to the correct messages.
One last note: keep in mind that code used below from Google is deprecated but it still works...
References used to make this work:
Syncing Data Between Wearable and Handheld Devices Using Xamarin in Android
Installed dependencies on the Xamarin.Android project within Xamarin.Forms project:
Xamarin.Android.Support.v4
Xamarin.GooglePlayServices.Base
Xamarin.GooglePlayServices.Wearable
MessageKeys.cs
This class is used to declare message keys that are being used to send and receive messages between devices.
public class MessageKeys
{
public const string Smartwatch = "Smartwatch";
public const string Smartphone = "Smartphone";
}
Xamarin.Forms (Base project) - App.xaml.cs
In the App.xaml.cs, as pointed out earlier, I'm making sure the wearable UI displays WatchScreen.xaml and any other devices display regular Android UI -> HomePage.xaml.
Xamarin.Forms (Base project) - WatchScreen.xaml.cs
Send message from Wearable device to Android smartphone.
private void btnSendMessage_Clicked(object sender, EventArgs e)
{
MessagingCenter.Send(Xamarin.Forms.Application.Current, MessageKeys.Smartwatch);
}
Xamarin.Forms (Base project) - HomePage.xaml.cs
public HomePage()
{
InitializeComponent();
MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current, MessageKeys.Smartphone, (sender) =>
{
DisplayAlert("Message", "Wearable message received!", "OK");
});
}
Xamarin.Forms (Native Android Project) - MainActivity.cs
Within MainActivity.cs I implement the following interfaces:
public class MainActivity : WearableActivity, DataClient.IOnDataChangedListener,
GoogleApiClient.IConnectionCallbacks, GoogleApiClient.IOnConnectionFailedListener
Variables:
private GoogleApiClient client;
const string syncPath = "/[project name]/[subdirectory for watch]";
Internal class 'MessageReceiver' for receiving broadcast messages:
[BroadcastReceiver]
public class MessageReciever : BroadcastReceiver
{
MainActivity main;
public MessageReciever() { }
public MessageReciever(MainActivity owner) { this.main = owner; }
public override void OnReceive(Context context, Intent intent)
{
main.ProcessMessage(intent);
}
}
Registering receiver (to receive through Wearable Data Layer API), creating Google Client and Subscribing to smartwatch message (to retrieve message through MessagingCenter)
protected override void OnCreate(Bundle bundle)
{
IntentFilter filter = new IntentFilter(Intent.ActionSend);
MessageReciever receiver = new MessageReciever(this);
LocalBroadcastManager.GetInstance(this).RegisterReceiver(receiver, filter);
client = new GoogleApiClient.Builder(this, this, this)
.AddApi(WearableClass.Api)
.Build();
MessagingCenter.Subscribe<Xamarin.Forms.Application>(Xamarin.Forms.Application.Current, MessageKeys.Smartwatch, (sender) =>
{
SendData();
});
}
ProcessMessage method: sends received message from wearable to smartphone
public void ProcessMessage(Intent intent)
{
// For now I'm not sending the payload...
string message = intent.GetStringExtra("WearMessage");
MessagingCenter.Send(Xamarin.Forms.Application.Current, MessageKeys.Smartphone);
}
SendData(), OnStart(), OnStop(), OnDataChanged (didn't do anything with this part, because this is to receive messages outside the project and I don't need it for now), OnConnected(), OnConnectionSuspended(), OnConnectionFailed():
See the reference to see what code has been used, since code is exactly the same... P.S.: one thing for SendData has been changed. If you want to keep sending data, remove 'client.Disconenct()' from finally after the try and catch block.
Xamarin.Forms (Native Android Project) - WearableService inherits from WearableListenerService:
WearableService is a new class and created within the native project. Also for this part see the reference, because it's the exact same code being used within my project.
To get an overall overview of what's happening, I've visualized this in the diagram below: (example shows how communication works from smartwatch to smartphone)
If you want to communicate from smartphone to smartwatch, you could do something like this:
That's it guys. Now you will receive messages within the same application using the Wearable Data Layer API and MessagingCenter. Instead of having separate projects, we just use separate UIs to make this happen...

MvvmCross and back button in Windows Phone app

I'm building a Windows Phone app (8.1 using WinRT) using MvvmCross. To navigate to a new view I using ShowViewModel(). But when I hit the back button on the phone the app is closing instead of navigating back to the first view. How can I do it I want to return to the first view when I hitting the back button?
I solved it to use a interface in my viewmodel with a backbutton event, then I wrote a client speific implementation of it. In the viewmodel I handle the event and called the close method in the my base class MvxViewModel. Read more about my solution on my blog, http://danielhindrikes.se/windows-phone/handle-windows-phone-back-button-pressed-when-using-mvvm/
Here's a simpler solution. Create a base type for all your WP pages that derives from MvxWindowsPage. Then, handle the back key there and route the proper information to your VM:
public abstract class MyBaseView : MvxWindowsPage {
public MyBaseView() {
this.InitializeComponent();
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
}
void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e) {
if (Frame.CanGoBack) {
var vm = ViewModel as MyBaseViewModel;
if (vm != null) {
e.Handled = true;
vm.GoBackCommand.Execute(null);
}
}
}
}
Now, you also have to make sure that you have a base viewmodel which derives from MvxViewModel and from which you derive all your VMs. That base VM should have a GoBackCommand observable property, and executing that command should do a simple Close(this).
To see what's going on under the hood, see this related question: Windows Phone 8.1 Universal App terminates on navigating back from second page?
EDIT
Fixed declaration.

Titanium module - life cycle events not called

I am building a Titanium module for the Android platform and I want to use the life cycle events of the module (i.e. onDestroy, onPause, etc). I tried to use them by overriding these life cycle events in the module class like this:
#Kroll.module(name="custom", id="vub.ac.be.custom")
public class CustomModule extends KrollModule {
private static final String TAG = "customModule";
#Kroll.onAppCreate
public static void onAppCreate(TiApplication app) {
}
private void destroyServices(){
//...
}
#Override
public void onStop(Activity activity) {
Log.d(TAG, "STOPPING");
destroyServices();
super.onStop(activity);
}
#Override
public void onPause(Activity activity) {
Log.d(TAG, "[MODULE LIFECYCLE EVENT] pause");
super.onPause(activity);
}
#Override
public void onResume(Activity activity) {
Log.d(TAG, "[MODULE LIFECYCLE EVENT] resume");
super.onResume(activity);
}
#Override
public void onDestroy(Activity activity) {
Log.d(TAG, "[MODULE LIFECYCLE EVENT] destroy");
destroyService();
super.onDestroy(activity);
}
}
but when I opening and closing the application, these life cycle events are never called. Does anybody know how to use them, because only if I can use them I will be able to build the module I want. Thanks
Could this be the origin of my problems: inline link moddevguide
https://github.com/appcelerator/titanium_modules/blob/master/moddevguide/mobile/android/src/ti/moddevguide/ModdevguideModule.java
on line 72 they describe the following:
// Lifecycle
// NOTES:
//
// 1. Modules are created in the root context
// 2. Using navBarHidden (or fullscreen or modal) causes the window, when opened, to run in a new Android Activity.
// 3. The root context/activity will be stopped when a new activity is launched
// 4. Lifecycle notifications will NOT be received while the root activity is stopped.
I run the module in an application that uses navBarHidden, so as described a new android activity wil be started and the root activity is stopped. Whenever the root activity is stopped, the lifecycle notifications are received. Can anyone confirm this and does anybody know how to solve this? thanks
What version of SDK are you using? On 3.3.0 lifecycle callbacks is calling regardless to navigation bar hidden.

Invoking Message dialog from Settings Flyout causes Message Dialog to flicker

I'm trying to invoke messagedialog from setting flyout for my Windows 8 Metro app but it's causing the message dialog to flicker. Below is the code.
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
SettingsPane.GetForCurrentView().CommandsRequested+=settings_CommandsRequested;
}
private void Settings_CommandsRequested(SettingsPane sender, SetttingsPaneCommandsRequestedEventArgs args)
{
SettingsCommand cmd = new SettingsCommand("test","test1232",new UICommandInvokedHandler(CreateDialog));
args.Request.ApplicationCommands.Add(cmd);
}
private void CreateDialog(IUICommand command)
{
if (ReferenceEquals(command.Id, "cmd"))
{
MessageDialog md = new MessageDialog("Hi");
md.ShowAsync();
}
}
I've contacted official microsoft dev-support and their response was:
"MessageDialog is not recommended within the SettingsFlyout".
So in case you want to implement something simillar to support user's decision from the SettingsPane, you should either:
1) Enable toggling feature in the Flyout.
2) Desiging the SettingsFlyout so it lets the user make decision (for example in Logout cases, add Yes/no buttons inside the settingsFlyout) - Thats the option I chose.

automatic redirection after specific time in WP8

I'm developing windows phone 8 application.
I have two pages ; one of the page is the start up one ; Once the user open the application this page will appear and automatically after a specific time ; it will redirect the user to the main menu of the application .
How I can make an automatic redirection after specific time in WP8 ?
May be these lines of code helps you:
public partial class MainPage : PhoneApplicationPage
{
private DispatcherTimer timer;
// Constructor
public MainPage()
{
InitializeComponent();
timer = new DispatcherTimer();
//Set your specific time here using TimeSpan instance
timer.Interval = TimeSpan.FromSeconds(2);
timer.Tick += (s, e) => {
var frame = App.Current.RootVisual as PhoneApplicationFrame;
frame.Navigate(new Uri("/Page1.xaml", UriKind.Relative));
};
timer.Start();
}
}
Hope it helps.
Look at this answer for the a timer implementation. Only thing left to do is, when the timer finished, call a navigation method to navigate to the main menu.