How to load default view in Prism WPF C# - c#

I am using this Repo:
https://github.com/PrismLibrary/Prism-Samples-Wpf/tree/master/17-BasicRegionNavigation
It works, but I need to make changes:
when run the app, I must click to the selected view to load, so I need to show the default view.
After click
the Bootstrapper.cs file code:
using Microsoft.Practices.Unity;
using Prism.Unity;
using NavigationParticipation.Views;
using System.Windows;
using Prism.Modularity;
using ModuleA;
namespace NavigationParticipation
{
class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void InitializeShell()
{
Application.Current.MainWindow.Show();
}
protected override void ConfigureModuleCatalog()
{
var catalog = (ModuleCatalog)ModuleCatalog;
catalog.AddModule(typeof(ModuleAModule));
}
}
}
Folder Structure (Solution)
And how to show The Login window Or another window before initialize the MainWindow?

One of the way is to add CompositeCommand somewhere outside modules so they all can use it...
public class ApplicationCommands
{
public static CompositeCommand NavigateCommand = new CompositeCommand();
}
and then if you wanna ViewA to be default
inside ModuleAModule in method Initialize() add
ApplicationCommands.NavigateCommand.Execute(typeof(ViewA));
if you want ViewB to be default then inside ModuleBModule in method Initialize() add
ApplicationCommands.NavigateCommand.Execute(typeof(ViewB));
There are couple more ways, but dont know your logic and all code used, so this one seems the easiest one.
and if you are using IRegionManager then you can do something like this.
_regionManager.RegisterViewWithRegion("YourRegionName", typeof(ViewA));

I solve A part of the Problem :
by adding this code to :
17-BasicRegionNavigation/ModuleA/ModuleAModule.cs
this code:
_regionManager.RequestNavigate("ContentRegion", new Uri("ViewA", UriKind.Relative));
where: ViewA correspond to the Target view that you want to show.
and the ContentRegion correspond to your MainRegion, you can find it in :
17-BasicRegionNavigation/BasicRegionNavigation/ViewModels/MainWindowViewModel.cs
and looks like:
using Microsoft.Practices.Unity;
using ModuleA.Views;
using Prism.Modularity;
using Prism.Regions;
using Prism.Unity;
using System;
namespace ModuleA
{
public class ModuleAModule : IModule
{
IRegionManager _regionManager;
IUnityContainer _container;
public ModuleAModule(RegionManager regionManager, IUnityContainer container)
{
_regionManager = regionManager;
_container = container;
}
public void Initialize()
{
_container.RegisterTypeForNavigation<ViewA>();
_container.RegisterTypeForNavigation<ViewB>();
_regionManager.RequestNavigate("ContentRegion", new Uri("ViewA", UriKind.Relative));
}
}
}
Now How To Add Login Window before MainWindow initialized ??

Related

WPF MVVM compliant way to open views

I would love to find a simple and MVVM compliant way,
to open a new view from the MainWindow.
I have already worked through some instructions and tutorials.
But somehow none really worked or it was a mixture of code behind.
I would like to open a view after pressing a button and edit an ObservableCollection in it.
I have already created DataTemplates in App.xaml and mapped the ViewModels with the respective views.
But I don't know how to cleanly open a separate window from the MainWindow (MainViewModel) via an ICommand for another ViewModel.
You should't open a window directly from the MainWindow nor the MainWindowViewModel. But the view model may use a service to open a window:
public class MainWindowViewModel
{
private readonly IWindowService _service;
public MainWindowViewModel (IWindowService service)
{
_service = service;
}
//...
public void OpenWindowExecuted()
{
_service.ShowWindow();
}
}
Service:
public interface IWindowService
{
void ShowWindow();
}
public class WindowService : IWindowService
{
public void ShowWindow();
{
Window window = new Window()
window.Show();
}
}
You could easily mock out the service in your unit tests.

Navigate to a Default View when Application Loaded using Prism 7 in WPF

I follows samples code provided in https://github.com/PrismLibrary/Prism-Samples-Wpf/blob/master/17-BasicRegionNavigation
I want to achieve the following result when I run the application (without explicitly clicking Navigate to View A). Does anyone know how to do it?
I have tried adding Navigate("ViewA"); after this line. However, I cannot get the desired outcome. Is it because the module hasn't been initialized?
Thanks.
did you add your module to the modulecatalog using override method ConfigureModuleCatalog? take a look at here
Eventually I solve by adding the following code in MainWindow.xaml.cs
public partial class MainWindow
{
IRegionManager _regionManager;
public MainWindow()
{
InitializeComponent();
_regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
RegionManager.SetRegionManager(ContentRegion, _regionManager);
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
_regionManager.RequestNavigate("ContentRegion", "ViewA");
}
}
Get idea from: https://github.com/MahApps/MahApps.Metro/issues/1020#issuecomment-44779574
I'm kinda late to the party here, but I also stumbled over the question of how to navigate to a default view during the applications startup.
I found two ways:
1. App decides the default view
This can be solved in the CreateShell()-override in the App-Class.
This is my CreateShell-Method:
/// <inheritdoc />
protected override Window CreateShell()
{
var window = this.Container.Resolve<MainWindow>();
window.Loaded += (sender, args) =>
{
var manager = this.Container.Resolve<IRegionManager>();
manager.RequestNavigate("ContentRegion", "ViewA");
};
return window;
}
2. ViewModel decides the default view
Add a constructor to MainWindowViewModel that looks like this:
public MainWindowViewModel(IRegionManager regionManager)
{
regionManager.RegisterViewWithRegion("ContentRegion", "ViewA");
}

