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

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.

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>

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(); //<--
}
}

Error in data binding in Xaml

I have tried data binding in WPF.
But it is showing few errors.Please help me.
I am attaching the code.I have create a simple text block and tried to bind the string. Also I want to know how Windows.datacontext works? In my code it is giving an error. please help me out.
Xaml code
<Window x:Class="Shweta.DataBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataBinding" Height="300" Width="300">
<Window.DataContext>
<l:DataBinding />
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="54*" />
<ColumnDefinition Width="224*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="59*" />
<RowDefinition Height="202*" />
</Grid.RowDefinitions>
<Grid Grid.Column="1" Grid.Row="1">
<TextBlock Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="textBlock1" Text="{Binding TextString, TargetNullValue=Test}" VerticalAlignment="Top" Width="68" />
</Grid>
</Grid>
</Window>
**Code behind**
namespace Shweta
{
public partial class DataBinding : Window
{
public DataBinding()
{
InitializeComponent();
Setupviewmodel();
}
private void Setupviewmodel
{
TextString="this worked";
}
public string TextString{get;set;}
}
}
Okay so first of all read the error messages ... It clearly says that l is not defined in XAML but still you're trying to use it : <l:DataBinding />...
Fix this by declaring l in your XAML :
<Window x:Class="Shweta.DataBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:l="<your l declaration"/>
Another thing is that you haven't implemented INotifyPropertyChanged so you'r value wont get updated anyway.
Implement this like such :
public partial class DataBinding : Window, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
void NotifyPropertyChanged([CallerMemberName]string propertyName = "")
{
if ( PropertyChanged != null )
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
string text;
public string TextString
{
get { return text; }
set { text = value; NotifyPropertyChanged(); }
}
public DataBinding()
: base()
{
InitializeComponent();
Setupviewmodel();
// as #Nahuel Ianni stated, he has to set DataContext to CodeBehind
// in order to be able to get bindings work
DataContext = this; // <-- only if not binded before
}
public void Setupviewmodel() // forgot to to place ()
// produced error : `A get or set accessor expected`
{
TextString = "this worked";
}
}
Yet another thing is that you have to specify DataContext only when it's not the same as your code behind so you do not need this part :
<Window.DataContext>
<l:DataBinding />
</Window.DataContext>
You are not specifying the DataContext correctly as you are trying to set it up on XAML by using a namespace that has not been declared. For more info on XAML namespaces, check the following link.
In your example it would be on the xaml side:
<Window x:Class="Shweta.DataBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataBinding" Height="300" Width="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="54*" />
<ColumnDefinition Width="224*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="59*" />
<RowDefinition Height="202*" />
</Grid.RowDefinitions>
<Grid Grid.Column="1" Grid.Row="1">
<TextBlock Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="textBlock1" Text="{Binding TextString, TargetNullValue=Test}" VerticalAlignment="Top" Width="68" />
</Grid>
</Grid>
</Window>
And in your code behind:
namespace Shweta
{
public partial class DataBinding : Window
{
public DataBinding()
{
InitializeComponent();
this.DataContext = this; // Pay attention to this line!
Setupviewmodel();
}
private void Setupviewmodel()
{
TextString="this worked";
}
public string TextString{get;set;}
}
}
The difference with the original version is that I'm not specifying the DataContext on XAML but on the code behind itself.
The DataContext can be considered as the place where the view will retrieve the information from. When in doubt, please refer to this MSDN article or you could learn about the MVVM pattern which is the pillar of working with XAML.
In order to make this work you have to set the DataContext properly. I'd suggest to create a viewmodel class and bind to that. Also I initialized the binding in the codebehind because your namespaces are missing. You can do that in xaml aswell. For now to give you something to work with try this for your codebehind:
public partial class DataBinding : Window
{
public DataBinding()
{
InitializeComponent();
DataContext = new DataBindingViewModel();
}
}
public class DataBindingViewModel
{
public DataBindingViewModel()
{
Setupviewmodel();
}
private void Setupviewmodel()
{
TextString = "this worked";
}
public string TextString { get; set; }
}
And change your view to this:
<Window x:Class="Shweta.DataBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataBinding" Height="300" Width="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="54*" />
<ColumnDefinition Width="224*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="59*" />
<RowDefinition Height="202*" />
</Grid.RowDefinitions>
<Grid Grid.Column="1" Grid.Row="1">
<TextBlock Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="textBlock1" Text="{Binding TextString, TargetNullValue=Test}" VerticalAlignment="Top" Width="68" />
</Grid>
</Grid>
Note that the Text property will only be set at initialization. If you want DataBinding at runtime your DataBindingViewModel will have to implememnt INPC and throw the PropertyChanged Event after setting the property bound to.

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.

MVVM Creating the ViewModel

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>

Categories