Change selected tab from ViewModel And send objects between ViewModels - c#

I got a wpf desktop application with 3 ViewModels.
I have 1 ViewModel that contains a tabhost and tabs. And I got 2 tabs. Each of those tabs has it's own ViewModel.
The problem I have is that in tab1 I have a listview with searchresults and a button. When I select one item in that list and press the button, I want to change tab and display information about that item I selected in tab2.
I have searched for a solution, but it seems to include creating all ViewModels in the MainViewModel and providing a reference of the MainViewModel to all the subViewModels.
Is there no other way?
EDIT
I just managed to solve my problems with MVVM light that I added to the project.
By binding a method in the MainViewModel and a property in it to the .xaml I can now call it from tab1 with info to tell it to change tab.
Also, by binding a method in tab2 I can now send over an item from tab1 in the same manner.
This is how I solved it after importing MVVM light into the project.
In tab1
Messenger.Default.Send<string, tab2ViewModel>(--object to send to tab2--);
Messenger.Default.Send<int, MainViewModel>(--tab index--);
In Main/tab2
Messenger.Default.Register<int>(this, ChangeTab);
public void ChangeTab(int i)
{
SelectedTabIndex = i; //Bound property in .xaml
}
It seems to automagically just work..
P.s. Thanx for reply. I shall look into how Prism work as well, and see if there is any advantages to use that instead of MVVM light(Not right now however).

A good solution to your problem would be to use the EventAggregator of Prism.
This service lets you listen to and publish a particular type of event from anywhere in your application without tying your view-models to each other. This is a really nice solution for MVVM and other losely-coupled designs. It is easy and works like a charm.
You can find the documentation here : EventAggregator
More information about Prism here : Prism on Codeplex
Here is a concrete sample. Imagine you want to change the title of the main Window from anywhere in the application. You would create this event:
public class TitleChangedEvent : CompositePresentationEvent<string>{}
Then you would suscribe to this event in your MainWindowViewModel like this:
eventAggregator.GetEvent<TitleChangedEvent.Suscribe(
(newTitle) =>
{
this.WindowTitle = newTitle;
});
And finally you can update the title from anywhere in your application like this:
eventAggregator.GetEvent<TitleChangedEvent>().Publish("This is a new title!");
Quite easy as you can see.
Smililarly with MVVM Light you would first create a message for notification:
public class TitleChangedMessage : GenericMessage<string>{}
Then you would listen to the message like this in your MainWindowViewModel:
Messenger.Register<TitleChangedMessage>(this,
(message) =>
{
this.WindowTitle = message.Content;
}
And you would send an update like this:
Messenger.Send<TitleChangedMessage>(this, new TitleChangedMessage("This is a new title!"));
This is the MVVM Light way to do it :)
You can also have a look at this related question: Messenger class in MVVM Light

Related

Opening a second Window from MainWindow following MVVM and loose coupling

