How can I debug a Xamarin watchOS Linker Error - c#

I am developing an iOS App using Xamarin Forms for which I have created a core model around which all app functionality works.
I would like to include a simple watchOS app which allows the user to operate on a single instance of this model at any one time. I have implemented some code to update the model in the watchOS App using WCSession (via this WCSessionManager Class). I have also reused some code for implementing a timer from my Xamarin Forms project.
However I am encountering a Linker error when building my solution. I think it may be because I have referenced my Xamarin Forms project from my watchOS project, which may not be allowed. Here is the error:
/Users/luketimothy/Projects/TodoQ/TodoQ.Watch/TodoQ.Watch.WatchOSExtension/MTOUCH: Error MT2001: Could not link assemblies. Reason: Error while processing references of 'TodoQWatchWatchOSExtension, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' (MT2001) (TodoQ.Watch.WatchOSExtension)
The file the error references is MTOUCH. I am not sure what this is exactly, but the only place in my watchOS app I have referenced my Xamarin Forms code is this object:
using System;
using System.Collections.Generic;
using TodoQ.Models;
using TodoQ.Utilities;
using WatchConnectivity;
using WatchKit;
namespace TodoQ.Watch.WatchOSExtension
{
internal class TodoState
{
private TodoItem current;
private ISessionTimer timer;
public TodoItem Current { get => current; set { current = value; TaskUpdated(this, value); } }
public event TaskUpdatedEventHandler TaskUpdated;
public delegate void TaskUpdatedEventHandler(object sender, TodoItem current);
public event TimerElapsedEventHandler TimerElapsed;
public delegate void TimerElapsedEventHandler(object sender, TimerElapsedEventArgs current);
public TodoState()
{
WCSessionManager.SharedManager.ApplicationContextUpdated += DidReceiveApplicationContext;
timer = new PomodoroTimer();
timer.ProgressUpdate += (object sender, ProgressUpdateEventArgs e) =>
{
TimerElapsed(this, new TimerElapsedEventArgs() { Elapsed = e.Elapsed, EndTime = e.EndTime });
};
timer.MilestoneUpdate += (object sender, PomodoroStateID e) =>
{
var audio_file = WKAudioFilePlayerItem.Create(WKAudioFileAsset.Create(new Foundation.NSUrl("ShortBreak.wav")));
var audio_player = WKAudioFilePlayer.Create(audio_file);
audio_player.Play();
WKInterfaceDevice.CurrentDevice.PlayHaptic(WKHapticType.Notification);
};
}
public void DidReceiveApplicationContext(WCSession session, Dictionary<string, object> applicationContext)
{
var message = (TodoItem)applicationContext["FocusedItem"];
if (message != null)
{
Console.WriteLine($"Application context update received : {message.Heading}");
Current = message;
}
}
public void StartTimer()
{
timer.StartSession();
}
}
public class TimerElapsedEventArgs
{
public TimeSpan Elapsed;
public TimeSpan EndTime;
}
}
So, my question is. If this ought to be allowed, and the error is something else, could I get some help tracking down what this MTOUCH is and why it's throwing this error? If it is not allowed, what is the recommended solution for sharing this kind of code between my Phone App and my Watch App? Could I put it in a PCL? Should I copy the code between projects?

You should not reference your WatchOS project to the Forms project. It should be added in iOS project directly.
And if you want to define some common code for reuse. You could create a shared library:
Add some public classes there:
namespace UtiLibrary
{
public static class UtiClass
{
public static List<Model> datas { get => new List<Model> { new Model { Name = "name" } }; }
}
public class Model
{
public string Name { set; get; }
}
}
Then you could utilize it on each platform which has referenced this library.

Related

Wix - Managed Bootstrapper Application upgrade problem

