MVVM Light with WPF ViewModel Navigation Actions - wpf

Good Day!
I am using MVVM Light on a WPF app. I have a single page and I'm using User Controls to provide views with their own respective ViewModels. I'm using the ViewModelLocator to have those wired up. I've figured out how to navigate by binding the ContentControl's Content to an observable property that gets set. Here is a snipet of the XAML:
<ContentControl Content="{Binding CurrentViewModel}" Grid.Row="1" />
The MainViewModel creates references to my two different ViewModels:
private ViewModelBase _currentViewModel;
private readonly static LogInViewModel _loginViewModel = new LogInViewModel();
private readonly static ClockEventViewModel _clockEventViewModel = new ClockEventViewModel();
In my constructor I register some Actions from the other ViewModels:
_loginViewModel.AllowClockEvent += ExecuteClockEventViewCommand;
_clockEventViewModel.ReturnToLogin += ExecuteLoginViewCommand;
They are implemented in each ViewModel like this:
public Action ReturnToLogin = delegate { };
Finally in those methods I set the CurrentViewModel to the different ViewModel. For example:
CurrentViewModel = _clockEventViewModel;
This works fine, but what I cannot do is, upon the other viewmodel becoming the current one, I'd like to capture that and do some initial work. Kind of like a NavigateTo event. I'm sure there is probably an easy way, but I've looked at the ViewModelBase and I see no method to override giving me this capability.
Who has already done this? If there is a better way then the approach I've made, please let me know.

What I end up doing is calling the method I'm needing through the exposed _clockEventViewModel. It was actually easier than I thought.

Related

Removing UserControl code behind for use with MVVM

I am trying to create a user control using MVVM.
Basically I am trying to wrap a combobox that will pull data from a respository. This will allow me to use the same combobox in many different views in my application. There will be many of the wrapped comboboxes throughout the application.
I was easily able to create this control using a DependencyProperty and code-behind. I am now trying to convert this to MVVM and am having trouble figuring out how to get the value back to /from the ViewModel that in bound to the View where my combobox is located.
Any ideas or suggestions would be greatly appreciated at this point.
Thanks,
Eric
It is perfectly acceptable to use a UserControl that has code behind in it when using MVVM. If you really want to move the functionality out of the UserControl, then move it to whichever parent view models will require it. If you don't want to have the same code repeated in several places, you could encapsulate it in a class and add an instance of that class as a property to each of the relevant view models.
if you have a viewmodel that will pull data from a respository - you can use the same viewmodel in many different viewmodels in your application :)
and if you use a datatemplate your views know how to render this viewmodel
<DataTemplate DataType="{x:Type local:MyPullDataViewmodel}">
<view:MyCoolPullDataShowComboboxUserControl />
</DataTemplate>
It's pretty easy.
Let's say you have:
MyUserControlView.xaml
MYUserControlViewModel.cs
MyMainWindowView.xaml - For your MainWindow view (the one containing the UserControl)
MyMainWindowViewModel.cs - Your MainWindow ViewModel.
And you want to bind List<string> MyListToBind
And leave the code-behind completely empty.
MyUserControlViewModel.cs
public class MyUserControlViewModel
{
private List<string> _MyListToBind;
public List<string> MyListToBind { get; set;}
}
MyMainWindowViewModel.cs
public class MyUserControlViewModel
{
private MyUserControlViewModel _MyControlViewModel;
public MyUserControlViewModel MyControlViewModel { get; set;}
}
MyMainWindowView.xaml
<Window ...
xmlns:my="clr-namespace:NamespaceContainingYourUserControlView>
<my:MyUserControlView DataContext = "{Binding Path= MyControlViewModel}"/>
</Window>
MyUserControlView.xaml
<UserControl ...>
<DataGrid ItemsSource = "{Binding Path= MyListToBind}" .../>
...
</DataGrid>
</UserControl>
This doesn't support ViewModel updating View. To do that You have to use either DependencyProperties as you did instead of normal variables (as i did) or use INotifyPropertyChanged(google it, you'll get tons of examples) and OnPropertyChanged event.
You might read up on DataTemplates they are really useful in data binding.
You can find this usefeul:
http://www.youtube.com/watch?v=BClf7GZR0DQ
I sure as hell did !
Good luck.

WPF VB.NET - Reference Controls in MainWindow from a Module