Implement Prism in existing Xamarin.Forms Shared Application

I would like to know of there is a valid way to implement Prism in a existing Xamarin.Forms Shared project. I'm sure others would also like to know if they can profit from what Prism has to offer without having to convert their existing project to a PCL project. All existing examples I've found show a PCL project(probably for a good reason).
To try implementing Prism in my project I installed the Prims.Unity.Forms nuget to each platform project.
I tried to inherit from PrismApplication:
public partial class App : PrismApplication
{ }
But, this does not work. The app class does not allow me to inherit from a different base class.
Adding the following lines to my android project did not help:
protected override void OnCreate(Bundle bundle)
{
LoadApplication(new App(new AndroidInitializer()));
}
public class AndroidInitializer : IPlatformInitializer
{
public void RegisterTypes(IUnityContainer container)
{ }
}
Perhaps I am missing something or trying something that structurally is not possible.
Yes, it's completely possible.
First, ensure nuget packages are installed.
For example, for Prism and Unity you need:
<package id="Prism.Core" version="6.3.0" ... />
<package id="Prism.Forms" version="6.3.0" .../>
<package id="Prism.Unity.Forms" version="6.3.0" ... />
<package id="Unity" version="4.0.1" ... />
Add missing folders(just for order)
And move your existing page to the Views folder, but remember to
adjust the namespaces or your binding just won't work.
Change application base type
Remember to change application base type in code and XAML.
using Prism.Unity;
using Xamarin.Forms;
namespace XamPrismShared
{
public partial class App : PrismApplication
{
public App (IPlatformInitializer platformInitializer):base(platformInitializer)
{
}
}
}
Set the first page and its ViewModel
Implement OnInitialized and RegisterTypes. Remember that you need to register each type you want to use with Prism as a page.
using Prism.Unity;
using Xamarin.Forms;
namespace XamPrismShared
{
public partial class App : PrismApplication
{
public App (IPlatformInitializer platformInitializer):base(platformInitializer)
{
}
protected override void OnInitialized()
{
InitializeComponent();
NavigationService.NavigateAsync("MainPage");
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<NavigationPage>();
Container.RegisterTypeForNavigation<MainPage>();
}
}
}
If you have existing pages
Add ViewModelLocator.AutowireViewModel="True"to your existent views in order to allow Prism to automatically bind with their respective ViewModel.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True" x:Class="x:Class="XamPrismShared.Views.MainPage"" Title="MainPage">
<Label Text="{Binding Title}"
VerticalOptions="Center"
HorizontalOptions="Center" />
</ContentPage>
Add the missing ViewModel
using Prism.Mvvm;
namespace XamPrismShared.ViewModels
{
public class MainPageViewModel : BindableBase
{
public MainPageViewModel()
{
Title = "Hi from Prism.";
}
public string Title { get; set; }
}
}
Add Platform initializers in each platform project
Add the missing platform initializers and fix Xamarin.Forms load.
For Android,
using Android.App;
using Android.Content.PM;
using Android.OS;
using Microsoft.Practices.Unity;
using Prism.Unity;
namespace XamPrismShared.Droid
{
[Activity (Label = "XamPrismShared", Icon = "#drawable/icon", Theme="#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate (Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate (bundle);
global::Xamarin.Forms.Forms.Init (this, bundle);
LoadApplication (new XamPrismShared.App(new AndroidPlatformInitializer()));
}
}
public class AndroidPlatformInitializer : IPlatformInitializer
{
public void RegisterTypes(IUnityContainer container)
{
}
}
}
For iOS,
using Foundation;
using Microsoft.Practices.Unity;
using Prism.Unity;
using UIKit;
namespace XamPrismShared.iOS
{
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init ();
LoadApplication (new XamPrismShared.App(new iOSPlatformInitializer()));
return base.FinishedLaunching (app, options);
}
}
public class iOSPlatformInitializer : IPlatformInitializer
{
public void RegisterTypes(IUnityContainer container)
{
}
}
}
And voilĂ 
If you have any question or want to check, you can review the code in Github https://github.com/jesulink2514/Xamarin-Prism-shared-project