I decided to ask my question here because I can't find answer in any other place.
My task is to prepare upgrade process with managed bootstrapper UI, but it doesn't want to work properly.
I prepared all of needed MSI packages. I mean I added Product Id="*", I added MajorUpgrade tag and configured it and I change versions between bundle(for test purposes), I also added a few properties which should helps me with distinguish is it an Install, Uninstall or Upgrade process.
And my problem starts here, because when I was using default burn UI it worked properly I mean during installation property _INSTALL was set to 1, during upgrade (installing version 2 of bundle) property _UPGRADE was set to 2 and the same with uninstallation, but now when I added Custom UI to that, UPGRADE property isn't set at all. Instead of that during trial of UPGRADE first starts Installation process and it goes to some point and then new window with Uninstallation appears.
My question is can I somehow make my Custom UI to behave like a Default UI for burn?
Thank you everyone for comments. Inspired by How to perform Wix Upgrade with custom bootstrapper I understood I didn't handle situation when installer is run quietly.
So I prepared another class for SilentUninstall and did it in my Bootstrapper class:
public class BootstrapperApp : BootstrapperApplication
{
public static Dispatcher Dispatcher { get; set; }
protected override void Run()
{
Dispatcher = Dispatcher.CurrentDispatcher;
var model = new BootstrapperApplicationModel(this);
var command = model.BootstrapperApplication.Command;
if (command.Action == LaunchAction.Uninstall && (command.Display == Display.None || command.Display == Display.Embedded))
{
model.LogMessage("Starting silent uninstaller.");
var viewModel = new SilentUninstallViewModel(model, Engine);
Engine.Detect();
}
else
{
model.LogMessage("Starting installer.");
var viewModel = new InstallViewModel(model);
var view = new InstallView(viewModel);
view.Closed += (sender, e) => Dispatcher.InvokeShutdown();
model.SetWindowHandle(view);
Engine.Detect();
view.Show();
}
Dispatcher.Run();
Engine.Quit(model.FinalResult);
}}
and my SilentUninstaller class:
public class SilentUninstallViewModel
{
private BootstrapperApplicationModel model;
private Engine engine;
public SilentUninstallViewModel(BootstrapperApplicationModel model, Engine engine)
{
this.model = model;
this.engine = engine;
WireUpEventHandlers();
}
private void WireUpEventHandlers()
{
this.model.BootstrapperApplication.PlanComplete += PlanCompleted;
this.model.BootstrapperApplication.DetectComplete += DetectCompleted;
this.model.BootstrapperApplication.ApplyComplete += ApplyCompleted;
}
private void DetectCompleted(object sender, DetectCompleteEventArgs e)
{
this.model.LogMessage("Detecting has been completed for silent uninstallation.");
this.model.PlanAction(LaunchAction.Uninstall);
}
private void ApplyCompleted(object sender, ApplyCompleteEventArgs e)
{
this.model.LogMessage("Applying has been completed for silent uninstallation.");
this.model.FinalResult = e.Status;
this.engine.Quit(this.model.FinalResult);
}
private void PlanCompleted(object sender, PlanCompleteEventArgs e)
{
this.model.LogMessage("Planning has been started for silent uninstallation.");
model.ApplyAction();
}
}
and it seems to works properly. Even property _UPGRADE is raised in particular MSIs ;)

Windows Workflow: Getting variable from WorkflowApplication

I'm using the Windows Workflow Foundation (WWF). I made an Activity (XAML) with one Sequence in which I defined a variable.
I run the activity in the console application by creating an instance of WorkflowApplication. How can I get the value of a variable in my console application?
I persist an instance of WorkflowApplication in XML and in it I saw my variable and its value. Is there any correct way to get the value of a variable from XML?
So in your last comment you stated you want to get the state in the console application before the workflow is completed. Unfortunately In/Out and Out arguments are only available upon completion of the workflow. But there are ways to communicate with the host process using other constructs than workflow variables and arguments.
One of the ways to do that is to use a custom extension that can be used to interact with the host process. An extensions can be of any type and is available to the workflow and the host process. A complete example:
using System;
using System.Activities;
namespace WorkflowDemo
{
class Program
{
static void Main(string[] args)
{
var app = new WorkflowApplication(new MyCustomActivity());
var myExtension = new MyCommunicationExtension();
myExtension.MyValueChanged += (s, e) => Console.WriteLine(myExtension.MyValue);
app.Extensions.Add(myExtension);
app.Run();
Console.ReadKey();
}
}
public class MyCommunicationExtension
{
public string MyValue { get; private set; }
public event EventHandler<EventArgs> MyValueChanged;
public void OnMyValueChanged(string value)
{
MyValue = value;
MyValueChanged?.Invoke(this, EventArgs.Empty);
}
}
public class MyCustomActivity : CodeActivity
{
protected override void Execute(CodeActivityContext context)
{
var extensionObj = context.GetExtension<MyCommunicationExtension>();
if (extensionObj != null)
{
extensionObj.OnMyValueChanged("Hello World");
}
}
}
}

