Beginning in Prism - c#

I'm totally new in Prism (Composite Wpf). I want to create messaging module for my application: invisible panel on the top of the main window which appears when I invoke ShowMessage(string message) (and disappears after 5 seconds, for instance).
What I've done:
Create infrastructure project (contains only one interface IUIMessagesService)
Create module project:
Project contains user control - it's panel for the message (View)
Project contains UIMessagesService class, which implements IUIMessagesService
In module class I did so:
public UIMessagesModule(IRegionManager regionManager, IUnityContainer container)
{
_regionManager = regionManager;
_container = container;
}
and
public void Initialize()
{
_regionManager.RegisterViewWithRegion("UIMessagesRegion", typeof(UIMessagesView));
_container.RegisterType<IUIMessagesService, UIMessagesService>(new ContainerControlledLifetimeManager());
}
Create shell project (bootstrapper, shell view with region e.t.c)
Questions:
How can I change properties of my view in class UIMessagesService (in this case RenderTrasform to show panel)? May be I need define theese properties in view model? How to change view model properties?
How to execute module methods ShowMessages from application?

For your first question you can use Event Aggregation
For second:
you can use ServiceLocator or container to resolve your type

Not sure if this is exactly you wanted. But you can use wpfextended toolkit busyindicator. This can show on top of your view with a glossy screen and you can control it just by setting or binding IsBusy dependency property.
take a look at example here

Related

How is the automatic ViewModel creation working in Prism 7

I have created a small test WPF .net Framework solution using Prism7 with Unity. In my only Module I have a View and a ViewModel. I'm not using the AutoWireViewModel property on the View. Instead I have a constructor on the View that takes my ViewModel as a parameter:
public partial class ViewA : UserControl
{
public ViewA(ViewAViewModel viewModel)
{
InitializeComponent();
DataContext = viewModel;
}
}
When I run the application this works, but I can't understand how. How is the ViewModel resolved without me having added it to the Unity Container? Is this some default Prism magic? If it is, is there a place where it is described?
Would be thankful for any insight.
All Prism containers are configured to resolve Concrete types automatically as transients. This is what allows Prism to resolve any ViewModel regardless of whether you have registered it or not.

Prism RequestNavigate to new view

I am using WPF Prism 6 with autofac and having issues navigating between views. What I have is a view that I only want to keep alive till I leave it, and the next time I navigate to it, I want to create a new version of this view.
On load, I regist an IModule that has the following code
_regionManager.RegisterViewWithRegion(RegionNames.MainRegion,
typeof(DxfDisplay.Views.DxfDisplay));
This registers my view and the system works on initial load, I implement the INavigationAware and IRegionMemberLifetime interfaces on the view model and have public bool KeepAlive => false; implementing the IRegionMemberLifetime so that my view is disposed when I am done.
When I navigate away from this view everything is fine, but when I attempt to navigate to navigate to the view using
_regionManager.RequestNavigate(RegionNames.MainRegion,
new Uri("DxfDisplay", UriKind.Relative), parameters);
The view is not opened and a view model constructor is not called. To make the navigation work correctly, I need to register with view with the region again. Or if I change the KeepAlive to true I can navigate back to the original view, but I cannot generate a new view if INavigationAware.IsNavigationTarget returns false.
My question is how do I register the view with the region manager in such a way that when I call _regionManager.RequestNavigate, it will create a new instance of the view and display it. I feel like I am missing something simple and just overlooking it.
_builder.RegisterTypeForNavigation<DxfDisplay.Views.DxfDisplay>();
In Prism 7, this is now called RegisterForNavigation<T>() and exists on the IContainerRegistry interface.
For example, in your module:
public class MyModule : IModule
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<MyView>();
}
}

How can I trigger calling a method on an object in my view from within the viewmodel

