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.
Related
I am totally new to WPF. I'm using DevExpress mvvm framework. I want to show a window from a viewmodel.
The code I am using is
public void NewEntity()
{
var factory = ViewModelSource.Factory((RemoteTable<AddressLocatorView> aRemoteTable) => new CreateEntityWizardViewModel(aRemoteTable));
CreateEntityWizardViewModel aVM = factory(fDataModule.DataAdapter.GetTable<AddressLocatorView>());
DialogService.ShowDialog(dialogCommands: null, title: "New Entity Wizard", viewModel: aVM);
}
This will open a form and set the viewmodel.
Though, in the form's constructor, I have
public CreateEntityWizard()
{
InitializeComponent();
}
Which, in turn, calls the public parameterless constructo of the EntityWizardViewModel. The EntityWizardViewModel is created twice, once by the factory method, and second by the InitializeComponent() which, I believe, does it by the means of the XAML :
DataContext="{dxmvvm:ViewModelSource Type={x:Type ViewModels:CreateEntityWizardViewModel}}"
What is the approach to pass a ViewModel to a form using the DialogService ?
Thank you
When you pass your view model to the DialogService.ShowDialog method, this view model is automatically used as data context for the associated view (your dialog content). Therefore, the best way to avoid duplicate view model creation is to remove the DataContext setter in xaml. To let the designer know your data context type, use the d:DataContext property:
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
d:DataContext="{dxmvvm:ViewModelSource ViewModel:RegistrationViewModel}"
This approach is used for RegistrationViewModel in the following example on the DevExpress website: https://www.devexpress.com/Support/Center/e/T145641.aspx
I am doing some data presentation using CM and WPF, and some of the data tabs have very similar formats but have to be kept in separate VM containing tabs as part of the standard for the application.
My initial thoughts were that I could do this programmatically in the VM by looking for any property pertaining to Views on the VM object (which itself is a Screen object derivation.) Its direct superclass is used as a contract for [ImportMany] so that the parent VM and View can tabulate the collection.
[ImportingConstructor]
public PartiesMasterPartiesViewModel(
IEventAggregator events,
IHelpService help,
ResourceManager<B_Action> actionResource,
IActionService actionService)
: base( events, help, actionResource, actionService)
{
}
protected override void OnActivate()
{
base.OnActivate();
this.Views.Add(new KeyValuePair<object, object>(this,
new PartiesMasterListView()));
}
So either I am not using this property correctly, or it does not do what I thought it does and I need to use another way.
Another way I'm thinking of doing it this explicitly instantiating multiple instances of the same viewmodel and manually adding them to the collection, but this seems like it would be violating what MEF's [ImportMany] would be here to do and weaken the design of the application.
The simplest way to achieve a view shared by multiple view models is to configure the ViewLocator with some extra rules.
In this example I have two view models Examples.ViewModels.SharedData1ViewModel and Examples.ViewModels.SharedData1ViewModel and a single view Examples.Views.SharedDataView that I'd like to be the view that Caliburn.Micro locates for both by default.
In my set up code I can add the following simple regular expression to the ViewLocator.
ViewLocator.NameTransformer.AddRule(
#"^Examples.ViewModels\.SharedData(\d+)ViewModel",
#"Examples.Views.SharedDataView");
I am aware there are a couple of questions similar to this one, however I have not quite been able to find a definitive answer. I'm trying to dive in with MVVM, and keep things as pure as possible, but not sure how exactly to go about launching/closing windows while sticking to the pattern.
My original thinking was data bound commands to the ViewModel triggering code to start a new View, with the View's DataContext then set to it's ViewModel via XAML. But this violates pure MVVM I think...
After some googling/reading answers I came across the concept of a WindowManager (like in CaliburnMicro), now if I was to implement one of these in a vanilla MVVM project, does this go in with my ViewModels? or just in the core of my application? I'm currently separating out my project into a Model assembly/project, ViewModel assembly/project and View assembly/project. Should this go into a different, "Core" assembly?
Which leads on a bit to my next question (relates somewhat to the above), how do I launch my application from an MVVM point of view? Initially I would launch my MainView.xaml from App.xaml, and the DataContext in the XAML would attach the assigned ViewModel. If I add a WindowManager, is this the first thing that is launched by my Application? Do I do this from the code behind of App.xaml.cs?
Well it mainly depends on how your application looks like (i.e. how many windows opened at the same time, modal windows or not...etc).
A general recommendation I would give is to not try to do "pure" MVVM ; I often read things like "there should be ZERO code-behind"...etc., I disagree.
I'm currently separating out my project into a Model assembly/project,
ViewModel assembly/project and View assembly/project. Should this go
into a different, "Core" assembly?
Separating views and ViewModels into different assemblies is the best thing you can do to ensure you won't ever reference something related to the views in your viewModel. You'll be fine with this strong separation.
Separating Model from ViewModel using two different assemblies could be a good idea too, but it depends on what your model looks like. I personally like 3-tier architectures, so generally my model is the WCF client proxies and are indeed stored in their own assembly.
A "Core" assembly is always a good idea anyway (IMHO), but only to expose basic utility methods that can be used in all the layers of your application (such as basic extension methods....etc.).
Now for your questions about views (how to show them...etc), I would say do simple. Personally I like instantiating my ViewModels in the code-behind of my Views. I also often use events in my ViewModels so the associated view is notified it should open another view for example.
For example, the scenario you have a MainWindow that should shows a child window when the user click on a button:
// Main viewModel
public MainViewModel : ViewModelBase
{
...
// EventArgs<T> inherits from EventArgs and contains a EventArgsData property containing the T instance
public event EventHandler<EventArgs<MyPopupViewModel>> ConfirmationRequested;
...
// Called when ICommand is executed thanks to RelayCommands
public void DoSomething()
{
if (this.ConfirmationRequested != null)
{
var vm = new MyPopupViewModel
{
// Initializes property of "child" viewmodel depending
// on the current viewModel state
};
this.ConfirmationRequested(this, new EventArgs<MyPopupViewModel>(vm));
}
}
}
...
// Main View
public partial class MainWindow : Window
{
public public MainWindow()
{
this.InitializeComponent();
// Instantiates the viewModel here
this.ViewModel = new MainViewModel();
// Attaches event handlers
this.ViewModel.ConfirmationRequested += (sender, e) =>
{
// Shows the child Window here
// Pass the viewModel in the constructor of the Window
var myPopup = new PopupWindow(e.EventArgsData);
myPopup.Show();
};
}
public MainViewModel ViewModel { get; private set; }
}
// App.xaml, starts MainWindow by setting the StartupUri
<Application x:Class="XXX.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
...
StartupUri="Views/MainWindow.xaml">
I am currently trying to design an application that loads viewmodels through MEF imports.
So far so good, I navigate from viewmodel to viewmodel, having loaded each vm datatemplate through dictionaries.
Each time I navigate, I modify the content of the main contentPresenter in my Shell (MainWindow).
One of the viewmodel allows me to display a WindowFormHost for an activeX control (such as acrobat reader for example). Since WindowFormHost does not allow binding, I created the windowFormHost in the viewmodel and binded it to a ContentPresenter in the view.
And here is where it fails : when coming back to the same viewmodel, the view is created again... throwing a “Element is already the child of another element.” error.
How can I prevent that ? Should I unload WindowFormHost when view is reloaded ? Or Can I keep view instances so that I keep only one instance for each view and let data binding update controls ? (It looks better for memory consumption).
Thanks for your help !
[EDIT]
Loaded dictionary :
<DataTemplate x:Shared="False" DataType="{x:Type vm:DAVPDC3DVIAControlViewModel}">
<vw:MyUserControl />
</DataTemplate>
View :
<DockPanel>
<ContentControl Name="WFH3DVia" Content="{Binding Path=Control3DVIA, Mode=OneWay} </ContentControl>"
<!--<WindowsFormsHost Name="WFH3DVia"></WindowsFormsHost>-->
</DockPanel>
VM (singleton, mef module) :
[Export(typeof(IDAVPDC3DVIAControl))]
public partial class DAVPDC3DVIAControlViewModel : ViewModelBase, IViewModel, IPartImportsSatisfiedNotification
VM (main window)
[Export]
public class MainWindowViewModel : ViewModelBase, IPartImportsSatisfiedNotification
// CurrentUC binds main widow view to controller active viewmodel
public IViewModel CurrentUC
{
get
{
return myAddinManager.CurrentVM;
}
}
Main view :
Controler (displays module on event) :
private void ModuleReadyEventAction(string iModuleName)
{
if (null != this.Modules && this.Modules.Count() > 0)
{
foreach (var item in Modules)
{
IBaseModule ibasemodule = item as IBaseModule;
if (null != ibasemodule)
{
Type tp = ibasemodule.GetType();
if (0 == tp.Name.CompareTo(iModuleName))
{
CurrentVM = ibasemodule.GetViewModel();
break;
}
}
}
}
}
I'm also working on a project in WPF using Prism v4 and MVVM (except I'm using Unity). I also have at least two controls that I need to use which are Windows Forms controls that must be hosted in a WindowsFormsHost. Let me explain my thoughts on the process..
It seems to me, that you are trying to avoid any code in your View's code behind. That's the only reason I can think of that you are moving your WindowsFormsHost into your ViewModel. I think that this is fundamentally the wrong approach. The WindowsFormsHost exists for the reason of displaying a graphical Windows Forms control. Therefore, it belongs in the view!
Now, I understand the appeal of DataBindings. Trust me, I've wanted to able to DataBind many parts of my WindowForms control. Of course, to accept a WPF data binding the property must be a dependency property on a dependency object. The easiest solution, which is not unreasonable, is to simply add the code to configure your windows forms control in the code behind for your view. Adding your UI logic into your ViewModel is an actual violation of the MVVM design pattern, while adding code behind is not. (And in some cases is the best approach)
I've seen possible hacks to try to get around this limitation. Including using "proxies" which inject a databinding, or perhaps extending WindowsFormsHost and adding DependencyProperties which wrap a specific hosted control's properties, or writing classes using reflection and trying to throw in windows forms bindings. However, nothing I've seen can solve the problem completely. For example, my windows forms control can contain other graphical components, and those components would need to support binding as well.
The simplest approach is to simply synchronize your view with your viewmodel in your view's code behind. Your view model can keep the file or document that is open, filename, title, etc., but leave the display and display related controls up to the View.
Last, let me comment more directly on your question. I would need to see how you are registering your View and ViewModel with the MEF Container and how you are navigating to understand why you are receiving that error. It would seem to me that either your view or view model is getting created more than once, while the other is not. Are these registered as singleton types? Regardless, I stand by what I said about not including the WindowsFormsHost in your ViewModel.
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