I want to use modules in my first WPF application, as I'm used to using them in WinForm applications I've created before. So I have this button, with a textblock inside.
MainWindow.xaml:
<Button x:Name="Connection_Button" Width="200" Height="30" Margin="5,5,5,5">
<TextBlock FontSize="14" MouseDown="TextBlock_MouseDown">CONNECT</TextBlock>
</Button>
Before in WinForms I could easily reference a button and it's text property by adding MainForm. before the control, but how can I do this in WPF through modules, similar to below? How do I even declare the controls at the top of my module code? And elements inside a control such as TextBlock?
Module_Connection.vb in my old WinForm way:
Private Sub Connect()
If MainForm.Connection_Button.Text = "Connect" Then
' Code
You don't usually do this in WPF.
To base your application logic in the state of UI elements is not really a good idea.
Why? because UI is Not Data and therefore you should base your application logic on the state of data items instead.
I suggest you read this and this
To try to use WPF the same way you use winforms is a straight path to miserable failure and suffering.
You must embrace MVVM and understand DataBinding and how the UI must always be separate from Application Logic and Data.
Edit: Adding a Code Sample (in C#)
ViewModel:
public class MainViewModel
{
public string ButtonContent {get;set;} //TODO: Add Property Change Notification
public MainViewModel()
{
ButtonContent = "SomeButtonContent";
}
}
View:
<Button Width="200" Height="30" Margin="5,5,5,5">
<TextBlock FontSize="14" Text="{Binding ButtonContent}"/>
</Button>
Code Behind:
public class MainWindow: Window
{
public MainWindow()
{
InitializeComponent();
SomeModule.Instance = new MainViewModel();
DataContext = SomeModule.Instance;
}
}
Module (static class in C#):
public static class SomeModule
{
public static MainViewModel Instance {get;set;}
public void DoSomething()
{
if (Instance.ButtonContent == "SomeButtonContent")
//...etc
}
}
This is what I mean by separating the UI from the data. You place your strings in a ViewModel, not in the View, then you evaluate the value of those ViewModel properties to determine what to do.
Still, basing your application logic on the value of a string seems like a weak solution. What do you really need to do? There are much better approaches such as using an Enum for this.
Edit2:
Having a ViewModel to Bind to does not "complicate" things. It actually simplifies them a LOT.
How? because you're now doing this with simple controls, but then you'll want to do the same thing with UI elements inside a ControlTemplate or even worse a DataTemplate and that's when real problems arise.
Therefore, you must get used to "the WPF Way" before you deal with more complex UI scenarios.
Non-optimal approach:
public class MainWindow: Window
{
public string ButtonContent
{
get
{
return this.txtButtonContent.Text;
}
set
{
this.txtButtonContent.Text = value;
}
}
}
<Button Width="200" Height="30" Margin="5,5,5,5">
<TextBlock x:Name="txtButtonContent" FontSize="14" Text="Connect"/>
</Button>
You must understand that the Button class doesn't have a Text property in WPF. In contrast to most ancient framweworks, WPF has a Content Model where literally anything can contain anything with little to no restrictions. Putting a Text property to the Button would be to introduce the limitation for the Button to only contain Text, which is not the case in WPF.
Therefore, what you actually want to do here is to modify the Text property of the TextBlock (which happens to be inside the Button, but could actually be anywhere in the Visual Tree).
That's why I mentioned the fact that you actually NEED a ViewModel to hold your data, because there is no (easy) way to access the UI elements located, for example within a DataTemplate in order to manipulate them. Your UI must always be a reflection of the state of your application's Data, which is stored in Model or ViewModel classes, not the UI.
This should get you in the ballpark:
Module:
Public Sub Connect(RyRef txtBox as TextBox)
If txtBox.Text = "...
note Public vs. Private
Call:
Call .Connect(MyTextBox1)
Call and are optional
I'll answer your question in VB.NET and not provide C# samples of alternative ways of doing it which if you're not used too can be an uphill struggle.
Declare this in your module, access the controls through rootWindow.
Private rootWindow As MainWindow = TryCast(Application.Current.MainWindow, MainWindow)
Another approach is :
Dim mw as MainWindow = Application.Current.Windows.OfType(Of MainWindow).First
assuming you have a MainWindow and only have one MainWindow.

How should I populate the ViewModel in WPF?

I'm new to WPF and I'm writing a simple test app to familiarize myself with it. My test app will detect all joysticks I have attached to my computer and display information about it. So far, I have this ViewModel:
public class JoystickViewModel
{
public ObservableCollection<Joystick> Joysticks { get; set; }
public JoystickViewModel()
{
GetAttachedJoysticks();
}
private void GetAttachedJoysticks()
{
// populate Joysticks collection by using SlimDX
}
}
And this is my codebehind for my MainWindow.xaml:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new JoystickViewModel();
}
}
And my XAML for MainWindow:
<Window ...>
<Grid>
<ComboBox ItemsSource="{Binding Joysticks}"
DisplayMemberPath="Information.ProductName"/>
</Grid>
</Window>
I followed a tutorial that also populated the ViewModel in its constructor.
My question is, how should I populate the ViewModel? It seems sort of weird to me that I'm population the collection in the ViewModel constructor. Should this logic be in MainWindow's codebehind instead? Or somewhere else altogether? The end goal is to not only have this collection populated, but also updated periodically to reflect the current state (user plugged in new joystick, unplugged existing one, etc...).
The MainWindow code behind is definitively not the place where "business" logic should occur, as the View should be kept as simple as possible.
Keep your fetch/update logic inside of your viewmodel, this way you can test it easily and independently.
From a learning perspective, it's important to keep concerns separated :
the View is bound to the ViewModel, and has no intelligence
the ViewModel has knowledge on how to get the Model
the Model represents the data
In your case, the VM knowledge is at the moment a call inside it's constructor. Later you can change this to call some IJoystickDataService interface, and wire everything using a MVVM framework.
I would have your JoySticks observable collection property (and the code that populates it) in a Model class. The viewmodel simply exposes this same property to the view for binding. The vm should be as thin as possible - ideally just exposing properties that are in the model for binding and not doing any kind of 'business' logic (i.e. populating joystick info as in your case).

