Child window in MVVM light - wpf

How can I use Messaging from MVVM Light in am WPF app for showing a ChildWindow and returning some values from it ?
what I need is to present to user a modal dialog with 2 datepickers and the message to return somehow theese 2 values for using as parameters to other views.
Is it possible ?

Usually I just use a Popup for something like this.
The Popup usually has its Visibility bound to something like an IsPopupVisible property in the ViewModel, and the Popup DataContext is usually also part of the ViewModel
I'm not a huge fan of WPF's default Popup control, so I have my own custom Popup control here if you're interested.
It's typically used like this:
<local:PopupPanel Content="{Binding PopupContent}"
local:PopupPanel.PopupParent="{Binding ElementName=ParentPanel}"
local:PopupPanel.IsPopupVisible="{Binding IsPopupVisible}" />
and I show it from the ViewModel like this:
PopupContent = new SelectDatesViewModel();
IsPopupVisible = true;
Once the SelectDatesViewModel.SaveCommand gets triggered, you can fire off a message containing the selected dates to whatever ViewModels are interested:
Messenger.Default.Send(new DatesChangedMessage(this.Date1, this.Date2))
The actual popup content can either be defined in the XAML, or with an implicit DataTemplate
<DataTemplate DataType="{x:Type local:SelectDatesViewModel}">
<local:SelectDatesView />
</DataTemplate>

Related

WPF multiple views [duplicate]

I am working on a an WPF MVVM application where I need to have a Main Window with just a logo and it has to show child views inside it. I don't have any controls in Main Window all the controls reside in child view for example Buttons like Next, Back, Cancel and some text blocks etc. Now If users select Next button on the child view I have to draw or load the next child view inside the Main Window. If Back button is clicked I have to go back to the previous child view. So basically I am changing the child views depending on which button is clicked. Also I am maintaining different view models for every child view. Now the problem is I am not able to figure how should I link the child views to there respective view models. This application is similar to some Installation applications where different dialogs are shown depending on the selection and the button clicked by the user.I am new to this wpf and don't want to use MVVM Light , Prism etc. Any detailed help will be greatly appreciated. Thanks in advance.
One of the easiest ways to associate any data type with XAML controls is to use a DataTemplate. Therefore, you can simply add something like this into your Application.Resources and as long as you do not set the x:Key properties on the DataTemplates, then they will be explicitly applied by the Framework whenever it comes across instances of your view models:
<DataTemplate DataType="{x:Type ViewModels:HomeViewModel}">
<Views:HomeView />
</DataTemplate>
...
<DataTemplate DataType="{x:Type ViewModels:MainViewModel}">
<Views:MainView />
</DataTemplate>
Then displaying the view is as simple as this:
<ContentControl Content="{Binding YourViewModelProperty"} />
In code behind, or your view model:
YourViewModelProperty = new MainViewModel();
It's often handy to create a base class for your view models and then the YourViewModelProperty can of that type and you will be able to interchange them using the same property and ContentControl.
UPDATE >>>
The general idea is that you have one MainViewModel class with one BaseViewModel property data bound to one ContentControl in MainWindow.xaml... the navigation controls should also be in MainWindow.xaml and not in the views themselves. In this way, the MainViewModel class is responsible for changing the property to the relevant view model instances when it receives navigation Commands from the MainWindow.xaml.

Delay load user controls in parent control

I have the scenario where I have some User controls Let say they are :
CreateStudents
CreateTeachers
Each of the User control have their own View model. The datacontext is set in initialization.
I have a main UI where these above User control can be loaded. So setting the datacontext works fine.
Problem statement
I have another User control "CreateClass" which is a collection of tabs. From here I can go to the above two user cotrols (Hidden tabs) .
The datacontext is set by two properties in CreateClassViewModel "CreateStudentManager" and "CreateTeacherManager".
This works fine but the issue is when i default open CreateClass UI, the other User controls also load (I guess because they have default constructor).
Because when I open CreateClass I donot want other controls to be loaded. These should be only loaded when they are called from the Create Class UI explictly.
How to achieve this ?
below is sample for one "CreateStudent"
<TabItem Header="Students" Visibility="{Binding IsStudentVisible, Converter={StaticResource BooleanToVisibilityConverter}}" >
<Grid>
<local:UCCreateStudent DataContext="{Binding CreateStudentManager}"/>
</Grid>
</TabItem>
Girija
Easiest would be to do it in code, ie. add the local:UCCreateStudent item to the grid on the desired event trigger. Give the grid a name (e.g. x:Name="MyGrid"), then
void OnTrigger(...)
{
UCCreateStudent NewStudent = new UCCreateStudent();
// extra code for setting the datacontext and any other layout properties
MyGrid.Children.Add(NewStudent);
}
There is a good article on codeproject:
http://www.codeproject.com/Articles/217022/Delaying-Element-Initialization-for-Collapsed-Cont

Setting a property on the ViewModel from the View in WPF