My WPF application has a tool bar that contains several buttons that by default do not have any command bindings. Within the application various different forms/windows can be opened (think MDI) and each of these views is responsible for registering any of its commands with a shared utility that will apply them to the tool bar buttons.
The toolbar and other standard ui pieces reside in a separate library from the views and the shared utility I mentioned is the only interface for views to hook into the buttons. Here is an example usage of how the view registers a command
public class MyView : BaseView
{
public MyView(CommandHolder commandHolder) : base(commandHolder)
{
SaveCommand = new DelegateCommand(Save,CanSave);
//RegisterCommand is a method on BaseView that works with the commandHolder object
RegisterCommand(DefaultCommands.SaveCommand, SaveCommand);
}
}
This functionality is working great for all back end tasks, like saving and searchign, but now I find that I need to be able to trigger functionality that exists within the view from one of these commands.
The specific example here is I have an Export to Excel button in my toolbar that I want to of course generate a xlsx file based on data in a grid.
I am using DevExpress controls and their grid supports the following method call:
//taken from xaml code behind
grid.ExportToXlsx(#"c:\grid_export.xlsx");
Is there any way for my command execute method to be able to trigger this call short of giving the viewmodel a reference to the view?
Prism has an EventAggregator component for loosely coupled communication between components in your application.
Create a class that inherits from CompositePresentationEvent. The generic type is the type of any data you want to use as a payload
public class ExportRequestedEvent : CompositePresentationEvent<object> { }
Create an event aggregator using the Singleton pattern or your IoC container of choice so that the instance is shared across relevant components.
IEventAggregator _aggregator = new EventAggregator();
In the command use the event aggregator to get the event and publish
_aggregator.GetEvent<ExportRequestedEvent>().Publish(null);
In your view you can register a method to handle the event using the Subscribe method
_aggregator.GetEvent<ExportRequestedEvent>().Subscribe(Export);
private void Export()
{
grid.ExportToXlsx(#"c:\grid_export.xlsx");
}
You can add your own functionality to the views to determine which one is currently active and should act on the event.

What is the proper pattern for instantiating and showing a window that relies on a value selected in the current window?

Using dependency injection principles and a DI container, what is the proper pattern to launch a child dialog that requires a selection made in the parent window as a dependency? Example: Select a PersonViewModel in a list on the main window, and click a button to launch the Details window. The Details window needs a PersonViewModel passed to it to provide the DataContext.
The two methods that I've found:
Pass in the child window as a dependency and use property injection to provide the child window dependency before calling ShowDialog.
Pass in a ViewFactory which is used to resolve the child window and use property injection to provide the child window dependency before calling ShowDialog.
What I'm looking for is a generally accepted testable pattern. All the examples of DI that I find seem to be only one layer deep or don't require a dependency selected in the parent window.
Thanks!
I'd suggest a different method. The way that I'd recommend doing this is to create a small manager object for displaying the dialog window, through which you can pass parameters to the dialog window's view-model before it's used to create the window itself.
Create factories for your window and view-model, and use them something like this:
public class DetailsWindowDisplayer : IDetailsWindowDisplayer
{
private readonly IDetailsWindowFactory windowFactory;
private readonly IDetailsWindowViewModelFactory viewModelFactory;
public DetailsWindowDisplayer(
IDetailsWindowFactory windowFactory,
IDetailsWindowViewModelFactory viewModelFactory)
{
this.windowFactory = windowFactory;
this.viewModelFactory = viewModelFactory;
}
public void ShowDetails(IPersonViewModel personViewModel)
{
var windowViewModel = this.viewModelFactory.Create(personViewModel);
var window = this.windowFactory.Create(windowViewModel);
window.Show();
}
}
Then it's a simple case of injecting IDetailsWindowDisplayer into the parent view-model, and calling the ShowDetails method. This abstracts the showing of the dialog to a single call, and the DetailsWindowDisplayer has only a single responsibility.
To make it completely testable, your window factory should return an interface. You can do this with the following hierarchy:
public interface IDialogWindow
{
void Show();
}
public interface IDetailsWindow : IDialogWindow
{
}
public class DetailsWindow : Window, IDetailsWindow
{
// The Show() method from IDialogWindow is automatically implemented
// by the WPF Window class
}
If you prefer to keep your details window as a singleton, you can inject the window into the constructor of DetailsWindowDisplayer and either 1. set a new view-model on it each time it's shown, or 2. make its view-model a singleton too, put a ViewModel property on to the window and a Person property on to the view-model, and then set Person to be the new PersonViewModel instance each time.
In general I tend not to use the container for resolution of the Views.
There are several projects which are specialized for making View-ViewModel bindings in a very good way.
I suggest that you checkout on Caliburn.Micro, for the UI stuff and use the container for all othere dependency injection. If you need any help with setting up Caliburn.Micro with Windsor, please let me know.

Getting access to StatusBar from pages in navigation workflow

I have a WPF application that provides navigation between few pages. The MainWindow is the Window object contains a Frame object. I then have few Page objects. I need to implement a StatusBar where some text will be updated (in a TextBlock) based on what action user has taken on a particular page.
Should my StatusBar be declared in the MainWindow or there is any better place for it?
How I will be able to access that TextBlock in StatusBar from various Pages?
What usually works for me is either pub-sub or dependency injection:
At first you might give your statusbar its own viewmodel. This would be composed into the shell view of your application, probably your MainWindow. I usually have a shell viewmodel comprising a toolbar or ribbon, a statusbar and, taking the remaining space, an IShellContent container. So, to answer your first question, I would declare it in its own view, give it its own viewmodel and compose it into your MainWindow.
The second problem can be solved in different ways:
Either give your statusbar viewmodel an interface, e.g. IStatusBar, and configure your dependency injection container to bind the viewmodel as singleton. Every viewmodel that needs to output status messages could use it via constructor injection, like this:
public MyViewModel(IStatusBar statusBar)
{
this.statusBar = statusBar;
statusBar.ShowMessage("Creating new MyViewModel...");
}
Or you could use a message bus infrastructure that comes with many MVVM frameworks today. Your statusbar viewmodel would subscribe a StatusMessage, and whenever something needs to post a status message it would create a new StatusMessage and publish it, like this:
public MyViewModel(IMessageBus bus)
{
this.bus = bus;
bus.Publish(new StatusMessage("Text"));
}
I would go for the first solution (dependency injection) because it is easier testable.

Categories