WMNetMgr.dll Application Error w3wp.exe error using WMPLib C# .NET

I am having an issue with my web application regularly crashing and resetting the application pool in IIS causing big performance issues, as well as wiping any timing threads running in my application.
The site is a .NET 4.5.2 C# MVC5 Site running on a 2012 Windows Server EC2 instance in AWS.
The issue was first noticed when I started see the site struggle to load after so many minutes of run-time. I thought it might be the ApplicationPool recycling and made sure to set IdleTime and Application Preload in IIS properly. The issue still persisted.
Next I went to the Server Manager to check the event logs and found these entries happening about every 15 minutes or so:
Faulting application name: w3wp.exe, version: 8.5.9600.16384, time
stamp: 0x5215df96 Faulting module name: WMNetMgr.dll_unloaded,
version: 12.0.9600.17415, time stamp: 0x545047db Exception code:
0xc0000005 Fault offset: 0x00000000000cf5cf Faulting process id:
0x17d0 Faulting application start time: 0x01d331dc20f096d0 Faulting
application path: c:\windows\system32\inetsrv\w3wp.exe Faulting module
path: WMNetMgr.dll Report Id: 777a35de-9dd1-11e7-81d7-025ff0be916d
Faulting package full name: Faulting package-relative application ID:
and
WIN-7PCRJOFR05F 5011 Warning Microsoft-Windows-WAS System 9/20/2017
7:01:04 AM - A process serving application pool 'SiteName' suffered a
fatal communication error with the Windows Process Activation Service.
The process id was '6096'. The data field contains the error number.
Next I ran a DebugDiag2 Collection and Analysis:
WARNING - DebugDiag was not able to locate debug symbols for
WMNetMgr.dll>, so the information below may be incomplete.
In
w3wp__SiteName__PID__5088__Date__09_20_2017__Time_06_31_02AM__436__Second_Chance_Exception_C0000005.dmp
an access violation exception (0xC0000005) occured on thread 26 when
another Module attempted to call the following unloaded Module:
WMNetMgr.dll>.
Thread 26:
Call Stack Unloaded_WMNetMgr.dll+cf5cf
0x000000de575cf7c0 0x000000dc2ed5ec10
This is the only error reported by this debugger. With no others exceptions in the .NET stack trace on the report. I can't seem to get the debugging symbols for this particular .dll and the messages do not seem to be very helpful.
The application utilizes WMPLib to create a singleton instance at startup of the wmplayer to play sounds on the Windows Server 2012 instance via web requests from clients. The application works in this regard with no issue playing sounds and requests from multiple users.
Here is the Singleton:
public sealed class SoundboardSingleton : IDisposable
{
private static readonly Lazy<SoundboardSingleton> lazy =
new Lazy<SoundboardSingleton>(() => new SoundboardSingleton());
public static SoundboardSingleton Instance { get { return lazy.Value; } }
public WindowsMediaPlayer WMPlayer;
private StayAliveBot _liveBot;
private Thread _botThread;
private SoundboardSingleton()
{
WMPlayer = new WindowsMediaPlayer();
WMPlayer.settings.volume = 50;
_liveBot = new StayAliveBot();
_botThread = new Thread(_liveBot.Live);
_botThread.Start();
}
public void Dispose()
{
if (_botThread.IsAlive)
{
_botThread.Abort();
}
}
}
public class StayAliveBot
{
public void Live()
{
while (SoundboardSingleton.Instance != null)
{
Thread.Sleep(1500000);
SoundboardHelper.PlaySound("C:\\SoundboardOpFiles\\TestTone.wav");
}
}
}
and initially instantiated in Startup.cs via:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
// startup soundboard singleton
SoundboardSingleton.Instance.WMPlayer.settings.volume = 50;
}
}
I can run this application on my local dev machine with no issues or crashes. Everything functions as expected with no crashing. On deployment to the EC2 instance, everything on the site works properly, but there is now a crash / reset every 15 minutes.
My suspicion is either:
A) It is a problem with the WMPLib instance and some missing dependency on the Windows Server 2012 box that allows it to play sounds but causes crashes on regular intervals.
B) I've made a mistake with my singleton instantiation and it is somehow crashing my application.
I have tried the solution here but no results.
Any help would be appreciated.
edit: I have confirmed the issue is related to the usage of WMPLib as removing its use stopped the crashing every 15 minutes. Still not sure why this happens.
This is not a direct answer to your question but rather a different way do to the same thing. Instead of the WMPLib COM control, try using the thread-safe MediaPlayer class from WPF. Add references to WindowsBase and PresentationCore, and use something like this instead:
using System.Windows.Media;
public void PlaySound(string filename)
{
var mplayer = new MediaPlayer();
mplayer.MediaEnded += new EventHandler(MediaEndedHandler);
mplayer.Open(new Uri(filename));
mplayer.Play();
}
public void MediaEndedHandler(object sender, EventArgs e)
{
((MediaPlayer)sender).Close();
}
You can also use it as a singleton the same way as above, and it is fully thread-safe, which WMPLib is not.
Documentation here.
Edit:
As noted in the comments, you really could use just a static class with a public bool property to show busy signal. A static class in IIS is shared among all requests for an application and the class is only subject to garbage collection when the application pool is recycled, so you do need to be careful with the lifetime of the objects you store in it, to avoid memory consumption problems. This code will use a new instance of the media player class for each PlaySound() and disposes of it right after it's done playing, but the busy flag is common among all requests made to the server.
using System;
using System.Threading;
using System.Windows.Media;
namespace SoundBoardApp
{
public static class Soundboard
{
private static bool _isBusy = false;
public static bool IsBusy { get { return _isBusy; } }
private static void MediaEndedHandler(object sender, EventArgs e)
{
_isBusy = false;
var wmp = ((MediaPlayer)sender);
wmp.MediaEnded -= new EventHandler(MediaEndedHandler);
wmp.Close();
}
public static bool PlaySound(string filename)
{
if (!_isBusy)
{
_isBusy = true;
var wmp = new MediaPlayer();
wmp.MediaEnded += new EventHandler(MediaEndedHandler);
wmp.Volume = 0.5;
wmp.Open(new Uri(filename));
wmp.Play();
return true;
}
else
{
return false;
}
}
}
public class StayAliveBot
{
public void Live()
{
while (true)
{
Thread.Sleep(1500000);
if (!Soundboard.IsBusy) Soundboard.PlaySound("C:\\SoundboardOpFiles\\TestTone.wav");
}
}
}
}
I ended up using NAudio with my singleton pattern.
As per recommendations from Lex Li, I used a third party as the Windows.MediaPlayer was not meant for a web app. Based of the solution from Drunken Code Monkey, I used a boolean flag on the singleton to evaluate play states being checked frequently by a separate thread evaluating the PlaybackState.Stopped value on the IWavePlayer object in my singleton. My only concern is performance. I haven't noticed any problems yet, but I'm pretty sure managing Events in a Handler would have been more performant and a lot less code if it's even possible to do from a web app.
Here is the code:
using NAudio.Wave;
public sealed class SoundboardSingleton : IDisposable
{
private static readonly Lazy<SoundboardSingleton> lazy =
new Lazy<SoundboardSingleton>(() => new SoundboardSingleton());
public static SoundboardSingleton Instance { get { return lazy.Value; } }
public IWavePlayer WaveOutDevice { get; set; }
public AudioFileReader AudioFileReaderObj { get; set; }
public float Volume { get; set; }
private MediaCloser _mediaCloser;
private Thread _mediaCloserThread;
private StayAliveBot _liveBot;
private Thread _botThread;
public bool IsBusy { get; set; }
private SoundboardSingleton()
{
// checks our NAudio WaveOutDevice playback for stop
_mediaCloser = new MediaCloser();
_mediaCloserThread = new Thread(_mediaCloser.CheckForStoppedPlayback);
_mediaCloserThread.Start();
// thread to play sound every 25 minutes, to avoid idle flag
_liveBot = new StayAliveBot();
_botThread = new Thread(_liveBot.Live);
_botThread.Start();
}
public bool PlaySound(string filename)
{
// make sure we are not active
if (IsBusy) { return false; }
// process sound
IsBusy = true;
WaveOutDevice = new WaveOutEvent();
AudioFileReaderObj = new AudioFileReader(filename);
AudioFileReaderObj.Volume = Volume;
WaveOutDevice.Init(AudioFileReaderObj);
WaveOutDevice.Play();
return true;
}
public void CloseWaveOut()
{
// clean up sound objects
WaveOutDevice?.Stop();
if (AudioFileReaderObj != null)
{
AudioFileReaderObj.Dispose();
AudioFileReaderObj = null;
}
if (WaveOutDevice != null)
{
WaveOutDevice.Dispose();
WaveOutDevice = null;
}
}
public void Dispose()
{
if (_mediaCloserThread.IsAlive)
{
_mediaCloserThread.Abort();
}
if (_botThread.IsAlive)
{
_botThread.Abort();
}
}
}
public class MediaCloser
{
public void CheckForStoppedPlayback()
{
while (true)
{
// continuously check for our stopped playback state to cleanup
Thread.Sleep(500);
if (SoundboardSingleton.Instance.WaveOutDevice != null &&
SoundboardSingleton.Instance.WaveOutDevice.PlaybackState == PlaybackState.Stopped)
{
SoundboardSingleton.Instance.CloseWaveOut();
SoundboardSingleton.Instance.IsBusy = false;
}
}
}
}
public class StayAliveBot
{
public void Live()
{
while (true)
{
// prevent bot from going idle
Thread.Sleep(1500000);
if (!SoundboardSingleton.Instance.IsBusy)
{
SoundboardSingleton.Instance.PlaySound(ConfigurationManager.AppSettings["SoundboardHeartbeatFile"]);
}
}
}
}
Hopefully this helps anyone running into the same problems. My site has been up and running for a few hours now with no issues and clients spamming the board. Thanks again everyone who helped.

