We are using Silverlight MVVM pattern in our application. In the application there is a mainpage (which does not change) and there are child pages, these child pages changes depending on the operation performed by the user. Untill now I have been using code-behind for navigation between different child pages, the code goes like this :
ChildPage2 obj = new ChildPage2 ();
Dialog_Box.Children.Clear();
Dialog_Box.Visibility = Visibility.Visible;
Dialog_Box.Children.Add(obj );
But as I am using MVVM pattern I want to do the same using my ViewModel. Is there a way to do the same (Navigation) using ViewModels.
Please help, thanks in advance.
Vaibhav
Couple of basic rules:
ViewModels should not know about how they are displayed. They are purely glue between views and real data objects & business logic.
Views only know how to display data with a certain shape. They should not know where the data is coming from (the exception that breaks this rule is using DomainDataSources... but that's another story).
Look at the navigation features available in Silverlight (try creating a sample Business application in Visual Studio). Your views are then created when hyperlinks are clicked based on configured mappings.
The alternative (to do it in code) is to introduce controllers into MVVM. This maintains the separation of concerns between views, viewmodels and data, but adds a level of complexity I usually reserve for PRISM-based apps. Best you try the hyperlink/url mapping option.
The way I solved this before was to have properties in the ViewModel that each child page would bind to:
public class YourViewModel : INotifyPropertyChanged
{
public Visibility FooVisibility { get { /* ... */ } }
public Visibility BarVisibility { get { /* ... */ } }
}
Related
I'm trying to learn MVVM Pattern doing a simple GUI using WPF . It's just a simple plugin for Autocad with a modal window showing some information about the model.
I have two Usercontrols with their respective views and viewmodels. I'm showing these usercontrols in a single windows/dialog. One of these usercontrols needs to show some information, representing a simple List (the model) in a DataGrid.
Like I said before, the model for that user control is a existing List. I need to pass the List information to the viewmodel but I'm struggling with this. I was thinking on passing it as a parameter to the MainWindow contructor and then to the respective viewmodel but doesn't sound like a good idea.
What options do I have in this case?
I'm using MVVM Toolkit.
Thanks!
You could use the WeakReferenceMessenger/StrongReferenceMessenger to send a message from one view model to another:
// Create a message
public class LoggedInUserChangedMessage : ValueChangedMessage<User>
{
public LoggedInUserChangedMessage(User user) : base(user)
{
}
}
// Register a message in some module
WeakReferenceMessenger.Default.Register<LoggedInUserChangedMessage>(this, (r, m) =>
{
// Handle the message here, with r being the recipient and m being the
// input message. Using the recipient passed as input makes it so that
// the lambda expression doesn't capture "this", improving performance.
});
// Send a message from some other module
WeakReferenceMessenger.Default.Send(new LoggedInUserChangedMessage(user));
Please refer to the docs for more information.
I want to create an application where the user navigates through pages that are placed inside a frame element. The problem is that one page can have different layouts which basically provide the same functionality. There can be a few buttons or input controls more or less per layout, but they all should share the same code behind file.
In Windows Forms, I used to place all elements (the layout) on the same form and then hide/show the controls I required, but that's a very ugly solution and I was hoping that WPF provided something more convenient here.
I tried to create 2 Pages, deleted their respective .cs files and set their "x:Class" attribute to a custom .cs file, but that results in compiler errors (ambiguous calls to InitializeComponent() ).
So can I have multiple pages that share the same code?
You should move the logic from the code-behind class to a view model class. This pattern is known as Model-View-ViewModel and is the recommended design pattern to use when developing XAML based user interface applications.
There are plenty of online tutorials about it and this one should provide a good starting point for you: https://msdn.microsoft.com/en-us/library/hh848246.aspx.
Once you have understood the pattern and implemented your application logic in a view model class, you could then simply set the DataContext property of both pages to the same view model:
public Page1()
{
InitializeComponent();
DataContext = new ViewModel();
}
A code-behind class is simply a partial class, i.e. it is partial definition of the Page that you define in XAML and therefore you cannot "share" this one between several different pages.
Partial classes are just a way of splitting the definition of a class across several different source files: https://learn.microsoft.com/en-us/dotnet/articles/csharp/programming-guide/classes-and-structs/partial-classes-and-methods.
I'm a fresher with WPF, MVVM. Now, I have been investigating MVVM Light. I got some confused. I appreciate any help from all of you. Thanks in advance for taking a look at this post.
Can we absolutely remove code behind of View (such as: Invoking InitializeComponent() somewhere inside .xaml --> Does not need view.xaml.cs anymore in some simple case).
I have many views insight my project, how many locator is necessary?
I intend to make separate locator for each view. And I wonder that if I register all locators in app.xml, are all of views initialized and registered right after user run the application? If Yes, Is it not good for performance?
Main question:
I have a scenario:
I have many forms: such as :
MainForm: which is the 1st form invoked by application.
ImportForm: which is invoked when User click Import (from MainForm)
Assumption that: I did finish all stuff related to binding (such as button Import --> RelayCommand(OnImport))
What is the best practice for me to implement this scenario?
1. Just implement to init and show ImportForm like below:
public void OnImport()
{
ImportForm importForm = new ImportForm();
importForm.ShowDialog();
}
It's simple, but I wonder if this way follow the MVVM's paradigm?
So, I did some researching and do another way like:
public void OnImport()
{
//// Just send message
Messenger.Default.Send(
new NotificationMessage(this, "OnImport"));
}
In Code Behind: MainForm.xaml.cs
public MainForm()
{
InitializeComponent();
Messenger.Default.Register<NotificationMessage>(this, (nm) =>
{
if (nm.Sender == this.DataContext)
{
if (nm.Notification == "OnImport")
{
ImportForm importForm = new ImportForm();
importForm.ShowDialog();
}
}
});
}
By this way, I must write some code inside code behind --> Is it a problem?
Both above solutions can finish mentioned scenario correctly, but I confused which one is better or is there any actually right solution out there?
Thanks again for your patience with the long question.
No, you cannot. InitializeComponent() paints UI on the screen. The purpose of MVVM is to separate logic that does not related to View and store it in a ViewModel. It does not tend or aim to remove code-behind.
It depends on you. You can create one Locator for all ViewModels or one Locator per one ViewModel. Anyway, I found that Locator does not scale well and hard to manage in a larger project. It creates dependency between View, Locators and ViewModels. I personal prefer to use DI framework instead of Locator even if it is a small project.
You can do both, depends on your requirement. (a) If clicking the button on the main form does nothing more than show a dialog then I would use Click event because it is View related. It has nothing to do with any logic, so keep it in the code behind is the best solution for me. (b) By the way, if clicking the button does something, for example, connect to a database then show a dialog if a condition is true. In this case, I would use Messenger to keep View and ViewModel separate from each other.
I'm still starting out with MVVM and read through all the MSDN Prism examples but I'd nonetheless like some guidance with the following design problem which I feel will help me latch onto the Prism concepts and reinforce what I just learnt
I have a multi-window application - each window has a filter drop down control. The value of this filter control will affect the display of other controls within that same window, eg grids, charts etc.
Certain interactions (eg double clicking a row on a grid) will spawn another window which will have its own separate filter control, which will similarly affect the display of other controls only within that window
How do I implement this behavior of the filter driving the display of other user controls it has no idea of and have it restrict its behavior to only the window hosting it? How do I keep these interactions loosely coupled?
I'm thinking I need to use the EventAggregator and for the filter control to publish an update event when the selection changes? Am I thinking about this the right way? Would each window need a separate EventAggregator?
Yes, you are thinking about this the right way. The EventAggregator is a good tool for what you're doing. You'll need to have the EventAggregator on every window you plan to raise an event from. You can inject the EA into your constructor or use the ServiceLocator. Here are 2 examples:
// Ctor injection
private IEventAggregator _eventAggregator;
public ViewModelBase(IEventAggregator eventAggregator)
{
_eventAggregator = eventAggregator;
}
// Service Locator
ServiceLocator.Current.GetInstance<IEventAggregator>().GetEvent<YourEvent>().Publish();
Now, you'll need to create a CompositePresentationEvent for the EA to publish. You can minimize the number of these that get created by including a payload in the CPE. Something like this:
public class NavigationSelectedEvent : CompositePresentationEvent<NavigationEnum.Destination>{}
So now you're ready to publish the event:
_eventAggregator.GetEvent<NavigationSelectedEvent>().Publish(NavigationEnum.Destination.Home);
And then subscribe to it - using an optional filter on the payload so you're not wasting resources:
this.EventAggregator.GetEvent<NavigationSelectedEvent>().Subscribe(HandleNavigationEvent, ThreadOption.UIThread, false, i => i == NavigationEnum.Destination.Home);
You should publish the filter notification using the EventAggregator, that is the best way to do it from my experience. You should be using only one EventAggregator to serve as a hub between all the subscribing objects.
Something like:
MyNotificationChangedArgs args = new MyNotificationChangedArgs();
args.Payload = GetThePayload(someParameters);
myEventAggregator.GetEvent<NotificationFilterChangedEvent>().Publish(args);
Another thing that really helps is to dependency-inject the EventAggregator, for instance via Unity. This way all of your controls can simply access the common EventAggregator by calling on the UnityContainer.Resolve method:
var myEventAggregator = myUnityContainer.Resolve<MyEventAggregator>();
Every View has access to the Model through the ViewModel. Model your filters. Then the Views bind to ViewModel representations that use the filters. Your Views don't have to know about each other, they just have to bind to the Model.
Hopefully quite a simple one, having my first try at WPF with Prism V2 using M-V-VM and so far finding everything pretty awsome. My Shell is pretty simple, Ribbon Control at the Top, DataGrid of Help desk tickets on the left, and a TabControl on the right.
When a user opens the selected ticket from the datagrid, I want the Ticket to open as a Tab on the Tab Control. I know in order to do that I need to add and then activate the View to the region using the RegionManager. But doing this from the ViewModel doesn't seem correct to me, although I could do it using DI (DepenecyInjection) it still rings alarms in my head about giving the ViewModel some knowledge about a View.
To Add to this, different modules will also be adding other views (Contact, Client etc) into the TabControl, I'd like to use DataTemplates to get the TabControl to display the View Correctly, can anyone give me any pointers for this as well.
Many Thanks
Ben
Full answers please, not just links. It's what StackOverflow is for!
MVVM + Services = Ultimate Power!
A service is just an interface that's well known and is registered in your IOC container. When the ViewModel needs to do something outside of itself, like say open a tabbed document, it uses the service. Then the service is implemented as needed for the particular program.
For example:
public interface IDocumentService
{
void OpenDocument(IViewModel viewModel);
}
internal class DocumentService:IDocumentService
{
public void OpenDocument(IViewModel viewModel)
{
// Implement code to select the View for the ViewModel,
// and add it to your TabControl.
}
}
{
// Somewhere in your ViewModel...
// Make sure you can get the IDocumentService
IDocumentService docService = ioc.Get<IDocumentService>();
docService.OpenDocument(new TicketViewModel());
}
Commands are the way to do this - you'll send a command to yourself, called "RequestBringTicketIntoView"; it will bubble up to the Window, where you handle it. Read Josh Smith's article:
http://joshsmithonwpf.wordpress.com/2008/03/18/understanding-routed-commands/