passing data to a mvvm usercontrol

I'm writting a form in WPF/c# with the MVVM pattern and trying to share data with a user control. (Well, the User Controls View Model)
I either need to:
Create a View model in the parents and bind it to the User Control
Bind certain classes with the View Model in the Xaml
Be told that User Controls arn't the way to go with MVVM and be pushed in the correct direction. (I've seen data templates but they didn't seem ideal)
The usercontrol is only being used to make large forms more manageable so I'm not sure if this is the way to go with MVVM, it's just how I would of done it in the past.
I would like to pass a class the VM contruct in the Xaml.
<TabItem Header="Applicants">
<Views:ApplicantTabView>
<UserControl.DataContext>
<ViewModels:ApplicantTabViewModel Client="{Binding Client} />
</UserControl.DataContext>
</Views:ApplicantTabView>
</TabItem>
public ClientComp Client
{
get { return (ClientComp)GetValue(ClientProperty); }
set { SetValue(ClientProperty, value); }
}
public static readonly DependencyProperty ClientProperty = DependencyProperty.Register("Client", typeof(ClientComp),
typeof(ApplicantTabViewModel),
new FrameworkPropertyMetadata
(null));
But I can't seem to get a dependancy property to accept non static content.
This has been an issue for me for a while but assumed I'd find out but have failed so here I am here.
Thanks in advance,
Oli
Oli - it is OK (actually - recommended) to split portions of the View into UserControl, if UI became too big - and independently you can split the view models to sub view models, if VM became too big.
It appears though that you are doing double-instantiations of your sub VM. There is also no need to create Dependency Property in your VM (actually, I think it is wrong).
In your outer VM, just have the ClientComp a regular property. If you don't intend to change it - the setter doesn't even have to fire a property changed event, although it is recommended.
public class OuterVm
{
public ClientComp Client { get; private set; }
// instantiate ClientComp in constructor:
public OuterVm( ) {
Client = new ClientComp( );
}
}
Then, in the XAML, put the ApplicantTabView, and bind its data context:
...
<TabItem Header="Applicants">
<Views:ApplicantTabView DataContext="{Binding Client}" />
</TabItem>
I answered a similar question as yours recently: passing a gridview selected item value to a different ViewModel of different Usercontrol
Essentially setting up a dependency property which allows data from your parent view to persist to your child user control. Abstracting your view into specific user controls and hooking them using dependency properties along with the MVVM pattern is actually quite powerful and recommended for Silverlight/WPF development, especially when unit testing comes into play. Let me know if you'd like any more clarification, hope this helps.