At first: This App and Question is for learning purpose
I'm on a new application and facing the problem that I want to open a Window when the user clicks on a Button in the MainView. In the past I'd have designed a Command which just creates the new Window and displays it
new RelayCommand((x)=>new SecondWindow().Show());
Now with this new Project I'm trying to fully decouple all classes from each other. To achieve this my App consists of 4 Assemblies (BL, COM, DAL and UI).
As in each WPF Application, the App starts with the MainWindow.xaml. The MainWindow.cs will create it's instance of MainWindowViewModel:
public ViewModel VM {get; private set;}
public class MainWindow(){
VM = new ViewModel();
InitializeComponent();
}
(which already violates loose coupling) (Any tips on how to make it better?)
My last attempt is to create an instance of my second Window inside my main window
<Window.Resources>
<local:SecondWindow x:Key="sw"/>
</Window.Resources>
and pass it as a CommandParameter to my Command
CommandParameter="{StaticResource sw}"/>
new RelayCommand((x)=> ((Window)x).Show());
This solution works but has one big disadvantage - the second window get's created immediately after the app starts - and so does it's ViewModel which starts some heavy processes (DB Connections etc.)
I've heard something abour IoC principle but I really don't know how to use it with an wpf application.
You are thinking along the right lines.... you basically have to create a List of ViewModels as your application starts up, then you can switch between them as the user presses buttons and pass the name of the ViewModel as a CommandParameter to your Command handler....
You might find this link to Rachel Lim's Blog
https://rachel53461.wordpress.com/2011/12/18/navigation-with-mvvm-2/
Also, I'm not going to post any code here coz it simply gets too complicated. So here is a download to just about the simplest example I could come up with
http://www.mediafire.com/download/3bubiq7s6xw7i73/Navigation1.rar
Download and un-RAR it (with win RAR) You will need to step though the code, figure out what its doing and how its doing it then modify it to suit your needs... Or modify your needs to suit the code.....
The example is a modification of Rachel Lim example. It simply contains Views and ViewModels, there are no Models or data. It demonstrates switching between two different Views.
UPDATE 1
With specific reference to the demo code.... Your VMs are added to a static collection of VMs (see AddViewModel function), each View ( the DataTemplate associates View with ViewModel) is selected when you click a button for example, by calling 'SelectViewCommand' which in turn sets Current_ViewModel to the selected ViewModel... the corrisponding ContentControl is then updated to display that currently selected View...
I know is confusing and very difficult to explain
When you press a button to 'change Views' you are actually changing the value of the property that your ContentControl is bound to, so you have to call the correct SelectViewCommand in the SAME instance of the class that your ContentControl is bound too...
In the demo you'll see that in the 'LogOn_View' I call
Command="{Binding DataContext.SelectViewCommand, ElementName=Base_V}"CommandParameter="Main_ViewModel"
Here I am calling the SelectViewCommand in the Base_ViewModel (x:Name="Base_V" in Base_View XAML), That's because I want to change the View that is displayed in the Base_View's 'ContentControl'
In Main_View I call
Command="{Binding SelectViewCommand}" CommandParameter="MainV1_ViewModel"
Here I am calling the SelectViewCommand in the Main_ViewModel, That's because I want to change the View displayed in the MainView's 'ContentControl'....
I typically create a WindowService class for managing window changes/dialogs in MVVM. Having "View" code in the ViewModel (i.e. Window.Show()) goes against MVVM principles. For example:
public class WindowService : IWindowService
{
public void ShowDialog<T>(ViewModelBase viewModel) where T : IApplicationDialog
{
IApplicationDialog dialog = (IApplicationDialog)Activator.CreateInstance(typeof(T));
dialog.Show();
}
}
And then your call from the ViewModel would look something like:
windowService.ShowDialog<SecondWindow>(new SecondWindowViewModel());
If you're using DI, you can pass a reference to the IoC container to the window service and create the window instances from that rather than using Activator.CreateInstance (i prefer the DI approach personally)

Caliburn.micro and devexpress: insert tabs (dockpanel) from viewmodel when user clicks

Cheers all,
I'm searching to use the devexpress tool in my visual c# project (vs2013). I'm also using caliburn.micro and i've inserted the Caliburn.Micro.DevExpress reference. So, this is my environment.
The problem: I want create a container, where you can open some predefined tabs. You can navigate in the open tabs, close them and open others.
I'm in stuck with the integration of devexpress and caliburn. Without caliburn and mostly without MVVM pattern, it's easy. But how can I do this using my viewModel?
The result I want is like the example "Simple MDI" in [the official caliburn.micro documentation1]. Instead of the button "open tab", I've my menu in the upper side and, depending on which button is clicked, I want open the relative dockpanel/tab.
Now, in the xaml file, my container is a devexpress object, the "DocumentGroup". My goal is to add dynamically the DocumentPanel(s), like written above.
Is it clear the problem? Some ideas for solution?
UPDATE: in my viewModel I have:
namespace **.ViewModels
{
class MainWindowViewModel : Conductor<IScreen>.Collection.OneActive
{
public void addT() {
Debug.WriteLine("start");
ActivateItem(new ucImpiantiViewModel());
Debug.WriteLine("end");
}
}
}
And in the xaml I have:
...
<dxd:DocumentGroup x:Name="Items" ItemHeight="3*" SelectedTabIndex="0">
...
I thought this way was good, but still nothing.. the good new is only that the console.writeline are working!

What is the recommended way to open a new window using MVVM in WPF

