Handle UIElements with MVVM - wpf

Im building an app trying to follow MVVM as far as possible. Its an windows 8 store app and Im using MVVM-light. Now let say I have a MainPage using MainViewModel and I also have a UserControl using UserViewModel.
Now I want to be able to communicate between MainPage and UserControl without using any code-behind. In this simple scenario i would like to "click" a button in my UserControl from a button in MainPage
MainPage.Xaml:
Here I have a button with which I wish to click a button in the Usercontroll
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="MyButton"
Command="MyCommand"></Button>
</Grid>
MainPageViewModel:
public class MainViewModel : ViewModelBase
{
//
}
UserControll.Xaml
Here is the button I wish to click from MainPage
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="UcButton"/>
</Grid>
UserControlViewModel:
public class UserControlViewModel : ViewModelBase
{
//
}
Im interested in knowing about how you can interact with UI-Elements following the MVVM-pattern. Any links or tips appreciated.

The 'clean' (MVVM way) of doing this is by having the ViewModels interact.
So if View1 should invoke something in View2 have View1 trigger a Command on its ViewModel1. ViewModel1 should then contact ViewModel2. ViewModel2 would executed the required action and show the result through its properties. View2 will bind to the properties and show the effect of the action.
Interaction between ViewModels can be done through a publisher-subscriber model (message broker, bus, ...) In MVVM-light there is a Messenger class to keep the dependencies clean.

I can't comment since I don't have enough points, and the question seems a bit weird. Do you want to click the button so that it shows in the GUI, like the button turns blue or do you want to invoke a command in usercontrolViewModel. If it is the latter you could maybe do a fix like this:
MainPage.xaml
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="MyButton"
Command="{Binding UserControlViewModel.SomeUserControlCommand, Source={StaticResource Locator}}"></Button>
</Grid>
Remember to check what name you gave UserControlViewModel in the ViewModelLocator and you will of course have to create the command: SomeUserControlCommand in UserControlViewModel.

Related

Prism INavigationAware methods not called with WPF app

We are using Prism 7.2.0.1441-ci in our WPF app. We have a problem that the INavigationAware
methods are not being called when navigating to a view. We use the below code to navigate.
The Login view is registered. The LoginViewModel has implemented INavigationAware
_regionManager.RegisterViewWithRegion(RegionNames.TabRegion, typeof(Login));
_regionManager.RequestNavigate(RegionNames.TabRegion, ViewNames.Login, parameters);
MainWindow.xaml
<DockPanel>
<!-- <Frame x:Name="_mainFrame" NavigationUIVisibility="Hidden" /> -->
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.TabRegion}" />
</DockPanel>
This mechanism does work in another part of the app, this particular bit is in the startup code and is being called from the MainWindowViewModel constructor.
Any ideas?
Thanks
This mechanism does work in another part of the app, this particular bit is in the startup code and is being called from the MainWindowViewModel constructor.
You cannot navigate from the shell view model's constructor, because the regions aren't there yet.
Instead, do the first navigation from OnInitialized (or let the user click a button).

How to bind the Panning command to the left mousebutton for OxyPlot in WPF?

I am using an Oxyplot control in my WPF application. Is there a MVVM friendly way to re-wire the right click pan action to happen with a left click?
My current wpf code is
<oxy:PlotView Model="{Binding MyData}" Grid.Column="1" Grid.Row="0" />
I'd suggest you to create a User Control:
myUC.xaml
<oxy:PlotView x:Name="PlotView1" Model="{Binding **MyPlotModel**}" />
myUC.xaml.cs
public partial class myUC : UserControl
{
public myUC()
{
InitializeComponent();
PlotView1.Controller = new OxyPlot.PlotController();
/* Events Managment */
PlotView1.Controller.UnbindAll();
PlotView1.Controller.BindMouseDown(OxyMouseButton.Left, PlotCommands.PanAt);
}
}
Note I have replaced Mydata by MyPlotModel, if you want to directly bind data use Plot instead.
It is not really MVVM friendly because you'll have to use this UC, but you can change its DataContext to bind it to your ViewModel.
Normally you could bind a Controller created from your viewModel just like this:
<oxy:PlotView x:Name="PlotView1" Model="{Binding **MyPlotModel**}" Controller="{Binding MyPlotController}"/>
But it seems bugged and I could not figure out why. May be this will help you
https://github.com/oxyplot/oxyplot/issues/436
Let me know if you can solve it without UC.