I have a dependency property on my ViewModel which is the DataContext for my View. The ViewModel has no reference to the View. The property on the ViewModel is going to reference a control on the view, but I need to be able to set this property in XAML.
How is this possible? One thought I had was to develop a custom control which has a Property property and a Value property, so you could do something like this in the View to set the property on the ViewModel:
<PropertySetter Property="{Binding MyViewModelDependencyProperty}" Value="{Binding ElementName=aControlOnMyView" />
Before I went down this route, I wanted to check if there was any other approach I could take?
Thanks for the detailed reply Ray, but if I give you a bit more detail about the problem I'm trying to solve, you might get a better idea of why I mentioned the approach I did.
Basically, what I'm trying to do is set the focus to a textbox when the user hits a button. I've written an attached property which you can attach to the Button control, specify what the trigger event is (in this case the 'Click' event), and then what control to focus on. This works really nicely, and keeps everything in XAML.
However, I now have a use case where the focus should be set to an arbitrary text box from the click event on a button which is part of a toolbar. This toolbar is itself a user control which is sitting inside another user control, which is inside another user control! This toolbar needs to be reusable across various different forms, and each time, the control to set focus on after you click the button will be different per form.
That's why I had the idea of making the focus control (i.e. a textbox) a property on the view model itself (on my ViewModel base to be precise), and have the ViewModel base code (which the toolbar is bound to), set the focus to the control when the button is clicked (and the e.g. Add/Edit method is called on the ViewModel base).
In unit test land, the control to focus on property will be null, so it's .Focus() method just won't be called. So I can't see an issue there. My problem is then how you set the focus control property from XAML, which is why I had the PropertySetter idea.
I don't like the fact that the ViewModel has any reference to controls sitting on the view, but I can't see another way to achieve what I need. What if the logic that dictates whether to set focus to the control is quite complex? This would sit in the ViewModel surely? Therefore, is there any harm in the ViewModel having this UIElement property? It still knows nothing about the specific View it is bound to, it just knows that there is a control which it needs to set focus to when some action happens on the ViewModel.
My first reaction (and it's a strong one) is so say "Don't do that!" By giving your view model a reference to a part of your UI you are breaking the encapsulation that makes view models so powerful and useful.
For example, what if you want to unit test your view model or serialize it to disk? In each case the piece of your UI will not be present, because there will be no view at all. Your tests will miss coverage and your reconstitution will be incomplete.
If your view model actually needs references to UI objects and there is no better way to architect it, the best solution is to have the view model itself construct those controls it requires a reference to. Then your view can incorporate that control as the Content of a ContentPresenter via binding and provide a Style to configure the control, including a ControlTemplate to provide its content. Thusly:
public class MyViewModel
{
public ListBox SpecialControl { get; set; }
public MyViewModel()
{
SpecialControl = new ListBox();
}
}
and
<DataTemplate TargetType="{x:Type local:MyViewModel}">
<DataTemplate.Resources>
<Style TargetType="ListBox" ... />
</DataTemplate.Resources>
...
<ContentPresenter Content="{Binding SpecialControl}" />
</DataTemplate>
Other possibilities are:
Have the view model actually derive from the Control class, then override OnApplyTemplate() and use GetTemplateChild to find a template item whose name starts with "PART_"
Implement an attached property that takes a property name, finds that property in the DataContext, and sets it to the DependencyObject to which the property is attached.
Implement your PropertySetter idea
My option #2 would look like this:
<DataTemplate TargetType="{x:Type MyViewModel}">
...
<TextBox local:PropertyHelper.SetViewModelToThis="SpecialControl" />
...
</DataTemplate>
The code in the SetViewModelToThis PropertyChangedCallback would get the view model from the DataContext, reflect on it to find the "SpecialControl" property, then set it to the TextBox. Note that the implementation of SetViewModelToThis must take into account the possiblity that DataContext is not set right away, and that it maybe changed requiring the old setting to be removed and a new one made.
First of all, the DataContext of the control should be the ViewModel object and not a property of it. Second, when you TwoWay bind a property of ViewModel to your control, changes in the control's value will update (in your case, 'set') the value of ViewModel's property.

Initiating UserControl via MVVM in WPF / focus issue

I have a few usercontrols loaded into a tabcontrol via MVVM in WPF.
Within the XAML for the usercontrol I am setting focus to a textbox using the FocusManager, however this appears to only work when the first instance of the usercontrol is created.
Just to test I added a loaded event handler to the usercontrol - this is only called on the first instance.
I'm using data templates for the user controls as follows:
<DataTemplate DataType="{x:Type local:UserTypeViewModel}">
<local:UserTypeView />
</DataTemplate>
The textbox is focused as follows:
FocusManager.FocusedElement="{Binding ElementName=txtName}"
Additionally I'm using a global event handler (for the textbox GotFocus event) which selects all the text using a dispatcher.
If anyone has any tips on how to achieve focus with every usercontrol I'd be very grateful.
Thanks, Ben.
Remember that a visual element can only receive focus, if:
It is visible (in a TabControl only one tabitem can be visible at a time
IsFocusable must be set to true (is default false for UserControls)
It has finished loading (as you write - do it in the Loaded event))
I think the first reason is why it only works for the first element.
As for how to achieve it for all controls - you can use a style with an EventSetter for the Loaded event. You need to make a style per type of control though to avoid having to set it for each control.

MVVM Tabcontrol change tab

I am developing an mvvm app with wpf. A requirement just got added on to block the user from changing tabs if a textbox has text.
What is the best way to do this completely in the viewmodel? I don't know how to block a tabitem because there is no dependencyobject command in the tabcontrol to tie into, do i need to roll my own tabcontrol and build an ICommand around the SelectionChanged event?
Should I simply (eegad..don't say it) put code in the code behind of the view in the SelectionChanged event?
Do I have an alternative that I haven't thought of?
You might consider binding each of the TabItems' IsEnabled property to a property in your ViewModel (e.g. ViewModel.TabsEnabled) and set that property to False when the textbox has text. That way, you'll be able to enable/disable those tabs from your ViewModel without having to have a code behind file for that particular view.
Which means you'll have something like the following in your view (assuming your ViewModel is a static class named ViewModel):
<TabItem IsEnabled="{Binding Source={x:Static local:ViewModel.TabsEnabled}}"/>
Then you just have to set the TabsEnabled property on the ViewModel when one of the textboxes has content; there are a couple of ways to do this, but if they are bound to your ViewModel you should have plenty of opportunities to listen for changes and set TabsEnabled as appropriate.

Resources