Binding a ContentControl to a deep path in WPF

The application I'm currently writing is using MVVM with the ViewModel-first pattern. I have XAML similar to the following:
<ContentControl Content="{Binding FooViewModel.BarViewModel.View, Mode=OneWay}"/>
Every VM is a DependencyObject. Every property is a DependencyProperty. Depending upon the state of the application, the value of the BarViewModel property of the FooViewModel can change, thus changing the value of the View property. Unfortunately when this happens, the new view is not displayed, and the old one remains.
This is extremely frustrating. I thought that if any part of a path expression changed, the binding would update, but that doesn't appear to be the case. When I've used shallower path expressions, such as FooViewModel.View and I've changed the value of the FooViewModel property, that has updated the ContentControl to which it's bound, but not in this case.
If your solution is that I abandon ViewModel-first, that is not an option, though I appreciate your advice. I must get this working as is.
CLARIFICATION
This is a question about data binding, and not about MVVM or how to implement it. You can safely ignore the MVVM aspects of this if it helps you to think about the problem, or if you have a different idea about how MVVM should be implemented. This is a large, existing project in which the MVVM design pattern cannot be changed. (It is far too late for that.)
So, with that said, the correct question to be answering is the following:
Given a binding path expression in which every element is a DependencyProperty and the final property is a view bound to a ContentControl, why does a change in a property in the middle of the path not cause the binding to update?
Although I would expect this to work, there are several problems with your approach.
Firstly, your view models should not use DependencyObject or DependencyProperty, this ties them in to WPF. They should instead implement INotifyPropertyChanged. This makes your view models reusable in other presentation technologies such as Silverlight.
Secondly, your view models shouldn't have references to your views, so you shouldn't require a View property on your view models.
I would seriously consider using an MVVM framework for view composition - Caliburn.Micro, for example, makes view model first development extremely straightforward, and already provides a view model base class which implements INotifyPropertyChanged, and a mechanism for building view compositions with conventions.
I.e. you can have a conductor view model which has an ActiveItem property, and you simply place a ContentControl on your view with the same name as the property:
<ContentControl x:Name="ActiveItem" />
You can use the ActivateItem() method to change the current active item.
Caliburn.Micro also has a host of other features, such as being able to place a Button control with x:Name="Save" on your view, and your Save method on your view model will automatically be invoked when the button is clicked.
Every VM is a DependencyObject. Every property is a
DependencyProperty.
why? a viewmodel should be a simple class with INotifyPropertyChanged and the Properties should be simple properties.
and if you want your different viewmodel be rendered in a different way - you should use DataTemplate.
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type local:MyViewModelA}>
<MyViewA/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:MyViewModelB}>
<MyViewB/>
</DataTemplate>
</Windows.Resources>
<Grid>
<ContentControl Content="{Binding MyActualVM}"/>
</Grid>
</Window>
EDIT: btw you always bind to the last Property: FooViewModel.BarViewModel.View --> so the INotifyPropertyChanged (if raised) just work for the .View
EDIT2: another approach could be to get the BindingExpression of your content control and call.
System.Windows.Data.BindingExpression expr = //get it from your contentcontrol
expr.UpdateTarget();
EDIT3: and a simple mvvm way - just use INotifyPropertyChanged
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.MyFooVM = new FooVM();
this.MyFooVM.MyBarVM = new BarVM(){View = "erster"};
this.DataContext = this;
}
public FooVM MyFooVM { get; set; }
private void Button_Click(object sender, RoutedEventArgs e)
{
this.MyFooVM.MyBarVM = new BarVM(){View = "zweiter"};
}
}
public class INPC : INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropChanged(string property)
{
var handler = PropertyChanged;
if(handler != null)
handler(this, new PropertyChangedEventArgs(property));
}
#endregion
}
public class FooVM:INPC
{
private BarVM _myBarVm;
public BarVM MyBarVM
{
get { return _myBarVm; }
set { _myBarVm = value;OnPropChanged("MyBarVM"); }
}
}
public class BarVM : INPC
{
private string _view;
public string View
{
get { return _view; }
set { _view = value;OnPropChanged("View"); }
}
}

Resources