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.
Related
I have a Unity + Zenject setup with a ProjectInstaller with some global dependencies that adhere to a "modal" interface, e.g.,
public class ProjectInstaller : MonoInstaller {
public override void InstallBindings() {
Container.Bind<ModalManager>().AsSingle();
Container.Bind<Modal>().To<DialogManager>().AsSingle();
}
}
Some modals are only relevant to certain scenes, so I bind those in the SceneInstaller:
public class SceneInstaller : MonoInstaller {
public override void InstallBindings() {
Container.BindInterfacesAndSelfTo<InventoryManager>()
.FromComponentInNewPrefab(InventoryPrefab)
.AsSingle()
}
}
I want to manage all modals from the single ModalManager, defined at the project scope. So it has a List<Modal> binding:
public class ModalManager : MonoBehaviour {
[Inject]
protected List<Modal> _modals;
}
When I run this, the ModalManager only gets a single modal: the one defined in the project scope. In my understanding the SceneContext is a subcontainer of the ProjectContext. So I should be able to use FromSubContainerResolve in the ProjectInstaller to bind items in the child scene, perhaps by adding a line like:
// ProjectInstaller.cs
public override void InstallBindings() {
// ...
Container.Bind<Modal>().To<InventoryManager>().FromSubContainerResolve();
}
But I'm not sure which of the eleventy FromSubContainerResolve methods make sense for this case. They all seem pertinent to prefabs with a game object context, not for use from within the ProjectContext.
Does this use case make sense? Is there an easier or better way?
The problem is that that ModalManager can only be injected with dependencies that are added directly to ProjectContext. For these kinds of problems I recommend using the following pattern:
public interface IModal
{
}
public class ModalManager
{
private readonly List<IModal> _modals = new List<IModal>();
public IReadOnlyList<IModal> Modals
{
get { return _modals; }
}
public void AddModal(IModal modal)
{
_modals.Add(modal);
}
public bool RemoveModal(IModal modal)
{
return _modals.Remove(modal);
}
}
public class ModalRegisterHandler : IInitializable, IDisposable
{
private readonly List<IModal> _modals;
private readonly ModalManager _modalManager;
public ModalRegisterHandler(
// We need to use InjectSources.Local here, otherwise we will
// add any project context modals again in each scene
[Inject(Source = InjectSources.Local)]
List<IModal> modals, ModalManager modalManager)
{
_modals = modals;
_modalManager = modalManager;
}
public void Initialize()
{
foreach (var modal in _modals)
{
_modalManager.AddModal(modal);
}
}
public void Dispose()
{
// We don't want ModalManager to retain references to Modals defined in unloaded scenes
// (dispose is executed on scene unload)
foreach (var modal in _modals)
{
_modalManager.RemoveModal(modal);
}
}
}
public class SceneInstaller : MonoInstaller
{
public override void InstallBindings()
{
Container.Bind<IModal>().To<FooModal>();
Container.Bind<IModal>().To<BarModal>();
}
}
public class ProjectInstaller : MonoInstaller
{
public override void InstallBindings()
{
// We use CopyIntoDirectSubContainers so that ModalRegisterHandler gets automatically added to every
// scene context
Container.BindInterfacesTo<ModalRegisterHandler>().AsSingle().CopyIntoDirectSubContainers();
Container.Bind<ModalManager>().AsSingle();
Container.Bind<IModal>().To<QuxModal>();
Container.Bind<IModal>().To<FizzModal>();
}
}
I am actually trying to archive global variables in Xamarin where any page can consume it. After a lot of research, looks like the best way to archive such thing is using the Singleton design pattern. I am facing difficulty to implement this. take a look...
global.cs
using System;
namespace xamarin_forms
{
sealed class Global
{
public string test { get; set; }
private static Global _instance = null;
private Global()
{
}
static internal Global Instance()
{
if (_instance == null)
{
_instance = new Global();
}
return _instance;
}
}
}
App.xaml.cs
using Xamarin.Forms;
namespace xamarin_forms
{
public partial class App : Application
{
Global global = Global.Instance();
public App()
{
InitializeComponent();
MainPage = new PageWelcome();
global.test = "123";
}
protected override void OnStart()
{
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
Ok, so far, I just created my singleton class with a simple test property. I set this to 123 when I initialize my application.
Now, on another page, welcome page...I'd like to read the value that I set previously on the initialization...
PageWelcome.xaml.cs
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace xamarin_forms
{
public partial class PageWelcome : ContentPage
{
public PageWelcome()
{
InitializeComponent();
Global global = Global.Instance();
DisplayAlert("Alert", global.test, "OK");
}
}
}
Actually this is not working. It's returns me a null. So, how to use this correctly ? Thanks !
In your App's constructor, you first create an instance of PageWelcome. This instance reads the test property of your Global singleton and displays its contents in an alert. At this point, no value has been assigned to that property as far as I can see.
It is only after the PageWelcome constructor finishes that you actually assign a value to the test property of your singleton. Change your App constructor to
public App()
{
InitializeComponent();
global.test = "123";
MainPage = new PageWelcome();
}
and it should work as expected.
You don't need a Singleton.
Just create a static class with your variables static and you would be able to use them on any Page, like you want global variables.
// 1. Create static class Global with string _Test
public static class Global
{
public static void Init()
{
// your init class
}
private static string _Test { get; set; }
public static string Test
{
get => return _Test;
set => _Test = value;
}
}
// 2. Init Global in your App.cs
public App()
{
Global.Init();
}
// 3. Then use them on any page
public PageWelcome()
{
Global.Test = "123";
}
Is the setter injection supported in the Xamarin.forms?
I have a service injected in the bootstrapper like this
Container.RegisterType<ICommonService, CommonService>();
And inside a viewmodel, I want to have an instance injected to a property like this
[Dependency]
public ICommonService CommonService { get; set; }
But in the runtime, the property CommonService is always null.
The attribute I used is the Microsoft.Practices.Unity.DependencyAttribute, not Xamarin.Forms.DependencyAttribute
If I inject inside the constructor, it works
public LandingPageViewModel(INavigationService navigationService, ICommonService commonService)
Edited: added code snippet
public class Bootstrapper : UnityBootstrapper
{
protected override Page CreateMainPage()
{
try
{
return Container.Resolve<Views.LandingPage>();
}
catch (Exception exception)
{
//TODO: intent to get exception info
throw;
}
}
protected override void RegisterTypes()
{
DependencyResolver.Instance.Initialize(Container);
this.RegisterViews();
this.RegisterServices();
this.RegisterSingleton();
}
private void RegisterViews()
{
Container.RegisterTypeForNavigation<LandingPage>();
Container.RegisterTypeForNavigation<Page1>();
}
private void RegisterServices()
{
Container.RegisterType<ICommonService, CommonService>();
}
private void RegisterSingleton()
{
}
}
public partial class App : Application
{
public App()
{
InitializeComponent();
var bootstrapper = new Bootstrapper();
bootstrapper.Run(this);
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
Hmm they removed the DependencyAttribute injection feature since Prism 7.0, I think we should register it manually. (your code snippet should work)
Look: https://brianlagunas.com/whats-new-in-prism-for-xamarin-forms-7-0/
I have created a WinForms MVC application using Dependency Injection (DI) and Ninject as the DI Container. The basic architecture is as follows
Program.cs (the main entry point of the WinForms application):
static class Program
{
[STAThread]
static void Main()
{
...
CompositionRoot.Initialize(new DependencyModule());
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(CompositionRoot.Resolve<ApplicationShellView>());
}
}
DependencyModule.cs
public class DependencyModule : NinjectModule
{
public override void Load()
{
Bind<IApplicationShellView>().To<ApplicationShellView>();
Bind<IDocumentController>().To<SpreadsheetController>();
Bind<ISpreadsheetView>().To<SpreadsheetView>();
}
}
CompositionRoot.cs
public class CompositionRoot
{
private static IKernel ninjectKernel;
public static void Initialize(INinjectModule module)
{
ninjectKernel = new StandardKernel(module);
}
public static T Resolve<T>()
{
return ninjectKernel.Get<T>();
}
public static IEnumerable<T> ResolveAll<T>()
{
return ninjectKernel.GetAll<T>();
}
}
ApplicationShellView.cs (the main form of the application)
public partial class ApplicationShellView : C1RibbonForm, IApplicationShellView
{
private ApplicationShellController controller;
public ApplicationShellView()
{
this.controller = new ApplicationShellController(this);
InitializeComponent();
}
public void InitializeView()
{
dockPanel.Extender.FloatWindowFactory = new CustomFloatWindowFactory();
dockPanel.Theme = vS2012LightTheme;
}
private void ribbonButtonTest_Click(object sender, EventArgs e)
{
controller.OpenNewSpreadsheet();
}
public DockPanel DockPanel
{
get { return dockPanel; }
}
}
where
public interface IApplicationShellView
{
void InitializeView();
DockPanel DockPanel { get; }
}
ApplicationShellController.cs
public class ApplicationShellController
{
private IApplicationShellView shellView;
public ApplicationShellController(IApplicationShellView view)
{
this.shellView = view;
}
public void OpenNewSpreadsheet(DockState dockState = DockState.Document)
{
SpreadsheetController controller = (SpreadsheetController)GetDocumentController("new.xlsx");
SpreadsheetView view = (SpreadsheetView)controller.New("new.xlsx");
view.Show(shellView.DockPanel, dockState);
}
private IDocumentController GetDocumentController(string path)
{
return CompositionRoot.ResolveAll<IDocumentController>()
.SingleOrDefault(provider => provider.Handles(path));
}
public IApplicationShellView ShellView { get { return shellView; } }
}
SpreadsheetController.cs
public class SpreadsheetController : IDocumentController
{
private ISpreadsheetView view;
public SpreadsheetController(ISpreadsheetView view)
{
this.view = view;
this.view.SetController(this);
}
public bool Handles(string path)
{
string extension = Path.GetExtension(path);
if (!String.IsNullOrEmpty(extension))
{
if (FileTypes.Any(ft => ft.FileExtension.CompareNoCase(extension)))
return true;
}
return false;
}
public void SetViewActive(bool isActive)
{
((SpreadsheetView)view).ShowIcon = isActive;
}
public IDocumentView New(string fileName)
{
// Opens a new file correctly.
}
public IDocumentView Open(string path)
{
// Opens an Excel file correctly.
}
public IEnumerable<DocumentFileType> FileTypes
{
get
{
return new List<DocumentFileType>()
{
new DocumentFileType("CSV", ".csv" ),
new DocumentFileType("Excel", ".xls"),
new DocumentFileType("Excel10", ".xlsx")
};
}
}
}
where the implemented interface is
public interface IDocumentController
{
bool Handles(string path);
void SetViewActive(bool isActive);
IDocumentView New(string fileName);
IDocumentView Open(string path);
IEnumerable<DocumentFileType> FileTypes { get; }
}
Now the view ascociated with this controller is
public partial class SpreadsheetView : DockContent, ISpreadsheetView
{
private IDocumentController controller;
public SpreadsheetView()
{
InitializeComponent();
}
private void SpreadsheetView_Activated(object sender, EventArgs e)
{
controller.SetViewActive(true);
}
private void SpreadsheetView_Deactivate(object sender, EventArgs e)
{
controller.SetViewActive(false);
}
public void SetController(IDocumentController controller)
{
this.controller = controller;
Log.Trace("SpreadsheetView.SetController(): Controller set successfully");
}
public string DisplayName
{
get { return Text; }
set { Text = value; }
}
public WorkbookView WorkbookView
{
get { return workbookView; }
set { workbookView = value; }
}
...
}
Finally the view interfaces are
public interface ISpreadsheetView : IDocumentView
{
WorkbookView WorkbookView { get; set; }
}
and
public interface IDocumentView
{
void SetController(IDocumentController controller);
string DisplayName { get; set; }
bool StatusBarVisible { get; set; }
}
Now for my questions. In Seemann's book "Dependency Injection in .NET" he talks about the "Three Calls Pattern" and this is what I have attempted to implement in the above. The code works, the shell view displays and via the MVC pattern my controllers correctly open views etc. However, I am confused as the above definately has the flavour of the "Service Locator Anti-Pattern". In chapter 3 of Seemann's book he states
The COMPOSITION ROOT pattern describes where you should use a DI CONTAINER. However,
it doesn’t state how to use it. The REGISTER RESOLVE RELEASE pattern addresses
this question [...] A DI CONTAINER should be used in three successive
phases called Register, Resolve, and Release.
In its pure form, the REGISTER RESOLVE RELEASE pattern states that you should only
make a single method call in each phase. Krzysztof Kozimic calls this the Three Calls Pattern.
Configuring a DI CONTAINER in a single method call requires more explanation. The
reason that registration of components should happen in a single method call is
because you should regard configuration of a DI CONTAINER as a single, atomic action.
Once configuration is completed, the container should be regarded as read-only.
This sounds like the dredded "Service locator", why is this not deemed service location?
In order to adjust my code to instead use Contstructor Injection, I changed my entry code to
[STAThread]
static void Main()
{
var kernel = new StandardKernel();
kernel.Bind(t => t.FromThisAssembly()
.SelectAllClasses()
.BindAllInterfaces());
FileLogHandler fileLogHandler = new FileLogHandler(Utils.GetLogFilePath());
Log.LogHandler = fileLogHandler;
Log.Trace("Program.Main(): Logging initialized");
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(kernel.Get<ApplicationShellView>());
}
using Ninject.Extensions.Conventions, I then changed ApplicationShellController in order to correct my code to inject the IDocumentControllers via ctor injection:
public class ApplicationShellController
{
private IApplicationShellView shellView;
private IEnumerable<IDocumentController> controllers;
public ApplicationShellController(IApplicationShellView shellView, IEnumerable<IDocumentController> controllers)
{
this.shellView = shellView;
this.controllers = controllers;
Log.Trace("ApplicationShellController.Ctor(): Shell initialized successfully");
}
...
}
where
public class SpreadsheetController : IDocumentController
{
private ISpreadsheetView view;
public SpreadsheetController(ISpreadsheetView view)
{
this.view = view;
this.view.SetController(this);
}
...
}
but this leads to a circular dependency, how do I handle this?
Question Summary:
Why is my initial use of Ninject using "Thee Calls Pattern" and CompositionRoot.Resolve<T>() bad or different to the Service Locator Anti-Pattern?
How can I resolve the circular dependency issue above if I want to switch to pure ctor injection?
Thanks very much for your time.
At some point in the process, you have to use service location. However, the difference between DI and SL is that in SL, you are resolving your services at the point they are requested, whereas in DI you resolve them in some kind of factory (such as a controller factory) and then construct your objects and pass the reference in.
You should create some kind of infrastructure that dispatches your commands and uses a factory of some kind to locate the dependencies used by the created objects.
In this way, the rest of your code doesn't have dependency resolution, and you are following a DI pattern except at the construction point.
Class:
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerSession)]
public class MainService : IChat
{
IChatCallback ChatCallback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
Chat chat = new Chat(this);
public void ShowChat()
{
chat.Show();
}
public void SendInstantMessage(string user, string message)
{
chat.RaiseMsgEvents(user, message);
ChatCallback.InstantMessage(user, message);
}
}
Form:
public partial class Chat : Form
{
MainService service;
public Chat(MainService service)
{
InitializeComponent();
OnMsgReceivedEvent += new OnMsgReceived(callback_OnMsgReceivedEvent);
this.service = service;
}
private void btnSend_Click(object sender, EventArgs e)
{
service.SendInstantMessage("Admin", txtMsg.Text);
}
}
The mainForm use the class like this:
public partial class Form1 : Form
{
ServiceHost host;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
host = new ServiceHost(typeof(WCF_Server.MainService));
host.Open();
}
}
In the main form, i just pass the class, no initializing, but in the class when ShowChat() called i need to show the chat form and get to this class method so i can send messages.
.NET is an object oriented language. In fact, every class is an object.
The error you are getting is because you're instantiating an object with "this" on the global level.
UPDATE
Based on your update you could do the following and it will work. You might want to refactor this some more to ensure that it's not going to break any business rules etc.
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.PerSession)]
public class MainService : IChat
{
IChatCallback ChatCallback = OperationContext.Current.GetCallbackChannel<IChatCallback>();
//Changed this to be just a declaration. This will be null,
// as there is no object yet, this is really just a pointer to nothing.
//This tells the system that you might/are planning to use an object called
//chat, but it doesn't exist yet.
Chat chat;
// Get your default constructor going. This will create the actual chat object, allowing the rest of your class to access it.
public MainService()
{
//instantiate it! (or as some of our co-ops say "We're newing it")
chat = new Chat(this);
}
//now that chat is actually instantiated/created you can use it.
public void ShowChat()
{
chat.Show();
}
public void SendInstantMessage(string user, string message)
{
chat.RaiseMsgEvents(user, message);
ChatCallback.InstantMessage(user, message);
}
}
This is just a personal pet peeve, but having a function parameter the same name as a global variable is... well for me a no no. I noticed this on your Chat.Chat(MainService) function.
Of course it is, just create a method that takes this class of yours as a parameter and call it...
As other posts have suggested, you'll want to re-consider how you instantiate your chat field within your example class. I would consider lazy loading the property, like so...
private ChatForm _Chat = null;
private ChatForm Chat
{
get
{
if (this._Chat == null)
{
this._Chat = new ChatForm(this);
}
return this._Chat;
}
set { this._Chat = value; }
}
Using lazy-loading will ensure you're able to use the keyword this upon request.