MVVM Creating the ViewModel - c#

Can somebody explain to me how exactly to create a ViewModel for the MVVM Pattern.
I tried to understand the the tutorial here: http://msdn.microsoft.com/en-us/magazine/dd419663.aspx , but I was unable to understand what exactly is happening in the code.
Let's say we want to create a basic application about getting and adding people from and to a local database and displaying them in the View. How should the ViewModel look like and how to create the RelayCommands for it. First why do we set the variables twice: once privately and then again publicaly.
EDIT: Thanks for the help so far. I have one more thing that I don't know to do - how to bind the View to the ViewModel and Vice Versa
Here is the Model:
public class Student : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private string name;
private string surname;
private string age;
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged("Name");
}
}
public string Surname
{
get
{
return surname;
}
set
{
surname = value;
OnPropertyChanged("Surname");
}
}
public string Age
{
get
{
return age;
}
set
{
age = value;
OnPropertyChanged("Age");
}
}
}
and here is the ViewModel:
public class MainViewModel : ViewModelBase
{
ObservableCollection<Student> studentList;
Student selectedPerson;
public MainViewModel()
{
//populate some sample data
studentList = new ObservableCollection<Student>()
{
new Student(){Name="John", Surname="Smith", Age="28"},
new Student(){Name="Barbara", Surname="Anderson", Age="23"}
};
}
public ObservableCollection<Student> StudentList
{
get { return studentList; }
}
public Student SelectedPerson
{
get { return selectedPerson; }
set
{
selectedPerson = value;
RaisePropertyChanged("SelectedPerson");
}
}
private RelayCommand _addStudentCommand;
public ICommand AddStudentCommand
{
get
{
return _addStudentCommand
?? (_addStudentCommand = new RelayCommand(() =>
{
Student student = new Student();
studentList.Add(student);
}));
}
}
}
I have found a way to bind the ViewModel to the View using some code for the view in Csharp but the question how to bind the View to the ViewModel is still on my mind. To be more specific how to create a new student using the values a user has entered in the View.
Here is the View's XAML code
<Window x:Class="MVVMLight.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
SizeToContent="WidthAndHeight">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="NameTextBlock"
Text="Name"
Style="{StaticResource TextBlockTextStyle}"/>
<TextBlock x:Name="SurnameTextBlock"
Grid.Row="1"
Text="Surname"
Style="{StaticResource TextBlockTextStyle}"/>
<TextBlock x:Name="AgeTextBlock"
Grid.Row="2"
Text="Age"
Style="{StaticResource TextBlockTextStyle}"/>
<TextBox x:Name="NameTextBox"
Grid.Column="1"
Style="{StaticResource TextBoxTextStyle}"/>
<TextBox x:Name="SurnameTextBox"
Grid.Row="1"
Grid.Column="1"
Style="{StaticResource TextBoxTextStyle}"/>
<TextBox x:Name="AgeTextBox"
Grid.Row="2"
Grid.Column="1"
Style="{StaticResource TextBoxTextStyle}"/>
<ListBox x:Name="StudentListBox"
Grid.ColumnSpan="2"
Grid.Row="4"
Style="{StaticResource ListBoxStyle}"
ItemsSource="{Binding StudentList}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}"
Style="{StaticResource TextBlockTextStyle}"/>
<TextBlock Text="{Binding Surname}"
Grid.Column="1"
Style="{StaticResource TextBlockTextStyle}"/>
<TextBlock Text="{Binding Age}"
Grid.Column="2"
Style="{StaticResource TextBlockTextStyle}"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Button x:Name="AddButton"
Grid.Row="7"
Grid.ColumnSpan="2"
HorizontalAlignment="Center"
Content="Add"
Margin="7,7,7,7"
Command="{Binding AddStudentCommand}"/>
</Grid>
And here is the View's Csharp code
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
I have some questions concerning the Binding between the View and The ViewModel:
What are the pros and cons of using this type of binding?
What is the best way of binding if I am going to use a database?
Is this how the ViewModel and Model should look like
How to create a RelayCommand for adding a student to the ObservableCollection
Why do we set things first privately and then again publically [Answered]
How to bind the View to the ViewModel and Vice Versa

in your property setters you should check to see if the new value is equal to the old value, if it is you should return and not fire the PropertyChanged event.
As for your questions:
Yes this looks fine.
There are a couple of ways to setup your relay commands. I prefer
private RelayCommand<Student> _addStudentCommand;
public ICommand AddStudentCommand
{
get
{
return _addStudentCommand
?? (_addStudentCommand = new RelayCommand<Student>((student) =>
{
studentList.Add(student);
}));
}
}
another way without passing in a student object
private RelayCommand _addStudentCommand;
public ICommand AddStudentCommand
{
get
{
return _addStudentCommand
?? (_addStudentCommand = new RelayCommand(() =>
{
Student student = new Student();
studentList.Add(student);
}));
}
}
That is how properties work in .net, You could use automatic properties, but since you need to fire change notification in the setter you have to declare the field that the property will work against.
Also since it looks like you are using mvvm light you should try the code snippets. They make properties very easy to create. type mvvvminpc then hit tab twice. then fill in the highlighted part and hit tab till you are finished.
You can bind the View To the Viewmodel a couple of ways. I know that it is an Antipattern but you could use a locator. The basic idea is to set the viewmodel as the views datacontext.
public class Locator
{
public Viewmodel1 Viewmodel1
{
return new Viewmodel1();
}
}
You then in you app.xaml you add this class
<Application.Resources>
<Locator x:key="VMLocator" />
</Application.Resources>
Then in your view in the xaml
<Page DataContext="{Binding Source="{StaticResource VMLocator}" Path=ViewModel1}">
</Page>