Error: No matching constructor found on type

I have a WPF application where I am using multiple forms. There is one main form which gets opened when we start the application which is know as MainWindow.xaml. This form then have multiple forms which gets opened depending on the user option. There is a form StartClassWindow.xaml. Currently I am working on this form so I want it to start directly instead of MainWindow.xaml. So to do this I changed the app.xaml startupuri:
<Application x:Class="Class.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DispatcherUnhandledException="Application_DispatcherUnhandledException"
StartupUri="StartClassWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
But then it started giving error like below:
No matching constructor found on type 'Class.StartClassWindow'. You
can use the Arguments or FactoryMethod directives to construct this
type.' Line number '3' and line position '9'.
Here is the StartClassWindow.xaml.cs:
namespace Class
{
public partial class StartClassWindow : System.Windows.Window
{
public StartClassWindow(string classData)
{
InitializeComponent();
className = classData;
function();
}
//rest of the code.
}
}
You need to add a parameter-less constructor to your StartClassWindow like this:
public StartClassWindow(string classData)
{
InitializeComponent();
className = classData;
function();
}
public StartClassWindow()
{
}
Or if you don't want to have another constructor you can override the OnStartup method in the App.xaml.cs but you should remove the StartupUri="StartClassWindow.xaml" in your App.xaml first. Like below:
protected override void OnStartup(StartupEventArgs e)
{
StartClassWindow st = new StartClassWindow("");
st.Show();
}
"Normally", your constructor must be parameterless:
public Login()
But, since you are using dependency injection, like this:
public Login(IUserService userService)
The constructor isn't parameterless, and the framework cannot instantiate the page if it's not told how to.
For this there are a couple of options:
Remove service from constructor
Just like this, but you'll need to access the userservice differently:
public Login()
pre .net 4.8, 4.7, using Unity (or Prism)
You can use a dependency injection framework like Unity to register the components.
It is described here:
https://www.wpftutorial.net/ReferenceArchitecture.html
public class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
IUnityContainer container = new UnityContainer();
container.RegisterType<IUserService, UserService>();
MainWindow mainWindow = container.Resolve<MainWindow>();
mainWindow.Show();
}
}
Manual using Navigation Service
Manually navigate, and do your own construction:
NavigationService.Navigate(new LoginPage(new UserService);
As described here: https://learn.microsoft.com/en-us/dotnet/desktop/wpf/app-development/navigation-overview?view=netframeworkdesktop-4.8
.net 5 WPF, using built in DI
If you are using .net 5, here is a tutorial. Make sure to register both the window and the service:
https://executecommands.com/dependency-injection-in-wpf-net-core-csharp/
Here's an example:
private void ConfigureServices(ServiceCollection services)
{
services.AddScoped<IUserService,UserService>();
services.AddSingleton<MainWindow>();
}
private void OnStartup(object sender, StartupEventArgs e)
{
var mainWindow = serviceProvider.GetService<MainWindow>();
mainWindow.Show();
}

Setter injection with hierarchical controls in Winforms

Trying to inject dependencies to usercontrols in WinForms. As the instantiation of controls is generated by designer, only setter injection seems to be possible.
My objective is to directly reference DI container only at the form level:
public void Form(StructureMap.Container container)
{
InitializeComponent();
container.BuildUp(this); // this should also traverse Controls and their subcontrols
}
The problem is how to force DI container to traverse the Controls collection hierarchically and inject dependencies. Is this possible with any DI container? Trying with StructureMap and so far no luck:
Container container = new Container(delegate (ConfigurationExpression e)
{
InjectedClass c = new InjectedClass();
e.Policies.SetAllProperties(delegate (StructureMap.Configuration.DSL.SetterConvention x)
{
x.OfType<InjectedClass>();
});
e.For<InjectedClass>().Use(c);
});
Form form1 = new Form(container);
// here the form.Controls[0].Controls[0].MyInjectedClass has no instance
StructureMap seems to stop at the first level (injecting to Form.MyInjectedClass is working)
I Couldn't do it either.
I used structuremap with setter injection and called ObjectFactory.BuildUp in every control.
Define a CustomInjectAttribute
public class CustomInjectAttribute : Attribute
{
}
Define a Registry
public class MyRegistry : Registry
{
public MyRegistry()
{
this.Policies.SetAllProperties(by => by.Matching(prop => prop.HasAttribute<CustomInjectAttribute>()));
}
}
Then call BuildUp on Control's constructor:
public partial class MyControl : UserControl
{
public MyControl()
{
this.InitializeComponent();
ObjectFactory.BuildUp(this);
}
[CustomInject, Browsable(false)]
public IInjectable Injectable { protected get; set; }
}

Categories

Resources