Hello everyone and thanks in advance for your time.
I'm currently learning MVVM using WPF for a small Biz App that I'm writing. I have read a lot of articles about the MVVM pattern and found that one of the key areas is to decouple the ViewModel from the View as much as possible.
I want to open a new Window in my app but I'm not sure if I should open it from the ViewModel using an ICommand or directly from the view using a standard event. Someone I work with suggested that that I should use commands, but then I thought that this would mean having a reference to a View in my ViewModel, which according to what I understand is precisely what the MVVM pattern focuses on avoiding.
My understanding is that if a window will open for navigation purposes only and the process of opening that new windows has no effect on the Model, then I should keep all of this on the view using standard events.
I know in sw development everything "depends", but guess my question is there a "right"/standard way of doing this?
Best regards,
Daniel
Yes, VMs should communicate with Views utilizing the Events that Views can subscribe to...
In VM:
public event EventHandler<NotificationEventArgs<string>> DisplayOptionsNotice;
In View:
private readonly MainViewModel mvm;
...
mvm = DataContext as MainViewModel;
mvm.DisplayOptionsNotice += DisplayOptionsWindow;
...
private void DisplayOptionsWindow(object sender, NotificationEventArgs<string> e)
{
...
optionsWindow = new OptionsWindow { Owner = this };
optionsWindow.ShowDialog();
...
}
but then I thought that this would mean having a reference to a View in my ViewModel, which according to what I understand is precisely what the MVVM pattern focuses on avoiding.
In general, the way this is handled is via some form of inversion of control. Most MVVM frameworks will provide a service of some form to open a window, and use a Service Locator or Dependency Injection to provide the service to the ViewModel.
This allows your ViewModel to stay decoupled from the specific view rendering framework - you'd pass the service the new VM and say "Show this VM in a window", and that code would be platform specific.
As Reed said Service locator or DI will do the work and wont breake the MVVM pattern.
from my experience you will have to do three things:
First check about the Service Locator or Di see what more friendly for you and implement it.
Second start making the interface of IWindow\IWindowDialog that your view (windows\ Messagebox - if you like) will implement for example.
the last thing is to implement the windows\ messages.
it will take time to do it from scratch (I did it) but if you will focus one thing at a time.
you cut the time in half.
Good luck

Organizing Commands and ViewModels in MVVM

Currently I have 2 ViewModels in my project. MainViewModel is currently bound to my WPF window, so I have a command in my other ViewModel (AdventurerViewModel) that doesn't fire anymore. Is there a way to have that command fire without the ViewModel being bound or do all of my commands need to be in MainViewModel? And is there a good way to organize commands that might help me avoid this situation in the future? Any advice would be greatly appreciated.
EDIT: A bit of code
From my XAML
Button Grid.Column="3" Grid.Row="1" Content="IncreaseLevel" Command="{Binding IncreaseLevel}"
AdventurerViewModel.cs
public ICommand IncreaseLevel { get { return new RelayCommand(IncreaseLevelExecute, CanIncreaseLevelExecute); } }
private bool CanIncreaseLevelExecute()
{
return true;
}
private void IncreaseLevelExecute()
{
Level++;
}
First make sure you split the ViewModels correctly and have the commands in the correct ViewModels.
After that it might still be possible that you want to notify other ViewModels of changes in a current ViewModel. In that case you could have a look at a messaging system between ViewModels so a ViewModel can subscribe to a specific event and respond to it.
Use one ViewModel per View. If you use a Window then you should use one ViewModel. Right now in my project a have a Window which has 2 user controls in it, and each UserControl has its own ViewModel plus the ViewModel for the Window. I could put everything in one ViewModel but it is cleaner this way, but for your example I recomend you put everything in one ViewModel. It realy depends on situations or project requirements.

Having trouble deciding how to wire up a UserControl with MVVM