Caliburn Micro self-replacing View/ViewModel

I have a listbox to select an item for edit. I have an edit button as well. Call this the MainView[Model].
If I press the edit button the MainView[Model] shall be replaced by EditView[Model].
The EditView shall not be displayed in a area below or beside the MainView. It should be completely replaced or at least completely hide the MainView.
If edit is finished (OK, cancel) the MainView shall be displayed again.
I have tried to overlay a ContentControl but with no success.
Now, I'm thinking about a kind of NavigatorViewModel which has multiple ViewModels exposed by a property. But I'm not sure if this is the right direction to go.
Can anybody help?
Thx.
You would preferably use the Conductor pattern that Caliburn.Micro provides. A conductor manages one or more Screens and controls their lifetime. See Screens, Conductors and Composition for further information.
At first, we need a shell. This is your "NavigatorViewModel". It is derived from Conductor<Screen>.Collection.OneActive, what means that it holds a list of Screens of which a single one can be active at a time:
public interface IShell
{
void ActivateItem(Screen screen);
}
public class ShellViewModel : Conductor<Screen>.Collection.OneActive, IShell
{
public ShellViewModel()
{
this.ActivateItem(new MainViewModel());
}
}
A conductor has an ActiveItem property, and we want to bind a ContentControl to it, so we see the corresponding view:
<!-- ShellView.xaml -->
<Window x:Class="WpfApplication1.ShellView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ContentControl Name="ActiveItem" />
</Window>
Our MainViewModel can navigate to the EditViewModel using its parent, the shell:
public class MainViewModel : Screen
{
public void Edit()
{
((IShell)this.Parent).ActivateItem(new EditViewModel());
}
}
We bind a button to the Edit method:
<!-- MainView.xaml -->
<UserControl x:Class="WpfApplication1.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button Name="Edit" Content="Edit" />
</UserControl>
EditViewModel also derives from Screen and just contains your edit logic:
public class EditViewModel : Screen
{
}
Finally, we bind a button to the TryClose method, so the view model closes itself and is removed from the shell's items. The last activated item (MainViewModel) will be reactivated:
<!-- EditView.xaml -->
<UserControl x:Class="WpfApplication1.EditView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Button Name="TryClose" Content="Back" />
</UserControl>
That's about it.

WPF mvvm DataTemplates change View from UserControl

I am new to WPF and the MVVM pattern. I am trying to build a 'step by step' or 'wizard' like application.
So the user should first login then select some data and finally the selected data should be stored somewhere. (This flow never changes!)
However I decided to use DataTemplates and different ViewModels for each Template and a MainViewModel for the Window which populates the Templates. (Think this should be ok regarding to different Posts here)
But now my problems are starting. I know how I can change the current view from the MainViewModel BUT I want to change the current View from the inner ViewModel. And I want to be able to collect data from one inner ViewModel to another and I have no clue how this can work.
MainViewModel:
public class MainWindowViewModel : ViewModelBase
{
public ViewModelBase CurrentView {get; set;}
public MainWindowViewModel()
{
CurrentView = new ViewModelA;
}
}
XAML:
<Window x:Class="PUSEImporter.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:VM="clr-namespace:PUSEImporter.ViewModel"
xmlns:V="clr-namespace:PUSEImporter.View">
<Window.DataContext>
<VM:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type VM:ViewModelA}">
<V:Detail />
</DataTemplate>
<DataTemplate DataType="{x:Type VM:ViewModelB}">
<V:Overview />
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding CurrentView}"/>
</Window>
So think about a button in ViewModelA (or the View from ViewModelA) and now I want to switch to ViewModelB when someone clicks on the button. And not enough the data which is collected by ViewModelA should also be available in ViewModelB.
Is this possible and the preferred way of using this techniques or do I misunderstand some concepts?
(And if this is true how should i handle things like that?)
Thanks in advance!
There are many ways to achieve what you want. In MVVM, there's one view model to each view... therefore, if your main view has a child view, then your main view model should have a property of the type of another view model. In this instance, you can add a delegate to the child view model and register a handler for it in the main view model.
This will enable you to effectively pass a signal to the main view model from the child view model, which you can react to in the parent view model in any way you want to. Rather than explain the whole story again here, please see my answers from the Passing parameters between viewmodels and How to call functions in a main view model from other view models? posts here on Stack Overflow for more information on how to achieve this.