Related

Input to and Output from User Control in WPF

I made this minimalistic project to learn output and input with user control and it's working as intended. I want to ask, is this a good approach or is there something which is not necessary?
I also want to post this, because there is tons of post with specific user cases, but not one with a simple example to learn binding mechanics.
Main Window:
<Window x:Class="OutputFromUserControl.View.OutputFromUserControlWindow"
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:OutputFromUserControl.View"
xmlns:uc="clr-namespace:OutputFromUserControl.View.Controls"
xmlns:vm="clr-namespace:OutputFromUserControl.ViewModel"
mc:Ignorable="d"
Title="Output From User Control" Height="450" Width="800">
<Window.DataContext>
<vm:MainVM x:Name="MainVM"/>
</Window.DataContext>
<StackPanel HorizontalAlignment="Left">
<Label Content="Form elements:"/>
<Border CornerRadius="5" BorderBrush="Blue" BorderThickness="1">
<Grid HorizontalAlignment="Left" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Content="Name Input: " Grid.Row="0" Grid.Column="0"/>
<TextBox Grid.Row="0" Grid.Column="1"
Text="{Binding NameInput, UpdateSourceTrigger=PropertyChanged}"
Width="200"
/>
<Label Content="Surname Input: " Grid.Row="1" Grid.Column="0"/>
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding SurnameInput, UpdateSourceTrigger=PropertyChanged}"
Width="200"
/>
<Label Content="Name Output from Control: " Grid.Row="2" Grid.Column="0"/>
<TextBlock Grid.Row="2" Grid.Column="1"
Text="{Binding FullName}"
Width="200"
/>
</Grid>
</Border>
<Label Content="User Control:" Margin="0,10,0,0"/>
<Border CornerRadius="5" BorderBrush="Red" BorderThickness="1">
<uc:NameConcatControl x:Name="NameUC"
NameInput="{Binding NameInput}"
SurnameInput="{Binding SurnameInput}"
NameOutput="{Binding FullName, Mode=TwoWay}"
/>
</Border>
</StackPanel>
</Window>
MainVM:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
namespace OutputFromUserControl.ViewModel
{
public class MainVM : INotifyPropertyChanged
{
private string nameInput;
public string NameInput {
get { return nameInput; }
set
{
nameInput = value;
OnPropertyChanged(nameof(NameInput));
}
}
private string surnameInput;
public string SurnameInput {
get { return surnameInput; }
set {
surnameInput = value;
OnPropertyChanged(nameof(SurnameInput));
}
}
private string fullName;
public string FullName {
get { return fullName; }
set {
fullName = value;
OnPropertyChanged(nameof(FullName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Control xaml:
<UserControl x:Class="OutputFromUserControl.View.Controls.NameConcatControl"
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:OutputFromUserControl.View.Controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Content="Name Input: " Grid.Row="0" Grid.Column="0"/>
<TextBlock Grid.Row="0" Grid.Column="1"
Text="{Binding NameInput}"
x:Name="NameInputTextBlock"
/>
<Label Content="Surname Input: " Grid.Row="1" Grid.Column="0"/>
<TextBlock Grid.Row="1" Grid.Column="1"
Text="{Binding SurnameInput}"
x:Name="SurnameInputTextBlock"
/>
<Label Content="Name Output: " Grid.Row="2" Grid.Column="0"/>
<TextBlock Grid.Row="2" Grid.Column="1"
Text="{Binding NameOutput}"
x:Name="OutputNameTextBlock"
/>
</Grid>
</UserControl>
User control .cs:
using System.Windows;
using System.Windows.Controls;
namespace OutputFromUserControl.View.Controls
{
/// <summary>
/// Interaction logic for NameConcatControl.xaml
/// </summary>
public partial class NameConcatControl : UserControl
{
public string NameInput {
get { return (string)GetValue(NameInputProperty); }
set { SetValue(NameInputProperty, value); }
}
public static string defaultNameInput = "NameInput";
public static readonly DependencyProperty NameInputProperty =
DependencyProperty.Register("NameInput", typeof(string), typeof(NameConcatControl), new PropertyMetadata(defaultNameInput, SetNameOutput));
public string SurnameInput {
get { return (string)GetValue(SurnameInputProperty); }
set { SetValue(SurnameInputProperty, value); }
}
public static string defaultSurnameInput = "Surname Input";
public static readonly DependencyProperty SurnameInputProperty =
DependencyProperty.Register("SurnameInput", typeof(string), typeof(NameConcatControl), new PropertyMetadata(defaultSurnameInput, SetNameOutput));
public string NameOutput {
get { return (string)GetValue(NameOutputProperty); }
set { SetValue(NameOutputProperty, value); }
}
public static string defaultNameOutput = "Name Output";
public static readonly DependencyProperty NameOutputProperty =
DependencyProperty.Register("NameOutput", typeof(string), typeof(NameConcatControl), new PropertyMetadata(defaultNameOutput));
private static void SetNameOutput(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
NameConcatControl control = (NameConcatControl)d;
string nameInput = "";
string surnameInput = "";
if(e.Property.Name == "NameInput")
{
string newValue = (string)e.NewValue;
nameInput = string.IsNullOrEmpty(newValue) ? "" : newValue;
}
else
{
nameInput = string.IsNullOrEmpty(control.NameInputTextBlock.Text)
? ""
: control.NameInputTextBlock.Text;
}
if(e.Property.Name == "SurnameInput")
{
string newValue = (string)e.NewValue;
surnameInput = string.IsNullOrEmpty(newValue) ? "" : newValue;
}
else
{
surnameInput = string.IsNullOrEmpty(control.SurnameInputTextBlock.Text)
? ""
: control.SurnameInputTextBlock.Text;
}
string fullName = $"{nameInput} {surnameInput}";
control.OutputNameTextBlock.Text = fullName;
control.NameOutput = fullName;
}
public NameConcatControl()
{
InitializeComponent();
}
}
}
This question has a very wide answers. Different people with different approaches can use for their applications.
But we always follow one common formula.
Each view - will have its own view model. (Again in this approach, someone might say might not be true all the time).
From your code (xaml and code), below are my observations.
<Window.DataContext>
<vm:MainVM x:Name="MainVM"/>
</Window.DataContext>
I generally don't like setting data context in xaml. Instead I prefer to set it on the code-behind (mostly from constructor)
Instead of creating a dependency properties in user control and bind the MainVM properties to the dependency properties of User control.
I prefer to do it this way.
I prefer to create a separate UserControlViewModel.cs and add required properties to it.
public class UserControlViewModel : INotifyPropertyChanged
{
private string nameInput;
public string NameInput {
get { return nameInput; }
set
{
nameInput = value;
OnPropertyChanged(nameof(NameInput));
}
}
private string surnameInput;
public string SurnameInput {
get { return surnameInput; }
set {
surnameInput = value;
OnPropertyChanged(nameof(SurnameInput));
}
}
private string fullName;
public string FullName {
get { return fullName; }
set {
fullName = value;
OnPropertyChanged(nameof(FullName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Then I prefer to add this as a property in MainVM.cs
public class MainVM : INotifyPropertyChanged
{
private UserControlViewModel _userControlViewModel;
public UserControlViewModel UserControlViewModel
{
get { return _userControlViewModel; }
set
{
_userControlViewModel = value;
OnPropertyChanged(nameof(UserControlViewModel));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
// Rest of your code
// You don't need existing properties any more here.
// If you want to access these properties from MainVM then use the UserControlViewModel property and access the members of it.
}
Then I prefer to set the data-context of my UserControl to this property like below in my MainWindow.xaml
<uc:NameConcatControl x:Name="NameUC" ="{Binding UserControlViewModel}" />
My usercontrol contorl binding's still remain same as the property names are same and we moved to UserControlViewModel.cs
Now you can remove all dependency properties from code behind of UserControl.xaml.cs
Note :- As I stated at the beginning of my answer, this question has wide area for answers and there are lot of possibilities to answer this question.
I hope I have tried to give you some inputs from my end. I guess this should give you some idea to develop rest..
You can try making those changes and let me know in case if you face any errors or binding issues.
Assuming you just want the full-name view to be something like "Surname, Name", you could actually remove the FullName property from your view model, and just use a MultiBinding (btw the StringFormat property can be used with both MultiBindings and regular Bindings, its pretty nifty if you aren't familiar with it).
As for the Labels, it's good to make a habit of using the simplest control required to get the job done, and in this case, TextBlocks would do just fine, since you don't appear to be using any of the extended functionality the Label offers (i.e., BorderBrush, Padding, ContentTemplate, etc.).
You don't generally need to create your own dependency properties in UserControl derived classes, since they are usually designed with a particular viewmodel in mind. They are more useful when the view is independent from the viewmodel, and the dependency properties serve as an api, through which other controls/viewmodels can interact with it.
<Window x:Class="OutputFromUserControl.View.OutputFromUserControlWindow"
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:OutputFromUserControl.View"
xmlns:uc="clr-namespace:OutputFromUserControl.View.Controls"
xmlns:vm="clr-namespace:OutputFromUserControl.ViewModel"
mc:Ignorable="d"
Title="Output From User Control" Height="450" Width="800">
<Window.DataContext>
<vm:MainVM x:Name="MainVM"/>
</Window.DataContext>
<StackPanel HorizontalAlignment="Left">
<Label Content="Form elements:"/>
<Border CornerRadius="5" BorderBrush="Blue" BorderThickness="1">
<Grid HorizontalAlignment="Left" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<TextBlock Text="Name Input:" Grid.Row="0" Grid.Column="0"/>
<TextBox Grid.Row="0" Grid.Column="1"
Text="{Binding NameInput, UpdateSourceTrigger=PropertyChanged}"
Width="200"
/>
<TextBlock Text="Surname Input:" Grid.Row="1" Grid.Column="0"/>
<TextBox Grid.Row="1" Grid.Column="1"
Text="{Binding SurnameInput, UpdateSourceTrigger=PropertyChanged}"
Width="200"
/>
<TextBlock Text="Name Output from Control:" Grid.Row="2" Grid.Column="0"/>
<TextBlock Grid.Row="2" Grid.Column="1" Width="200">
<MultiBinding StringFormat="{}{0}, {1}">
<Binding Path="SurnameInput"/>
<Binding Path="NameInput"/>
</MultiBinding>
</TextBlock>
</Grid>
</Border>
<Label Content="User Control:" Margin="0,10,0,0"/>
<Border CornerRadius="5" BorderBrush="Red" BorderThickness="1">
<uc:NameConcatControl x:Name="NameUC"
NameInput="{Binding NameInput}"
SurnameInput="{Binding SurnameInput}"
NameOutput="{Binding FullName}"
/>
</Border>
</StackPanel>

Should these members be in a model? and if so, how?

I'm new to WPF and Prism, so, I'm trying to figure very basic things out at the moment. My little experiment looks like this when running:
I have a Views\Registration.xaml that looks like this:
<UserControl x:Class="Configurator.Views.Registration"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Label Grid.Column="0" Grid.Row="0" Content="Email:" HorizontalContentAlignment="Right" Margin="6"/>
<TextBox Grid.Column="1" Grid.Row="0" x:Name="email" Margin="6" Text="{Binding Email}"/>
<Label Grid.Column="0" Grid.Row="1" Content="Password:" HorizontalContentAlignment="Right" Margin="6"/>
<PasswordBox Grid.Column="1" Grid.Row="1" x:Name="password" Margin="6" />
<Label Grid.Column="0" Grid.Row="2" Content="Password Confirmation:" HorizontalContentAlignment="Right" Margin="6"/>
<PasswordBox Grid.Column="1" Grid.Row="2" x:Name="passwordConfirmation" Margin="6"/>
<Button Grid.Column="1" Grid.Row="3" Content="Register" HorizontalAlignment="Left" Margin="6" Padding="6,2" Command="{Binding RegisterCommand}"/>
</Grid>
</UserControl>
and then a ViewModels\RegistrationViewModel.cs that looks like this:
namespace Configurator.ViewModels {
public class RegistrationViewModel : BindableBase {
private string _email;
public string Email {
get { return _email; }
set { SetProperty(ref _email, value); }
}
private string _password;
public string Password {
get { return _password; }
set { SetProperty(ref _password, value); }
}
private string _passwordConfirmation;
public string PasswordConfirmation {
get { return _passwordConfirmation; }
set { SetProperty(ref _passwordConfirmation, value); }
}
public DelegateCommand RegisterCommand { get; private set; }
public RegistrationViewModel() {
Console.WriteLine("RegistrationViewModel");
RegisterCommand = new DelegateCommand(Register);
}
private void Register() {
Console.WriteLine("Registering");
Console.WriteLine($"Email: {Email}");
Console.WriteLine($"Password: {Password}");
Console.WriteLine($"Password Confirmation: {PasswordConfirmation}");
}
}
}
Should the Email, Password and PasswordConfirmation go into a model when following MVVM? If it should go into a model, how is the wiring done? I can't find any examples.
Your implementation looks all good to me, i.e. you bind to properties of a view model.
Sometimes people tend to refer to view models as "models" but the actual model would in this case be represented by a service that performs the actual registration.
You could inject you view model with an interface that this service implements and then call a method of the service through this interface in your Register() method, e.g.:
public class RegistrationViewModel : BindableBase
{
...
private readonly IRegistrationService _registrationService;
public RegistrationViewModel(IRegistrationService registrationService)
{
_registrationService = registrationService ?? throw new ArgumentNullException(nameof(registrationService));
RegisterCommand = new DelegateCommand(Register);
}
private void Register()
{
_registrationService.Register(...);
}
}
Since you are using MVVM, the properties should be in a Model.
You then create a property of that Model in your ViewModel make it call the PropertyChanged-Event from INotifyPropertyChanged.
In the View you then bind the Element-Names of the Model-Property from your ViewModel.
You then just should decide wheter you make your Model implement INotifyPropertyChanged as well or you find another way.
Your ViewModel and your XAML are fine. Yes, you should expose the Email, Password etc. as part of the ViewModel.
The missing part in your example is the binding of the ViewModel to the UI. It is possible to achieve this by declaring it in your XAML, or in the constructor in the code-behind page you could have
public Registration(RegistrationViewModel model)
{
DataContext = model;
}
What wiring are you enquiring about because it all looks pretty good to me.

Struggling to pick up MVVM Basics

I am brand new to MVVM (some experience with WPF) and I am getting really confused with regards to what I assume as the basics. I'm trying to make a simple registration form. The user enters their name, a username and a password. For the sake of learning MVVM and not over-complicating things, the only check I am doing on the password is if it contains an upper case letter. No hashing, encryption etc.. for now.
So I have a model that is a User, generated from entity framework. Here is my first bit of confusion. It looks like so:
public partial class User : INotifyPropertyChanged
{
public short Id { get; set; }
public string LastName { get; set; }
public string Username { get; set; }
public string Password { get; set; }
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (_firstName == value)
{
return;
}
_firstName = value;
OnPropertyChanged("FirstName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
As you can see, it's in a little bit of a mismatch. The first section was generated by Entity, which I have modified with regards to the FirstName to look like what I believe is more MVVM. Is the Model the location where I create get/set, do they also need to be in the ViewModel?
Moving onto my ViewModel, I am confused as to whether or not I need to recreate the properties of the User (FirstName, LastName ....) again, or are they accessible through the Model. Can I create a User in the ViewModel that exposes all of the properties of a User? Here is the code for my ViewModel so far:
internal class NewUserViewModel : BaseViewModel
{
private User _newUser;
public User NewUser
{
get => _newUser;
set
{
if (_newUser == value)
{
return;
}
_newUser = value;
OnPropertyChanged("NewUser");
}
}
private string _password;
public string Password
{
get => _password;
set
{
if (_password == value)
return;
_password = value;
OnPasswordChanged();
OnPropertyChanged("Password");
}
}
#region RegisterCommand
private DelegateCommand _registerCommand;
public ICommand RegisterCommand
{
get
{
_registerCommand = new DelegateCommand(param => Register(), param => CanRegister());
return _registerCommand;
}
}
private bool CanRegister()
{
return _isPasswordValid;
}
private bool _isPasswordValid;
public void OnPasswordChanged()
{
_isPasswordValid = Password.Any(char.IsUpper);
}
private void Register()
{
using (var context = new WorkstreamContext())
{
var users = context.Set<User>();
users.Add(_newUser);
context.SaveChanges();
}
}
#endregion
}
As of now I have recreated the Password property so I can access it, however this settles uneasily for me and I feel like I either expose all the properties or use NewUser, however I am unsure about how to do this. Currently the code half works. The Save button is grayed out, however it does not become enabled when the password contains an uppercase letter which is what I would expect. The actual registration form:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Image Source="pack://application:,,,/Resources/NewUserForm/NewUser.jpg" HorizontalAlignment="Center" VerticalAlignment="Center" Height="100"/>
<TextBlock Grid.Row="1" Text="Please Enter Your Details" HorizontalAlignment="Center" Foreground="DarkSlateGray" FontSize="16"/>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Height="{Binding ActualHeight, ElementName=FirstNameTextBox}" Source="pack://application:,,,/Resources/NewUserForm/FirstName.jpg" Margin="5,5,0,5"/>
<xctk:WatermarkTextBox Grid.Column="1" x:Name="FirstNameTextBox" Watermark="first name" Text="{Binding NewUser.FirstName, Mode=TwoWay}" />
</Grid>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Height="{Binding ActualHeight, ElementName=LastNameTextBox}" Source="pack://application:,,,/Resources/NewUserForm/LastName.jpg" Margin="5,5,0,5"/>
<xctk:WatermarkTextBox Grid.Column="1" x:Name="LastNameTextBox" Watermark="last name" Text="{Binding NewUser.LastName, Mode=TwoWay}" />
</Grid>
<Grid Grid.Row="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Height="{Binding ActualHeight, ElementName=UsernameTextBox}" Source="pack://application:,,,/Resources/NewUserForm/User.jpg" Margin="5,5,0,5"/>
<xctk:WatermarkTextBox Grid.Column="1" x:Name="UsernameTextBox" Watermark="username" Text="{Binding NewUser.Username, Mode=TwoWay}" />
</Grid>
<Grid Grid.Row="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Height="{Binding ActualHeight, ElementName=PasswordTextBox}" Source="pack://application:,,,/Resources/NewUserForm/Password.jpg" Margin="5,5,0,5"/>
<xctk:WatermarkTextBox Grid.Column="1" x:Name="PasswordTextBox" Watermark="password" Text="{Binding NewUser.Password, Mode=TwoWay}"/>
</Grid>
<DockPanel Grid.Row="6">
<Button Content="Save" Command="{Binding RegisterCommand, Mode=OneWay, Source={StaticResource NewUserViewModel}}" HorizontalAlignment="Right"/>
<Button HorizontalAlignment="Right"/>
</DockPanel>
</Grid>
Is the way that I have bound the TextBoxes to the NewUser exposed on the ViewModel the correct way to operate in MVVM? I appreciate that there are ALOT of tutorials on MVVM, I have read/viewed many. However, I am getting to the stage where I am feeling more and more confused, and would really appreciate if someone would give me a breakdown on my code and pointers on where I am going wrong, why the code is not working and where I can improve.
Your question is very broad. But if the User class implements the INotifyPropertyChanged interface, it is effectively a kind of a view model and you can bind directly to the properties of this one like you are doing:
{Binding NewUser.FirstName}
If NewUser was some kind of DTO object, you could wrap it in your view model and bind to the view model properties:
public string Password
{
get { return _user.Password; }
set { return _user.Password = value; OnNotifyPropertyChanged(); }
}
A real "model" is rather a service or some kind of business logic object.
The Save button is grayed out, however it does not become enabled when the password contains an uppercase letter which is what I would expect.
Does the setter of your Password property even get hit? Bind to the Password property of the view model:
Text="{Binding Password}"
...and call the RaiseCanExecuteChanged() of the command to refresh its status:
private string _password;
public string Password
{
get => _password;
set
{
if (_password == value)
return;
_password = value;
OnPasswordChanged();
OnPropertyChanged("Password");
_registerCommand.RaiseCanExecuteChanged(); //<--
}
}

WPF ListBox data binding

New to WPF here. The application being built has a list of users being pulled from a database for display in a "Users" Window, navigable from a "Main" Window. The list seems to be transferred to the code behind, but the list of users isn't displaying in the "Users" Window ListBox. Does anyone see why this isn't displaying? Many thanks in advance!
"Main" Window directing:
UsersViewModel Usersvm = new UsersViewModel();
Usersvm.Users = new List<UserViewModel>();
DbEntities db = new DbEntities();
var pulledUsers = db.uspGetUsers().ToList();
foreach (var result in pulledUsers)
{
var pulledUser = new UserViewModel
{
FirstName = result.FirstName,
LastName = result.LastName,
EMail = result.Email,
UserID = result.UserID,
Position = result.Position,
EndDate = result.EndDate,
};
Usersvm.Users.Add(pulledUser);
}
new UsersWindow(Usersvm).Show();
UsersWindow code behind:
public partial class UsersWindow : Window
{
public UsersWindow(UsersViewModel uvm)
{
InitializeComponent();
listboxUsers.ItemsSource = uvm.Users;
}
}
UsersWindow.xaml:
<Window x:Class="DbEntities.UsersWindow"
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:DbEntities"
mc:Ignorable="d"
Title="UsersWindow" Height="Auto" Width="900">
<Window.Resources>
<Style x:Key="borderBase" TargetType="Border">
<Setter Property="BorderBrush" Value="Black" />
<Setter Property="BorderThickness" Value="1" />
</Style>
</Window.Resources>
<StackPanel>
<TextBlock x:Name="textBlock" Height="21" Margin="0,0,161,0" TextWrapping="Wrap"
Text="Users Page" VerticalAlignment="Top" RenderTransformOrigin="1.022,0.409" HorizontalAlignment="Right" Width="344"/>
<Grid>
<Grid Grid.IsSharedSizeScope="True">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="151*" />
<ColumnDefinition Width="95*" />
<ColumnDefinition Width="110*" />
<ColumnDefinition Width="351*" />
<ColumnDefinition Width="75*" />
<ColumnDefinition Width="110*" />
</Grid.ColumnDefinitions>
<Border Style="{StaticResource borderBase}">
<TextBlock HorizontalAlignment="Center" Text="Last Name" />
</Border>
<Border Grid.Column="1" Style="{StaticResource borderBase}">
<TextBlock HorizontalAlignment="Center" Text="First Name" />
</Border>
<Border Grid.Column="2" Style="{StaticResource borderBase}">
<TextBlock HorizontalAlignment="Center" Text="Position" />
</Border>
<Border Grid.Column="3" Style="{StaticResource borderBase}">
<TextBlock HorizontalAlignment="Center" Text="Email" />
</Border>
<Border Grid.Column="4" Style="{StaticResource borderBase}">
<TextBlock HorizontalAlignment="Center" Text="End Date" />
</Border>
<Border Grid.Column="5" Style="{StaticResource borderBase}">
<TextBlock HorizontalAlignment="Center" />
</Border>
<ListBox x:Name="listboxUsers" HorizontalAlignment="Center" Height="Auto" Margin="3,25,0,0" VerticalAlignment="Top" Width="889"
ItemsSource="{Binding Users}" Grid.ColumnSpan="6">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="LastNameColumn" />
</Grid.ColumnDefinitions>
<Border Style="{StaticResource borderBase}">
<TextBlock Text="{Binding LastName}"/>
</Border>
<Border Style="{StaticResource borderBase}">
<TextBlock Text="{Binding FirstName}"/>
</Border>
<Border Style="{StaticResource borderBase}">
<TextBlock Text="{Binding Position}"/>
</Border>
<Border Style="{StaticResource borderBase}">
<TextBlock Text="{Binding Email}"/>
</Border>
<Border Style="{StaticResource borderBase}">
<TextBlock Text="{Binding EndDate}"/>
</Border>
<Border Style="{StaticResource borderBase}">
<Button Content="Edit" x:Name="editButton" Click="editButton_Click"/>
</Border>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Grid>
</Grid>
</StackPanel>
</Window>
And finally, the UsersViewModel, with a list of the user contact information:
public partial class UsersViewModel : Window
{
public List<UserViewModel> Users { get; set; }
}
EDIT (Solved):
Ed Plunkett's comments and answer directly solved the original ListBox question, and using that input combined with ThyArtIsCode's, which was all neatly presented by Monty, the process is much more elegant. Thanks to all who replied - there's a ton of great learning material here.
I see a couple things wrong...
First, your ViewModel is inheriting Window. If there isn't a particular reason for this, get rid of it. If you want to notify UI of changes made to your collection (which should ideally be part of your view model), make the view model inherit INotifyPropertyChanged.
You are also binding to ListBox here:
ItemsSource="{Binding Users}"
AND setting the ItemsSource again here:
listboxUsers.ItemsSource = uvm.Users;
BAD! If you are binding in XAML, there's absolutely no need to set the ItemsSource again. Need to modify the collection? Do so with the collection directly.
Also, since you're new to WPF, I figured I'd add some suggestions that helped me when I first started learning:
If you want things to go quicker, add IsAsync=True to your ListBox binding. This will enable asynchronous binding (amazing, I know).
Virtualize the crap out of that ListBox (simply add following to ListBox):
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling"
And one last thing, though others suggested using an ObservableCollection, it also comes with a performance hit when using large data. Even if you don't intend to have large data, it always safer to use a BindingList anyway. In fact, ObservableCollection has an upper hand when working with smaller data sets.
They are much quicker and share many similar properties as the OC.
You've got a few things to fix here, but nothing very complicated. Just a lot of MVVM/XAML housekeeping stuff.
The way MVVM works in XAML is that your viewmodels don't know about your views --- ideally they don't know about any UI at all. To make that happen with stuff like message boxes and file open dialogs can involve some contortions, but we're not going there right now. Incidentally, you definitely don't want to derive a view model from Window -- that's a UI class, and it doesn't do anything that view models need a base class to do.
So your viewmodels have public properties, which you've got, but when those properties change, they should fire notifications off into the darkness. To do that, you implement INotifyPropertyChanged on your viewmodel, and you fire the PropertyChanged event when a property changes. The UI will subscribe to those notifications -- if your view model is the DataContext of the element whose property is bound (clear as mud -- more on that later).
When a viewmodel exposes a collection, it usually uses ObservableCollection, because that class fires notifications on add/remove/etc. List doesn't do that. ObservableCollection comes with some overhead from all the notification stuff, so don't just use it everywhere -- still use List when all you need is a List.
So UsersViewModel.Users needs to be of type ObservableCollection<UserViewModel><UserViewModel>, and when the collection is replaced, fire PropertyChanged.
private ObservableCollection<UserViewModel> _users =
new ObservableCollection<UserViewModel>();
ObservableCollection<UserViewModel> Users {
get { return _users; }
set {
_users = value;
// Implementations of this are everywhere on Google, very simple.
OnPropertyChanged("Users");
// Or in C#6
//PropertyChanged?.Invoke(new PropertyChangedEventArgs(nameof(Users)));
}
}
And of course make sure UserViewModel also implements INotifyPropertyChnaged and fires notifications when its own property values change.
Next, your XAML binding for ItemsSource on the ListBox is correct, but assigning a collection to that property in code behind will break it. A {Binding ...} in XAML isn't just an assignment: It creates an instance of the Binding class, which sits in the middle and manages all the notification event business I mentioned above. You can create bindings programmatically, but doing it in XAML is much simpler and in 99.5+% of cases does everything you need.
Most importantly, the window needs to know about your viewmodel. Make that happen by assigning an instance of UsersViewModel to the window's DataContext. The window's child controls will inherit that DataContext, and all bindings will be evaluated in that context.
public partial class UsersWindow : Window
{
public UsersWindow(UsersViewModel uvm)
{
InitializeComponent();
var vm = new UsersViewModel();
// initialize vm if needed
DataContext = vm;
}
}
You could have the window's creator pass in a UsersViewModel instance via the window's constructor as well.
OK try this.....
ViewModel....
class Base_ViewModel : INotifyPropertyChanged
{
public RelayCommand<UserViewModel> editButton_Click_Command { get; set; }
public Base_ViewModel()
{
editButton_Click_Command = new RelayCommand<UserViewModel>(OneditButton_Click_Command);
this.Users = new ObservableCollection<UserViewModel>();
this.Users.Add(new UserViewModel() { FirstName = "John", LastName = "Doe", EMail = "JohnDoe#yahoo.com", EndDate = "02-01-2016", Position = "Developer", UserID = "AADD543" });
}
private ObservableCollection<UserViewModel> _Users;
public ObservableCollection<UserViewModel> Users
{
get { return _Users; }
set { _Users = value; NotifyPropertyChanged("Users"); }
}
private void OneditButton_Click_Command(UserViewModel obj)
{ // put a break-point here and you will see the data you want to Edit in obj
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
User Class.....
public class UserViewModel : INotifyPropertyChanged
{
private string _FirstName;
public string FirstName
{
get { return _FirstName; }
set { _FirstName = value; NotifyPropertyChanged("FirstName"); }
}
private string _LastName;
public string LastName
{
get { return _LastName; }
set { _LastName = value; NotifyPropertyChanged("LastName"); }
}
private string _EMail ;
public string EMail
{
get { return _EMail; }
set { _EMail = value; NotifyPropertyChanged("EMail"); }
}
private string _UserID;
public string UserID
{
get { return _UserID; }
set { _UserID = value; NotifyPropertyChanged("UserID"); }
}
private string _Position;
public string Position
{
get { return _Position; }
set { _Position = value; NotifyPropertyChanged("Position"); }
}
private string _EndDate;
public string EndDate
{
get { return _EndDate; }
set { _EndDate = value; NotifyPropertyChanged("EndDate"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
XAML.....
Set the Window x:Name....
<Window x:Name="Base_V"......
DataContext
<Window.DataContext>
<ViewModels:Base_ViewModel/>
</Window.DataContext>
And the rest of the View....
<Grid>
<DataGrid Name="DataGrid1" ItemsSource="{Binding Users}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding DataContext.editButton_Click_Command, ElementName=Base_V}" CommandParameter="{Binding}">Edit</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
you should end up with something like this....
Update 1
In the constructor of the Base_ViewModel
this.Users.Add(new UserViewModel() { FirstName = "John", LastName = "Doe", EMail = "JohnDoe#yahoo.com", EndDate = "02-01-2016", Position = "Developer", UserID = "AADD543" });
this.Users.Add(new UserViewModel() { FirstName = "Fred", LastName = "Doe", EMail = "FredDoe#yahoo.com", EndDate = "02-01-2016", Position = "Developer", UserID = "AADD543" });
// empty record to allow the use to Add a new record
this.Users.Add(new UserViewModel());
When the user selects the Edit button for the empty record they are in effect simply filling in a blank record, Once they have filled that in, make sure to add another blank record to produce a new (empty row) in the DataGrid ....

Define command binding in user control

I wrote user control with 2 buttons and one check box and now I want to bind Commands to data context - for each button and checkbox.
But I don't know how to define command binding. I think I'll need some kind of ICommand property in User control - but how can I connect user's data context command delegate? I want to use user control to manage each item in collection like this:
<ItemsControl ItemsSource="{Binding Path=MoneyInfo}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ChannelSetupControl
CurrentCount="{Binding Count}"
CoinValue="{Binding Value}"
UpCommand="{Binding DataContextUp}"
DownCommand="{Binding DataContextDown}"
ChangeCheckboxCommand="{Binding DataContextChange}"></local:ChannelSetupControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
XAML User control
<UserControl>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="3*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="0" Text="{Binding CoinValue}" TextAlignment="Center"></TextBlock>
<TextBlock Grid.Column="0" Grid.Row="1" Text="{Binding CurrentCount, Mode=TwoWay}" TextAlignment="Center" VerticalAlignment="Center" FontSize="30"></TextBlock>
<StackPanel Grid.Column="1" Grid.Row="1" VerticalAlignment="Center">
<Button Content="+ 10" Padding="0 5"></Button>
<Button Content="- 10" Padding="0 5"></Button>
</StackPanel>
<CheckBox Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" IsChecked="{Binding Cycling, Mode=TwoWay}" Content="recycling" VerticalContentAlignment="Center"></CheckBox>
</Grid>
</UserControl>
and code behind and this is where I'm lost - how to define UpCommand, DownCommand and ChangeCheckboxCommand?
public partial class ChannelSetupControl : UserControl, INotifyPropertyChanged
{
private int currentCount;
private bool cycling;
private double coinValue;
public int Step { get; set; }
public double CoinValue { get { return coinValue; } set { coinValue = value; NotifyPropertyChanged("CoinValue"); } }
public int CurrentCount { get { return currentCount; } set { currentCount = value; NotifyPropertyChanged("CurrentCount"); } }
public bool Cycling { get { return cycling; } set { cycling = value; NotifyPropertyChanged("Cycling"); } }
public ChannelSetupControl()
{
InitializeComponent();
DataContext = this;
CurrentCount = 0;
Step = 10;
Cycling = false;
CoinValue = 0;
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
First of all your ChannelSetupControl class extends UserControl, so it implicitly extends DependencyObject class. It means you can use Dependency Properties instead of implementing INotifyPropertyChanged.
So you can define a dependency property in your ChannelSetupControl class, like this one:
public static readonly DependencyProperty UpCommandProperty =
DependencyProperty.Register("UpCommand", typeof(ICommand), typeof(ChannelSetupControl));
public ICommand UpCommand
{
get { return (ICommand)GetValue(UpCommandProperty); }
set { SetValue(UpCommandProperty, value); }
}
At the same time in your control XAML:
<Button Command="{Binding RelativeSource={RelativeSource Mode=Self}, Path=UpCommand, Mode=OneWay}"
Content="+ 10" Padding="0 5" />
In this way in your window XAML you can wrote:
<local:ChannelSetupControl UpCommand="{Binding UpCommand, Mode=OneWay}" ... />
You can use the same "pattern" for the other controls.
Regarding ICommand, there are a lot of implementations. The one that I prefer is the so called delegate command (for a sample you can take a look here).
I hope this quick explanation can help you.

Categories