I've been doing the best I can to try to stay true to the separation recommended by the MVVM pattern. One thing I haven't figure out how to do correctly has to do with initializing my UserControls.
My most recent example of this has to do with a library that I wrote to talk to some low-level hardware. That assembly happens to have a UserControl that I can simply drop into any GUI that uses this hardware. All that is necessary for it to work is to set a reference to the object that has access to the low level methods.
However, that's where my problem lies -- currently, the UserControl is added to the GUI via XAML, where I define the namespace and then add the UserControl to my window. Of course, I have no control over its creation at this point, so the default constructor gets called. The only way to set the necessary reference for hardware control involves calling a method in the UC to do so. The ViewModel could feasibly call a method in the Model, e.g. GetController(), and then call the method in the UserControl to set the reference accordingly. The GUI can pass a reference to the UserControl to the ViewModel when said GUI creates the ViewModel, but this violates MVVM because the ViewModel shouldn't know anything about this control.
Another way I could deal with this is to not create the UserControl in XAML, but instead do it all from code-behind. After the ViewModel gets initialized and retrieves an initialized UserControl (i.e. one that has the low-level object reference set), it can set the Content of my Window to the UserControl. However, this also violates MVVM -- is there a way to databind the Content of a Window, TabControl, or any other element to a UserControl?
I'd like to hear if anyone has had to deal with this before, and if they approached it the first or second way I have outlined here, or if they took a completely different approach. If what I have asked here is unclear, please let me know and I'll do my best to update it with more information, diagrams, etc.
UPDATE
Thanks for the responses, guys, but I must not have explained the problem very well. I already use RelayCommands within the UserControl's ViewModel to handle all of the calls to the hardware layer (Model) when the user clicks in the control in the UserControl itself. My problem is related to initially passing a reference to the UserControl so it can talk to the hardware layer.
If I create the UserControl directly in XAML, then I can't pass it this reference via a constructor because I can only use the default constructor. The solution I have in place right now does not look MVVM-compliant -- I had to name the UserControl in XAML, and then in the code-behind (i.e. for the View), I have to call a method that I had added to be able to set this reference. For example, I have a GUI UserControl that contains the diagnostics UserControl for my hardware:
partial class GUI : UserControl
{
private MainViewModel ViewModel { get; set; }
public GUI( Model.MainModel model)
{
InitializeComponent();
ViewModel = new MainViewModel( model, this.Dispatcher);
ViewModel.Initialize();
this.DataContext = ViewModel;
diagnostics_toolbar.SetViewModel( ViewModel);
user_control_in_xaml.SetHardwareConnection( model.Connection);
}
}
where the outer class is the main GUI UserControl, and user_control_in_xaml is the UserControl I had to name in the GUI's XAML.
Looking at this again, I realize that it's probably okay to go with the naming approach because it's all used within the View itself. I'm not sure about passing the model information to user_control_in_xaml, because this means that a designer would have to know to call this method if he is to redo the GUI -- I thought the idea was to hide model details from the View layer, but I'm not sure how else to do this.
You will also notice that the main GUI is passed the Model in the constructor, which I assume is equally bad. Perhaps I need to revisit the design to see if it's possible to have the ViewModel create the Model, which is what I usually do, but in this case I can't remember why I had to create it outside of the GUI.
Am new to MVVM myself but here's a possible solution:
Create a property in your VM that is of the object type (that controls the hardware) and bind it to an attached property on your UserControl. Then you could set the property in your VM using dependency injection, so it would be set when the VM is created. The way I see it, the class that talks to the hardware (hardware controller) is a service. The service can be injected to your view model and bound to your UserControl. Am not sure if this is the best way to do it and if it is strict enough to all the MVVM principles but it seems like a possible solution.
if your question is: How do i show my viewmodel in the view? then my solution is always using viewmodelfirst approach and datatemplates.
so all you have to do is wire up your viewmodel via binding to a contentcontrol.content in xaml. wpf + datatemplates will do the work and instantiate your usercontrol for your viewmodel.
You are right, the ViewModel shouldn't know about anything in the View - or even that there is such a thing as a View, hence why MVVM rocks for unit testing too as the VM couldn't care less if it is exposing itself to a View or a test framework.
As far as I can see you might have to refactor things a little if you can. To stick to the MVVM pattern you could expose an ICommand, the ICommand calls an internal VM method that goes and gets the data (or whatever) from the Model, this method then updates an ObservableCollection property of the data objects for the View to bind to. So for example, in your VM you could have
private ICommand _getDataCommand;
public ICommand GetDataCommand
{
get
{
if (this._getDataCommand == null)
{
this._getDataCommand = new RelayCommand(param => this.GetMyData(), param => true);
}
return this._getDataCommand;
}
}
private void GetMyData{
//go and get data from Model and add to the MyControls collection
}
private ObservableCollection<MyUserControls> _uc;
public ObservableCollection<MyUserControls> MyControls
{
get
{
if (this._uc == null)
{
this._uc = new ObservableCollection<MyUserControls>();
}
return this._uc;
}
}
For the RelayCommand check out Josh Smiths MSDN article.
In the View you could either call the ICommand in the static constructor of your UC - I am guessing youwould need to add an event in your class for this - or call the ICommand from some sort of click event on your UC - maybe just have a 'load' button on the WPF window. And set the databinding of your UC to be the exposed observable collection of the VM.
If you can't change your UC at all then you could derive a new class from it and override certain behaviour.
Hope that helps a bit at least, like I say, have a look at Josh Smiths MVVM article as he covers the binding and ICommand stuff in there brilliantly.
If you set the DataContext of the Window or UserControl containing thisUserControl to the main view model, the user control can call SetHardwareConnection() on itself in its Loaded event (or DataContextChanged event handler).
If that's not possible because you're saying the UserControl is 'fixed', you should derive from it or wrap it up in another UserControl, which would serve as a MVVM 'adapter'.
(In order to bind the window: you could make the MainViewModel a singleton with a static Instance property and use DataContext="{x:Static MyClass.Instance}". A nice way to get things going quickly)
Note; this is based on my understanding that MVVM works because of Bindings.. I always bind the control to a ViewModel, not pass a ViewModel as a parameter.
Hope that helps!

Categories

Resources