Silverlight Switching views in a view using MVVM Light framework

I have searched and tried for days and finally must ask the question here.
I have a Silverlight 5 application, Using MVVM Light, where I want to be able to dynamically switch views in the main view.
For the sake of simplicity, lets say I have 2 buttons.
Button1 will switch to TestView1.
Button2 will switch to TestView2.
<Button Content="TestView1" Grid.Column="1" Command="{Binding CallTestView1Command}" HorizontalAlignment="Left" Margin="185,17,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="TestView2" Grid.Column="1" Command="{Binding CallTestView2Command}" HorizontalAlignment="Left" Margin="280,17,0,0" VerticalAlignment="Top" Width="75"/>
The way I have done it is by binding a relaycommand to the button and then instanciating a new viewmodel of the corresponding view.
ie:
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
CurrentView = ViewModelLocator.NinjectKernel.Get<TestViewModel1>();
}));
}
}
The CurrentViewmodel is then set to the new viewmodel.
In the MainView I have bound the CurrentView to a ContentControl:
<Border x:Name="displayedView" Grid.Row="2">
<ContentControl Content="{Binding CurrentView}" />
</Border>
This will actually work to some extend, since the CurrentView will change but instead of actually showing the content of the view it simply shows the Namespace of the ViewModel that is instanciated.
So far I have primarily used the knowledge taken from these sources:
http://rachel53461.wordpress.com/2011/05/28/switching-between-viewsusercontrols-using-mvvm/
Loading Views into ContentControl and changing their properties by clicking buttons
but they do not solve my problem, or I do not quite understand how to actually show the views.:-(
So does anyone have a good explanation on how to switch the views correct in Silverlight 5 using MVVM Light from GalaSoft.
Thanks
The part you are missing is the DataTemplates that tell WPF how to render your ViewModels
<Window.Resources>
<DataTemplate TargetType="{x:Type local:TestViewModel1}">
<local:TestView1 />
</DataTemplate>
<DataTemplate TargetType="{x:Type local:TestViewModel2}">
<local:TestView2 />
</DataTemplate>
</Window.Resources>
When you insert an object in the Visual Tree, such as placing a ViewModel object in ContentControl.Content, it will get drawn by default using a TextBlock bound to the .ToString() of the object, which is why you are only seeing the namespace.classname of the ViewModel in your ContentControl
By defining an implicit DataTemplate in your Resources somewhere (that's a DataTemplate with only a TargetType defined - no x:Key), you are telling WPF to draw the specified object using the specified DataTemplate anytime it tries to draw that object, instead of using the default TextBlock bound to the .ToString() of the object.
It should be noted that implicit DataTemplates are not supported in earlier versions of Silverlight, however they are supported in 5.0+. For earlier versions of Silverlight, I usually use a DataTemplateSelector instead.
Id first suggest that you do not display your views via a ContentControl but look into using the navigation Frame in the silverlight toolkit. Also, we dont want our ViewModel creating Views... that'd not be so good. We don't mind, however, if our ViewModel does business logic and DETERMINES which view to show. Get the toolkit here: http://silverlight.codeplex.com/
Now setup your XAML as so in your main page:
<Border x:Name="displayedView" Grid.Row="2">
<navigation:Frame x:Name="ContentFrame" />
</Border>
Since you are using MVVM Light, we will use messaging. Your View model will get the command to change views, determine which view to change, then send a message to the main page to instruct it to change views.
Setup a listener in your main page for a navigate request as so:
public MainPage()
{
InitializeComponent();
Messenger.Default.Register<Uri>(this, "NavigationRequest", (uri) => ContentFrame.Navigate(uri));
}
Next, setup your command in your view model.
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
Messenger.Default.Send<Uri>(new Uri("/Views/.../Page.xaml", UriKind.Relative), "NavigationRequest");
}));
}
}
These are the basics that work for me. You can expand on this and get real "architecty". For example, you can create a base class for you view models that sends the navigation requests, create a helper class that generates URIs (so they are not hard coded everywhere in your app, etc etc. Good luck!
So i actually solved this problem, in a way where there is no need to create datatemplates in the MainView, which i did not like. imo the MainView should know nothing about the views it is displaying, when we are talking about switching the views.
Prerequisite: You must use MVVM Light from GalaSoft for this solution
This is my test solution:
Two buttons are added to my MainView, Each button will open a new view. The clickevent are bound to Commands.
<Button Content="TestView1" Grid.Column="1" Command="{Binding CallTestView1Command}" HorizontalAlignment="Left" Margin="185,17,0,0" VerticalAlignment="Top" Width="75"/>
<Button Content="TestView2" Grid.Column="1" Command="{Binding CallTestView2Command}" HorizontalAlignment="Left" Margin="280,17,0,0" VerticalAlignment="Top" Width="75"/>
In the MainView i have a Border that should contain the views than can switch.
Since all views inherit from UserControl i bind the content to the property CurrentView of the MainViewModel
<Border x:Name="displayedView" Grid.Row="2">
<UserControl Content="{Binding CurrentView}" />
</Border>
In the MainViewModel i have the property CurrentView.
public const string CurrentViewPropertyName = "CurrentView";
private UserControl _currentView;
/// <summary>
/// Sets and gets the "CurrentView property.
/// Changes to that property's value raise the PropertyChanged event.
/// </summary>
public UserControl CurrentView
{
get
{
return _currentView;
}
set
{
if (_currentView == value)
{
return;
}
RaisePropertyChanging(CurrentViewPropertyName);
_currentView = value;
RaisePropertyChanged(CurrentViewPropertyName);
}
}
When a button is clicked the corresponding Command is called in the MainViewModel:
private RelayCommand _callTestView1Command;
public RelayCommand CallTestView1Command
{
get
{
return _callTestView1Command ??
(_callTestView1Command = new RelayCommand(() =>
{
CurrentView = new TestView1();
}));
}
}
private RelayCommand _callTestView2Command;
public RelayCommand CallTestView2Command
{
get
{
return _callTestView2Command ??
(_callTestView2Command = new RelayCommand(() =>
{
CurrentView = new TestView2();
}));
}
}
As seen each command will set CurrentView to a new view, and the views will switch in the MainView, because CurrentView will raise a ProperTyChanged Event.
This will actually work to some extend, since the CurrentView will
change but instead of actually showing the content of the view it
simply shows the Namespace of the ViewModel that is instanciated.
Because you are changing the CurrentView property to a viewmodel instance and bind that as the Content. This is wrong as the Content should be a view and you should set the DataContext of that view to a viewmodel.
The simplest thing you can do here is to create a View instance inside the command and set the viewmodel as its DataContext and then you can set the view to the CurrentView property. Of course this would violate the MVVM pattern so you should move this responsibility to a separate component. Instead of writing your own navigating logic I suggest you to pick up an existing solution as this kind of task is not as straightforward as it seems.
I suggest to use the Prism library

Resources