Get current Activity - Xamarin Android

I am developing an portable App for Android and iOS. My current function is taking a Screenshot and use that image in the code. Therefor I have an Interface in the portable library.
public interface IFileSystemService
{
string GetAppDataFolder();
}
I am taking the Screenshot also in the portable Library with the following code:
static public bool TakeScreenshot()
{
try
{
byte[] ScreenshotBytes = DependencyService.Get<Interface.IScreenshotManager>().TakeScreenshot();
return true;
}
catch (Exception ex)
{
}
return false;
}
This either calls the Android or the iOS version.
Android:
class ScreenshotManagerAndroid : IScreenshotManager
{
public static Activity Activity { get; set; }
public byte[] TakeScreenshot()
{
if (Activity == null)
{
throw new Exception("You have to set ScreenshotManager.Activity in your Android project");
}
var view = Activity.Window.DecorView;
view.DrawingCacheEnabled = true;
Bitmap bitmap = view.GetDrawingCache(true);
byte[] bitmapData;
using (var stream = new MemoryStream())
{
bitmap.Compress(Bitmap.CompressFormat.Png, 0, stream);
bitmapData = stream.ToArray();
}
return bitmapData;
}
The question now is to get the current Activity from my app.
A better way would be to use the Standalone Current Activity Plugin or the Current Activity Property in the Xamarin Essentials Plugin. Then you could just do:
Standalone: CrossCurrentActivity.Current.Activity
Xamarin Essentials: Platform.CurrentActivity
If you do not want to use a plugin and you only have 1 Activity in your app, you could get away with assigning a static variable in MainActivity and referencing that where ever you needed it like this:
public class MainActivity : FormsApplicationActivity {
public static Context Context;
public MainActivity () {
Context = this;
}
}
If you needed Context within a custom renderer, you would want to use the Context passed into the constructor, like this:
public class MyEntryRenderer : EntryRenderer {
private readonly Context _context;
public MyEntryRenderer(Context context) : base(context) {
_context = context;
}
// Now use _context or ((Activity)_context) any where you need to (just make sure you pass it into the base constructor)
}
The old deprecated way would be Context view = (Activity)Xamarin.Forms.Forms.Context
Xamarin automatically assigns the Activity to Forms.Context.
Since the release of Xamarin 2.5, Xamarin.Forms.Forms.Context is obsolete. The Context can now be obtained as follows:
var currentContext = Android.App.Application.Context;
var activity = (Activity)Forms.Context;
or if you are using MainActivity
var activity = (MainActivity)Forms.Context;
If you are using Xamarin Essentials 1.5 or higher, then you can use Platform.CurrentActivity. This is basically the equivalent of using the CurrentActivity plugin.
Ensure you initialise this correctly as per the instructions ie. in MainActivity OnCreate add the following line
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
I was trying to do something similar in Xamarin 5 and am having some luck in both my Android and iOS versions using
Shell.Current.CurrentPage
So something happens like a screenshot or a login and that method (whatever it is) can fire a static event so that any interested activity can look for itself whether it's the active view or not, and if so consume the data (byte array etc.) transported by the event.
class FileChooserPage : ContentPage
{
public FileChooserPage()
{
InitializeComponent();
GoogleDriveService.Authenticated += GoogleDriveService_Authenticated;
}
private void GoogleDriveService_Authenticated(object sender, EventArgs e)
{
if (ReferenceEquals(this, Shell.Current.CurrentPage))
{
Populate(e);
}
}
}

wp8 Application object

I am new to Windows phone programming and I am building a WP8 application and would like to access the "App" object from another module
eg:
ModuleA = where the 'public partial class App : Application' object lives
ModuleB = where 'DoThis.xaml' page lives
I have this in ModuleA:
public partial class App : Application
{
// .. most application stuff stripped out for brevity
private void Application_Launching(object sender, LaunchingEventArgs e)
{
// refresh the value of the IsTrial property when the application is launched
DetermineIsTrial();
string uriString = "/ModuleB;component/DoThis.xaml";
NavigationService.Navigate(new Uri(uriString, UriKind.Relative));
}
#region Trial
public static bool IsTrial
{
get;
// setting the IsTrial property from outside is not allowed
private set;
}
private void DetermineIsTrial()
{
#if TRIAL
// set true if trial enabled (Debug_Trial configuration is active)
IsTrial = true;
#else
var license = new Microsoft.Phone.Marketplace.LicenseInformation();
IsTrial = license.IsTrial();
#endif
#if DEBUG
// set to false if we are debugging....
//IsTrial = false;
#endif
}
#endregion
}
I don't know how to get the "App" object from ModuleA over to ModuleB so I can access it
I would like to do this in ModuleB
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
Debug.WriteLine("DoThis- OnNavigatedTo");
if( App.IsTrial ) // I would like this to be ModuleA's "App" object
{
// disable some functionality because trial mode...
}
// the rest cut for brevity
}
Thanks for your help !
You can always access the Application object via Application.Current.
Declare an interface in your module class:
public interface IMyApplication
{
void DoStuffInMainApp();
}
and implement it in your application class:
public partial class App : Application, ModuleB.IMyApplication
{
...
}
Now you can call a method in your application class from your module:
((IMyApplication)Application.Current).DoStuffInMainApp();
Since Module B can't know anything about Module A, you'll need to either create a shared Module C, or include all shared components in B.
I like a dependency-injection type of approach, where a given class (eg, the Page) calls for any external dependencies (eg, IsTrial), so that the owner of the class must inject all dependencies. I use something like this in my apps:
// settings class that the Pages will get access to
public interface ISettings
{
public bool IsTrial { get; }
}
// implementation of ISettings -- owned by the App class
public class Settings : ISettings
{
public bool IsTrial { get; set; }
}
// interface that a Page should inherit if it needs access to IsTrial
public interface IRequiresSettings
{
public ISettings { set; }
}
public class SomePage : PhoneApplicationPage, IRequiresSettings
{
public ISettings Settings { get; set; }
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
if( Settings != null && Settings.IsTrial )
{
// disable some functionality because trial mode...
}
}
}
Notice that ISettings encapsulates the read-only behavior of IsTrial, so that the page sees the property as read-only.
There is one more step, and that is to actually set ISettings. The App class should be responsible for this, by handling the RootFrame.Navigated event. It should check if the navigated-to page inherits IRequiresSettings, and set that property accordingly.
private Settings _settings = new Settings();
private void InitializePhoneApplication()
{
RootFrame.Navigated += RootFrame_Navigated;
}
void RootFrame_Navigated(object sender, NavigationEventArgs e)
{
if (e.Content is IRequiresSettings)
((IRequiresSettings)e.Content).Settings = _settings;
}
Edited: I deleted my "quick and dirty" approach, because #GerritFölster's answer is as quick and not dirty.

Categories

Resources