I am learning Caliburn Micro and I created a project similar to this tutorial :
http://caliburnmicro.codeplex.com/wikipage?title=Basic%20Configuration%2c%20Actions%20and%20Conventions&referringTitle=Documentation
Now I want to create another user control and add it to the above model. so I created a simple user control which is essentially the same as shellViewModel in the tutorial. The view and view model are the same as shellViewModel and ShellView, but with a different name.
When I run application, I can see that view is shown on screen, but it is not bind to ViewModel. Should I do any changes to bootstrap so this works?
More information:
I have created a wpf project similar to tutorial as explained above.
It works well.
I add a new user control to project and names it NewUCView.
So I have the following files in my project:
NewUCView.xaml
<UserControl x:Class="CaliburnMicroTest.NewUCView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<StackPanel>
<TextBox x:Name="Name" />
<Button x:Name="SayHello"
Content="Click Me" />
</StackPanel>
</UserControl>
NewUCView.xaml.cs
namespace CaliburnMicroTest
{
/// <summary>
/// Interaction logic for NewUC.xaml
/// </summary>
public partial class NewUCView : UserControl
{
public NewUCView()
{
InitializeComponent();
}
}
}
NewUCViewModel.cs
namespace CaliburnMicroTest
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using global::Caliburn.Micro;
/// <summary>
/// TODO: Update summary.
/// </summary>
public class NewUCViewModel : PropertyChangedBase
{
string name;
public string Name
{
get { return name; }
set
{
name = value;
NotifyOfPropertyChange(() => Name);
NotifyOfPropertyChange(() => CanSayHello);
}
}
public bool CanSayHello
{
get { return !string.IsNullOrWhiteSpace(Name); }
}
public void SayHello()
{
MessageBox.Show(string.Format("Hello {0}!", Name)); //Don't do this in real life :)
}
}
}
I changed the ShellView as follow and add a reference to NewUCView into it.
<StackPanel>
<my:NewUCView x:Name="newUC" />
<TextBox x:Name="Name" />
<Button x:Name="SayHello"
Content="Click Me" />
</StackPanel>
also changed the ShellViewModel to have a property called newUC which is a NewUCViewModel as follow:
private NewUCViewModel newUC=new NewUCViewModel();
public NewUCViewModel NewUC
{
get
{
return newUC;
}
}
But when I run this application and press the first click me (which is on user control), it doesn't work. The other button (which is on ShellView) works.
You should not add the usercontrol directly to the view, and let the caliburn framework do that for you.
You need to add a placeholder to your main view and bind it to the property that represents your ViewModel:
<ContentControl x:Name="NewUC"/>
Since caliburn can resolve data bindings by looking to the name of the Control, in my code the framework will bind the NewUCView with the NewUCViewModel that is named, in your example, NewUC, and show an instance of NewUCView inside the ContentControl palceholder.
Related
I'm trying to learn ReactiveUI. Currently I'm playing with ReactiveUI.Validation.
I'm using a simple TextBox. Everything is working as expected if the binding doesn't have UpdateSourceTrigger=PropertyChanged. If i add it, it will hang when i type something in the textbox.
What do I miss?
Here is the MainWindow not working code:
<rxui:ReactiveWindow x:Class="ReactiveValidationTest.MainWindow"
x:TypeArguments="local:MainViewModel"
xmlns:rxui="http://reactiveui.net"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ReactiveValidationTest"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel Margin="50">
<TextBox x:Name="TxtBox"
Text="{Binding InputText,UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch"/>
<TextBox HorizontalAlignment="Stretch"/>
</StackPanel>
</rxui:ReactiveWindow>
The code behind:
using ReactiveUI;
using ReactiveUI.Validation.Extensions;
using System.Reactive.Disposables;
namespace ReactiveValidationTest
{
public partial class MainWindow : ReactiveWindow<MainViewModel>
{
public MainWindow()
{
InitializeComponent();
ViewModel = new MainViewModel();
this.WhenActivated(d =>
{
this.WhenAnyValue(x => x.ViewModel)
.BindTo(this, x => x.DataContext)
.DisposeWith(d);
this.BindValidation(ViewModel, vm => vm.InputText, v => v.TxtBox.Text);
});
}
}
}
And the ViewModel:
using ReactiveUI.Fody.Helpers;
using ReactiveUI.Validation.Extensions;
using ReactiveUI.Validation.Helpers;
namespace ReactiveValidationTest
{
public class MainViewModel : ReactiveValidationObject
{
[Reactive]
public string InputText { get; set; }
//ctor
public MainViewModel()
{
this.ValidationRule(vm => vm.InputText,
txt => !string.IsNullOrEmpty(txt),
"Can not be null or Empty");
}
}
}
Thanks.
You have infinite loop, because you use this code:
this.BindValidation(ViewModel, vm => vm.InputText, v => v.TxtBox.Text);
Your current code reads input form control and set property, then ValidationRule is trying to update control, to show error message. It triggers update of input control, so again - it triggers ValidationRule. You use this mechanism wrong.
BindValidation method should be used to bind error message for given rule, which is connected with vm property to control which is designated to show that message.
You are trying to use build-in WPF mechanism which uses INotifyDataErrorInfo and your code is almost good. You should remove this.BindingValidation, because binding in XAML already do the job related with error message.
I have been doing development work in WPF application which uses an MVVM pattern for a couple of days now. I'm very new to WPF and MVVM pattern as well.
In my scenario, I have a user control view (named EPayView.xaml) which has a textbox that will accept a phone number. The view has a corresponding viewmodel (named EPayViewModel.cs). In the MainWindow.xaml, I have a user control (floating virtual keyboard) which is derived from namespace controls WpfKb.Controls. The MainWindow.xaml also has a corresponding viewmodel (named MainViewModel.cs)
Having said that, I have done research on how to use attached dependency properties which lead me to this solution. Set focus on textbox in WPF from view model (C#) which I believe this is where I could bind the property IsFocused in the textbox of EPayView.xaml.
Below are the codes that I have already incorporated in my solution.
EpayView.xaml (textbox xaml markup)
<TextBox Text="{Binding PhoneNo}" Grid.Row="5" Margin="10,0,10,0" VerticalContentAlignment="Center" FontSize="12" x:Name="Email" behaviors:FocusExtension.IsFocused="{Binding IsFocused, Mode=TwoWay}"/>
MainWindow.xaml (xaml markup)
<Window x:Class="SmartPole540.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:WpfKb.Controls;assembly=SmartPole.WpfKb"
xmlns:wpf="clr-namespace:WebEye.Controls.Wpf;assembly=WebEye.Controls.Wpf.WebCameraControl"
xmlns:utilities="clr-namespace:SoltaLabs.Avalon.Core.Utilities;assembly=SoltaLabs.Avalon.Core"
xmlns:userControls="clr-namespace:SoltaLabs.Avalon.View.Core.UserControls;assembly=SoltaLabs.Avalon.View.Core"
xmlns:square="clr-namespace:SmartPole.View.Square;assembly=SmartPole.View"
xmlns:view="clr-namespace:SmartPole.View;assembly=SmartPole.View"
Title="CitiPulse"
WindowStartupLocation="Manual"
PreviewMouseLeftButtonDown="Window_PreviewMouseLeftButtonDown"
Name="mainWindow">
<userControls:RollPanel.BottomContent>
<square:SquareView Canvas.Top="1010" DataContext="{Binding DataContext.SquareViewModel,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type userControls:RollPanel}}}"/>
</userControls:RollPanel.BottomContent>
<controls:FloatingTouchScreenKeyboard
x:Name="floatKb" Width="500" Height="250" PlacementTarget="{Binding ElementName=MainGrid}"
Placement="Center" AreAnimationsEnabled="False" Visibility="Visible"
IsOpen="{Binding IsChecked, ElementName=kbButton}"/>
</Window>
In the above code, the user control RollPanel.BottomContent host the EPayView.xaml view inside another view which is RollPanel.xaml
EpayViewModel.cs contains the static class FocusExtension for the IsFocused attached property (refer to this solution - Set focus on textbox in WPF from view model (C#)). And, EPayViewModel.cs already implemented INotifyPropertyChanged which is wrapped inside a concrete class ObservableObject that accepts type of T. This is also same with MainViewModel.cs
public class EPayViewModel : ObservableObject<EPayViewModel>, IPaymentViewModel, IActiveViewModel
{ ... }
public class MainViewModel : ObservableObject<MainViewModel>
{ ... }
As such, my goal is that when the textbox in EPayView.xaml has the focus, the floating virtual keyboard (floatKb) in the MainWindow.xaml will be shown.
I'm stuck on how to proceed (I was thinking if a call to FocusExtension static class in EPayViewModel inside my MainViewModel.cs will suffice?), any help is greatly appreciated.
Cheers,
As AnjumSKhan already said, to react to some event in a MVVM way, you'll have to use Command. Command can be called within an EventTrigger, you will need to add a Reference to System.Windows.Interactvity component.
Let's assume you have a simple View and View Model and you need to show this View when the TextBox in a MainWindow got focus.
View (NewWindow.xaml)
<Window x:Class="My.NewWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="NewWindow" Height="300" Width="300">
<TextBlock Text="{Binding Message}"/>
View Model
public class NewWindowViewModel
{
private string _message;
public string Message
{
get { return _message; }
set { _message = value; }
}
}
You also have a MainWindow, it is a main view for an app and it contains the target TextBox. You may see that there is an EventTrigger added to the TextBox and it has a property InvokeCommandAction which is binded to the MainWindowViewModel's command called ShowCommand.
Main Window
<Window x:Class="My.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity" Title="MainWindow" Height="350" Width="525">
<TextBox Height="40" Text="{Binding Text}">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="GotFocus">
<Interactivity:InvokeCommandAction Command="{Binding ShowCommand}"/>
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</TextBox>
In the Show method of MainWindowViewModel NewWindow view is created and got new NewWindowViewModel instance as a DataContext. RelayCommand class is presented in my answer to this question
MainWindowViewModel
public class MainWindowViewModel
{
private string _text;
public string Text
{
get { return _text; }
set { _text = value; }
}
private ICommand _increaseCommand;
public ICommand ShowCommand
{
get
{
if (_increaseCommand == null)
{
_increaseCommand = new RelayCommand(
p => true,
Show);
}
return _increaseCommand;
}
}
private void Show(object obj)
{
var w = new NewWindow();
var nvm = new NewWindowViewModel();
nvm.Message = "Test";
w.DataContext = nvm;
w.Show();
}
}
What is left is to create a new MainWindowViewModel and setup a DataContext for MainWindow.
public MainWindow()
{
InitializeComponent();
var mvm = new MainWindowViewModel();
mvm.Text = "Focus me!";
DataContext = mvm;
}
Hope it will help.
In XAML/WPF I have main window which contains Frame where I intend to put one of the user controls for given view of application.
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ignore="http://www.galasoft.ch/ignore"
mc:Ignorable="d ignore"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Grid x:Name="LayoutRoot">
<Frame Source="Main/MainUserControl.xaml" Name="Main" />
</Grid>
</Window>
Now I want to navigate this Frame to other source inside MainUserControl:
<UserControl x:Class="MyApp.View.MainMenu.MainMenuUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:lex="http://wpflocalizeextension.codeplex.com"
xmlns:command="clr-namespace:MyApp.Command"
mc:Ignorable="d"
Style="{StaticResource Localizable}"
DataContext="{Binding MainMenu, Source={StaticResource Locator}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Button Content="{lex:Loc About}" FontSize="28" Grid.Row="1" Command="NavigationCommands.GoToPage" CommandParameter="/Menu/AboutUserControl.xaml" />
</Grid>
</UserControl>
But the navigation button About remains inactive during execution. I verified correctly that /Menu/AboutUserControl.xaml exists.
I'm obviously doing something wrong. How can I navigate owning window's frame from within user control? Preferably via XAML?
I assume you are using an MVVM framework. (I have added the critical elements here in case you aren't).
Your MainWindow.xaml should use an "ItemsControl" instead of a "Frame". A frame can work, but a better way is to use the ItemsControl like so:
<!-- Main Frame -->
<Grid Grid.Column="1" Margin="10" Name="MainWindowFrameContent">
<ItemsControl ItemsSource="{Binding Path=MainWindowFrameContent}" >
<!-- This controls the height automatically of the user control -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="1" IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
In the constructor of my MainWindow.cs, I set the DataContext of the window to the MainViewModel:
using myProject.ViewModel;
public partial class MainWindow : Window
{
MainViewModel mMainViewModel;
public MainWindow()
{
InitializeComponent();
// Initialize MainViewModel and set data context to the VM
mMainViewModel = new MainViewModel();
DataContext = mMainViewModel;
}
}
(I'm not sure if this next part HAS TO be an observable collection, but I have implemented it as such and it seems to work well. The only downside is that I need to manually clear the ItemsControl before adding a new UserControl)
My MainViewModel implements the binding called "MainWindowFrameContent". All of my user controls are initialized within the MainViewModel.cs code. I have an additional ViewModel for each UserControl and assign the DataContext of the UserControl to the individual ViewModel before displaying the UserControl to the main window.
My MainViewModel.cs:
public class MainViewModel : ObservableObject
{
public MainViewModel()
{
}
// This handles adding framework (UI) elements to the main window frame
ObservableCollection<FrameworkElement> _MainWindowFrameContent = new ObservableCollection<FrameworkElement>();
public ObservableCollection<FrameworkElement> MainWindowFrameContent
{
get
{
return _MainWindowFrameContent;
}
set
{
_MainWindowFrameContent = value;
RaisePropertyChangedEvent("MainWindowFrameContent");
}
}
// This handles opening a generic user control on the main window
// The ICommand implementation allows me to bind the command of a button on the main window to displaying a specific page
public ICommand MainWindowDataEntryView
{
get
{
return new DelegateCommand(_MainWindowDataEntryView);
}
}
void _MainWindowDataEntryView(object obj)
{
DataEntryVM wDataEntryVM = new DataEntryVM();
DataEntryView wDataEntryView = new DataEntryView();
wDataEntryView.DataContext = wDataEntryVM;
MainWindowFrameContent.Clear();
MainWindowFrameContent.Add(wDataEntryView);
}
}
Then you need to make sure you have an ObservableObject.cs as part of your project:
using System.ComponentModel;
public class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChangedEvent(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
And you need a DelegateCommand.cs class as part of your project:
using System;
using System.Windows.Input;
public class DelegateCommand : ICommand
{
private readonly Action<object> _action;
public DelegateCommand(Action<object> action)
{
_action = action;
}
public void Execute(object parameter)
{
_action(parameter);
}
public bool CanExecute(object parameter)
{
return true;
}
#pragma warning disable 67
public event EventHandler CanExecuteChanged { add { } remove { } }
#pragma warning restore 67
}
So, it's a bit of a lengthy explanation, but once you have the previous items set up, you can add a bunch of buttons to your MainWindow.xaml, bind each button to a command that adds a new UserControl to your ItemsControl. When your UserControl displays, you can add controls as you would like and use them.
I hope this helps!
I was thinking to create a user control to be used in many applications, and I would like to use MVVM pattern.
For example, I have a user control with a calendar, that when I click in a day, the user control search for the tasks that I have to do this day.
So I was thinking that the user control has a view model for the logic inside the user control, that is to search for the tasks of the day. So I bind the selectedDate property of the calendar in the user control to the porperty to the view model of the user control, so when the value is changed, the view model can search for the tasks of the day.
Also I want that this user control notify to the main application, the selectedDate in the calendar, because the main aplication, has to do another things when the selected date is changed. So I have tried to bind a property in my main view model to the dependency property that I have created in my user control, but how in the property in the user control is bind to the property of the view model of the user control, the main view model is not notify when the day is changed.
I know how to do this in code behind, but I would like to know if it is possible to do in MVVM, because the user control has its own logic and I would like to follow the MVVM pattern. If not, when I have many user controls in my application, only the main application use the MVVM pattern and the rest code behind, so I could have a hugh percent of my application in code behind, and I would like to avoid this.
In summary, I would like to know how when I change a date in my calendar, the user control notify to its view model and also notify to the binding property in the main view in my application.
Thanks.
EDIT
Finally I have get what I wanted to do with events to communicate the changes in the view model of the user control to the code behind of the user control that updates the dependecy properties and the dependency properties notify the changes to the main view. The code is the following:
XAML of the main view:
<Window x:Class="UserControlMvvm.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:UserControlMvvm"
xmlns:vm="clr-namespace:UserControlMvvm"
mc:Ignorable="d"
Name="_mainView"
Title="MainView" Height="350" Width="525">
<Window.DataContext>
<vm:MainViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="102*"/>
<RowDefinition Height="217*"/>
</Grid.RowDefinitions>
<local:ucMyUserControlView HorizontalAlignment="Center" Margin="0,0,0,0" Grid.Row="1" VerticalAlignment="Center"
SelectedDate="{Binding ElementName=_mainView, Path=DataContext.SelectedDateInUserControl, Mode=TwoWay}"/>
<TextBox x:Name="txtSelectedDateInUserControl" Text="{Binding SelectedDateInUserControl}" HorizontalAlignment="Left" Height="23" Margin="10,35,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<Label x:Name="lblSelectedDate" Content="SelectedDate in UserControl" HorizontalAlignment="Left" Margin="10,0,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="txtSelectedDateToUserControl" HorizontalAlignment="Right" Height="23" Margin="0,35,5,0" TextWrapping="Wrap" Text="{Binding SelectedDateToUserControl, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120"/>
<Label x:Name="lblSeelectedDateToUserControl" Content="Change date on user control" HorizontalAlignment="Right" Margin="0,0,5,0" VerticalAlignment="Top"/>
</Grid>
</Window>
The code of the main view model:
using System;
namespace UserControlMvvm
{
class MainViewModel : ViewModelBase
{
#region properties
private DateTime? _selectedDateInUserControl;
public DateTime? SelectedDateInUserControl
{
get { return _selectedDateInUserControl; }
set
{
if(_selectedDateInUserControl != value)
{
SetProperty(ref _selectedDateInUserControl, value);
selectedDateInUserControlChanged();
}
}
}
private string _selectedDateInUserControlText;
public string SelectedDateInUserControlText
{
get { return _selectedDateInUserControlText; }
set
{
if (_selectedDateInUserControlText != value)
{
SetProperty(ref _selectedDateInUserControlText, value);
}
}
}
private string _selectedDateToUserControl;
public string SelectedDateToUserControl
{
get { return _selectedDateToUserControl; }
set
{
if (_selectedDateToUserControl != value)
{
SetProperty(ref _selectedDateToUserControl, value);
DateTime miDateParsed;
DateTime.TryParse(value, out miDateParsed);
SelectedDateInUserControl = miDateParsed;
}
}
}
#endregion properties
#region methods
/// <summary>
/// This method is used to do all the tasks needed when the selectedDate in the user control is changed.
/// </summary>
private void selectedDateInUserControlChanged()
{
try
{
//here the code that the main view model has to do when the selected date is changed in the user control.
}
catch
{
throw;
}
}//selectedDateInUserControlChanged
#endregion methods
}
}
The XAML of the user control:
<UserControl x:Class="UserControlMvvm.ucMyUserControlView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:UserControlMvvm"
mc:Ignorable="d"
Name="_ucMyUserControl"
Width="Auto" Height="Auto">
<Grid>
<Calendar HorizontalAlignment="Center" Margin="0,0,0,0" VerticalAlignment="Center"
SelectedDate="{Binding ElementName=_ucMyUserControl,Path=DataContext.SelectedDate, Mode=TwoWay}"/>
</Grid>
</UserControl>
The code behind of the user control, just to declare the dependency properties and notify the changes to and from the view model. The logic is in the view model.
using System.Windows.Controls;
using System;
using System.Windows;
namespace UserControlMvvm
{
/// <summary>
/// Interaction logic for ucMyUserControl.xaml
/// </summary>
public partial class ucMyUserControlView : UserControl
{
ucMyUserControlViewModel _viewModel;
public ucMyUserControlView()
{
InitializeComponent();
//The view model is needed to be instantiate here, not in the XAML, because if not, you will get a null reference exception
//because you try to access to a property when the view model is not still instantiate.
_viewModel = new ucMyUserControlViewModel();
DataContext = _viewModel;
//Events
_viewModel.SelectedDateChangedEvent += selectedDateChanged;
}
#region dependency properties
//This are the properties that the main view will have available when will use the user control, so dependency properties are the
//communication way between the main view and the user control.
//So here you have to declare all the properties that you want to expose to outside, to the main view.
public static readonly DependencyProperty SelectedDateProperty =
DependencyProperty.Register("SelectedDate", typeof(DateTime?),
typeof(ucMyUserControlView), new PropertyMetadata(null, selectedDateChanged));
public DateTime? SelectedDate
{
get
{
return (DateTime?)GetValue(SelectedDateProperty);
}
set
{
//This is the way in which the user control notify to the main view that the selected date is changed.
SetValue(SelectedDateProperty, value);
}
}
private static void selectedDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//This is the way in which the code behind notify to the view model that the main view has changed by the main view.
((ucMyUserControlView)d)._viewModel.SelectedDate = e.NewValue as DateTime?;
}
#endregion dependency properties
#region methods to receive notifications from the view model
//These are the methods that are subcribed to the events of the view model, to know when a property has changed in the view
//model and be able to notify to the main view.
private void selectedDateChanged(DateTime? paramSelectedDate)
{
try
{
//This update the dependency property, so this notify to the main main that the selected date has been changed in the
//user control.
SetValue(SelectedDateProperty, paramSelectedDate);
}
catch
{
throw;
}
}//selectedChanged
#endregion methods to receive notificactions from the view model
}
}
The view model of the user control:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UserControlMvvm
{
class ucMyUserControlViewModel : ViewModelBase
{
#region events
//The events are user to notify changes from the properties in this view model to the code behind of the user control, so
//later the user control can notify the changes from the code behind to the main view.
//This is because the user control only can notify changes to the main view from the dependency properties and the dependency properties
//are declared in the code behind. So to communicate changes from the view model to the code behind it is needed the use of an event.
//So the changes are notify in this way:
//view model --> code behind --> main view
public delegate void SelectedDateChangedEventHandler(DateTime? paramSelectedDate);
public event SelectedDateChangedEventHandler SelectedDateChangedEvent;
private void OnSelectedDateChanged(DateTime? paramTipoSeleccionado)
{
try
{
//Here notify to the code behind of the user control that the selectedDate is changed.
SelectedDateChangedEvent?.Invoke(paramTipoSeleccionado);
}
catch
{
throw;
}
}//OnSelectedDateChanged
#endregion events
#region properties
private DateTime? _selectedDate;
public DateTime? SelectedDate
{
get { return _selectedDate; }
set
{
if(_selectedDate != value)
{
SetProperty(ref _selectedDate, value);
selectedDateChanged();
OnSelectedDateChanged(SelectedDate);
}
}
}
#endregion properties
#region methods
private void selectedDateChanged()
{
try
{
//Here the code that the user control has to execute when the selectedDate is changed.
}//try
catch
{
throw;
}
}
#endregion methods
}
}
Finally, the class that implements the INotifyPropertyChanged, that can be any implementation that you prefer, but perhaps can be interesting for someone:
/*
* Class that implements the INotifyPropertyChanged that it is used by all view models to
* notifiy changes in their properties to the view.
*/
using System.Runtime.CompilerServices;
using System.ComponentModel;
namespace UserControlMvvm
{
public class ViewModelBase : INotifyPropertyChanged
{
protected virtual void SetProperty<T>(ref T member, T val,
[CallerMemberName] string propertyName = null)
{
if (object.Equals(member, val)) return;
member = val;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
}
With this solution, we can see that if I change a date in the calendar, the first text box in the main view is updated to, but not the second text box because is not binded to the user control.
If I change the date in the first text box in the main view, the selected day in the calendar of the user control is updated too, but not the second text box.
If I change the date in the second text box, it is changed in the calendar beccause I update the selectedItemInUserControl property of the view model, and this property notify to the user control that changes inthe calendar.
So with this solution, I can have a MVVM patter inside the user control, that just expose dependency properties to communicate with the main view.
Yes. If you use a framework that uses a navigation system to move between View/ViewModels, you could adapt this to launch your UserControl View/ViewModel. It doesn't matter if the view is a Window or UserControl.
EDIT
It is also possible to use a Messenger system (again available in most MVVM frameworks) to pass information between view models, so when a property in the control's ViewModel changes, it can send a message to the main ViewModel to change its property(s)
I want to use MVVM approach to achieve something like below:
I have a MainWindow where i have a 3 Buttons like: a)Customers b) Orders c) Sales
By clicking on button, it should open its respective window/usercontrol xaml with customers details,orders details,sales details.
I have tried everything but culdnt able to do so.
How to achieve this using MVVM pattern. Kindly provide the solution?
Thanks
The answer depends on how you want your Customers, Orders and Sales views displayed. If you want them displayed in the same view, simply add a content control bound to a property in your main ViewModel.
For example, if you're using the MVVM Light Toolkit, your MainPage.xaml might look like...
<UserControl x:Class="MvvmLight2.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="300"
Width="300"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Button Content="Customers" Command="{Binding DisplayView}" CommandParameter="Customers" Margin="10" />
<Button Content="Orders" Command="{Binding DisplayView}" CommandParameter="Orders" Margin="10" />
<Button Content="Sales" Command="{Binding DisplayView}" CommandParameter="Sales" Margin="10" />
</StackPanel>
<ContentControl Content="{Binding CurrentView}" IsTabStop="False" Margin="10" />
</StackPanel>
</UserControl>
And your MainPageViewModel would be...
using System.Windows.Controls;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
namespace MvvmLight2.ViewModel
{
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
DisplayView = new RelayCommand<string>(DisplayViewCommandExecute);
}
#region Commands
public RelayCommand<string> DisplayView { get; private set; }
#endregion
#region CurrentView Property
public const string CurrentViewPropertyName = "CurrentView";
private UserControl _currentView;
public UserControl CurrentView
{
get { return _currentView; }
set
{
if (_currentView == value)
return;
_currentView = value;
RaisePropertyChanged(CurrentViewPropertyName);
}
}
#endregion
private void DisplayViewCommandExecute(string viewName)
{
switch (viewName)
{
case "Customers":
CurrentView = new CustomersView();
break;
case "Orders":
CurrentView = new OrdersView();
break;
case "Sales":
CurrentView = new SalesView();
break;
}
}
}
}
This all assumes that you have created views and view models for Customers, Orders, and Sales, and modified the ViewModelLocator to include them.
At this point, if you need to display specific information in your child views, you can create a dependency property in them, and set that from your MainViewModel before you display the view.
You may want to look into the mediator pattern . Common implementations are the Messenger class in the MVVM Light Toolkit and Event Aggregation in PRISM.
One basic workflow using this pattern... Command is called on viewmodel1. Viewmodel1 registers some message with the mediator. Viewmodel2 subscribes to that message and does something in response (like creates new view2 or changes visual state of the view2).
I tried this using Sliverlight Naviagtion Application and MVVM
http://garfoot.com/blog/2010/09/silverlight-navigation-with-the-mvvm-pattern/
Pretty simple example. No frameworks involved as such.
But using a MVVM framework makes life easier for future use.
For MVVM and Prism framework check this link..
http://blog.roboblob.com/2010/10/24/introducing-prism-navigation-framework-for-silverlight-mvvm-applications/