I have a small program to test out textbox databinding and it works when I change the value in the textbox itself, but when I try to change the value from the code-behind, the variable is updated but the Textbox is not updated. I have looked around but have not been able to find a solution, as most I have seen are for the textbox updating the variable, which is opposite of what I need. Here is the code:
XAML:
<Grid>
<TextBox Text="{Binding FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="127,37,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBox Text="{Binding LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="23" Margin="127,88,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120"/>
<TextBlock HorizontalAlignment="Left" Margin="28,40,0,0" TextWrapping="Wrap" Text="First Name" VerticalAlignment="Top"/>
<TextBlock HorizontalAlignment="Left" Margin="28,91,0,0" TextWrapping="Wrap" Text="Last Name" VerticalAlignment="Top"/>
<Button x:Name="Name" Content="Name!" HorizontalAlignment="Left" Margin="145,212,0,0" VerticalAlignment="Top" Width="75" Click="Name_Click"/>
</Grid>
And My Code:
namespace WpfApplication1
{
public class UIControls : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _firstName;
private string _lastName;
protected void Notify(string propertyName)
{
if(this.PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public string FirstName
{
get { return _firstName; }
set
{
if (value != _firstName)
{
_firstName = value;
Notify("FirstName");
}
}
}
public string LastName
{
get { return _lastName; }
set
{
if (value != _lastName)
{
_lastName = value;
Notify("LastName");
}
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
UIControls viewModel = new UIControls();
this.DataContext = viewModel;
}
private void Name_Click(object sender, RoutedEventArgs e)
{
UIControls ui = new UIControls();
ui.FirstName = "Mike";
ui.LastName = "Smith";
}
}
}
Well you are creating new UIControls on every button click. New UIControls is not binded to text box. Try to create private filed that is binded to MainWindow, something like this:
public partial class MainWindow : Window
{
private UIControls viewModel;
public MainWindow()
{
InitializeComponent();
viewModel = new UIControls();
this.DataContext = viewModel;
}
private void Name_Click(object sender, RoutedEventArgs e)
{
viewModel.FirstName = "Mike";
viewModel.LastName = "Smith";
}
Related
I'm making an input page and I'm trying to implement a reset button. After a click on the button, the UI should be empty again.
I thought that entering an empty string would deal with this. In the code it seems to work and the value does get changed to "" but in the UI the typed text stays visible (so it doesn't show the empty "" string). I also tried with string.Empty as suggested in here but that also doesn't seem to work.
Am I missing something here? I'm kinda new to programming so if I did something horribly wrong, don't laugh too hard ;)
I'm using an MVVM pattern and Fody Weaver to deal with the property changed part of the code.
The UI / XAML
<TextBlock Text="Naam:"
Grid.Column="0"
Style="{StaticResource InputInputBlock}"
/>
<TextBox Foreground="White"
Grid.Column="1"
Text="{Binding Name, Mode=TwoWay}"
Style="{StaticResource InputInputBox}"
/>
<Button Content="Reset"
Height="50"
Width="150"
Grid.Column="0"
Grid.Row="2"
VerticalAlignment="Top"
HorizontalAlignment="Center"
Style="{StaticResource FlatButton}"
Command="{Binding ResetCommand}"
/>
The view model
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
}
}
public AddStakeholderViewModel()
{
ResetCommand = new RelayCommand(() => ResetForm());
}
private void ResetForm()
{
Name = " ";
}
You can implement the INotifyPropertyChanged interface in your class. This works for me:
public class Person : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("Name");
}
}
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
XAML:
<TextBox Foreground="White"
Grid.Column="1"
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Style="{StaticResource InputInputBox}"
/>
MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = newPerson;
}
Person newPerson = new Person();
private void button_Click(object sender, RoutedEventArgs e)
{
newPerson.Name = "";
}
}
I have a ListView that is bound on an ObservableCollection.
<ListView Grid.Column="0" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" BorderThickness="0" Margin="5" Name="CustomerListView" ItemsSource="{Binding Customers}" SelectedItem="{Binding Path=CurrentCustomer, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="{Binding FirstName}"/>
<TextBlock Margin="5,0,0,0" Text="{Binding LastName}"/>
</WrapPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
In the same View i have some TextBoxes which are meant to edit the CurrentCustomer. I also have a save button. If you click this button the modifications of the CurrentCustomer should be saved. If the button "cancel" is pressed the modifications should be discarded.
<TextBox Name="CustomerSalutationTextBox" Grid.Column="1" Grid.Row="0" Height="20px" Margin="5" Text="{Binding Path=CurrentCustomer.Salutation, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
The Problem is, if i make some changes on the currentCusomer, they are taking effect immediately.
Do you have a solution?
What you need to add in your ViewModel / the class you have a binding context to is to save what was previous in the Textfield.
And when you hit abort, u just overwrite your newValue with the old one.
I'm going to setup a small example.
class ExampleViewModel : INotifyPropertyChanged {
private string _customerLastName;
private string _customerName;
private string _initialCustomerName;
private string _initialCustomerLastName;
public string CustomerName {
get { return this._customerName; }
set {
this._customerName = value;
this.OnPropertyChanged();
}
}
public string CustomerLastName {
get { return this._customerLastName; }
set {
this._customerLastName = value;
this.OnPropertyChanged();
}
}
public ExampleViewModel(string customerName, string customerLastName) {
this.CustomerName = customerName;
this.CustomerLastName = customerLastName;
this._initialCustomerName = customerName;
this._initialCustomerLastName = customerLastName;
}
//example event handler for your abort button
private void OnAbortButtonClick(object sender, EventArgs args) {
this.CustomerName = this._initialCustomerName; //set the initial name
this.CustomerLastName = this._initialCustomerLastName; //set the initial lastName
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Alternative
As you might load your data from a database/csv file/something else, you should know the original values. When pressing the cancel button, you could invoke a CancelButtonClicked event in your ViewModel and some other class which subscribed to the ViewModels event and knows the original Model could set the original values on that viewModel instance, or just exchange the ViewModel instance with the original one.
Have a look at : https://msdn.microsoft.com/en-us/library/hh848246.aspx
class ExampleViewModel : INotifyPropertyChanged {
private string _customerLastName;
private string _customerName;
public event CancelButtonClicked CancelButtonClicked;
public string CustomerName {
get { return this._customerName; }
set {
this._customerName = value;
this.OnPropertyChanged();
}
}
public string CustomerLastName {
get { return this._customerLastName; }
set {
this._customerLastName = value;
this.OnPropertyChanged();
}
}
public ExampleViewModel(string customerName, string customerLastName) {
this.CustomerName = customerName;
this.CustomerLastName = customerLastName;
}
private void OnAbortButtonClick(object sender, EventArgs args) {
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
internal delegate void CancelButtonClicked(object sender);
public class SomeOtherClass {
private ExampleViewModel _viewModel;
public SomeOtherClass() {
this._viewModel = new ExampleViewModel("foo", "bar");
this._viewModel.CancelButtonClicked += ViewModelOnCancelButtonClicked;
}
private void ViewModelOnCancelButtonClicked(object sender) {
ExampleViewModel vm = sender as ExampleViewModel;
vm.CustomerName = "foo"; //set the initial values again
vm.CustomerLastName = "bar";
}
}
Alternative2
You could also exchange the complete VM when the event of the cancel button is invoked to retreive its original state.
Alternative3
Everytime your SelectedItem changes, you could save the current state of it by creating a copy of it. When your CancelButton is pressed, you set the SelectedItem to the copy of your original viewModel.
You'd need a copy constructor or a copy method for that purpose.
I've found out another solution. In the code behind of the view i've added following:
void saveButton_Click(object sender, RoutedEventArgs e)
{
BindingExpression be = customerFirstNameTextBox.GetBindingExpression(TextBox.TextProperty);
be.UpdateSource();
}
My textbox with UpdateSourceTrigger Explicit
<TextBox Name="customerFirstNameTextBox" Grid.Column="1" Grid.Row="2" Height="20px" Margin="5" Text="{Binding Path=CurrentCustomer.FirstName, Mode=TwoWay, UpdateSourceTrigger=Explicit}" IsEnabled="{Binding Path=IsCustomerTextEnabled}"/>
And my button
<Button Name="SaveButton" Click="saveButton_Click" Margin="5" Content="Save"/>
Hi I am trying to study simple data binding in wpf. I tried and am not succeeding.. please suggest ways..
<Window x:Class="WpfTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="250" Width="350">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Label Content="First Name" Grid.Row="0" Grid.Column="0" Height="25" HorizontalAlignment="Stretch"/>
<Label Content="Last Name" Grid.Row="1" Grid.Column="0" Height="25" HorizontalAlignment="Stretch"/>
<Label Content="Full Name" Grid.Row="2" Grid.Column="0" Height="25" HorizontalAlignment="Stretch"/>
<TextBox x:Name="fName" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" Grid.Row="0" Grid.Column="1" Height="25" HorizontalAlignment="Stretch"/>
<TextBox x:Name="lName" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" Grid.Column="1" Height="25" HorizontalAlignment="Stretch"/>
<Label x:Name="lblFullName" Content="{Binding FirstName}" Grid.Row="2" Grid.Column="1" Height="25" HorizontalAlignment="Stretch"/>
<Button x:Name="cmdCommand" Content="Command" Grid.Row="3" Grid.Column="1" Margin="2" VerticalAlignment="Center" HorizontalAlignment="Center" Click="cmdCommand_Click"/>
</Grid>
</Window>
Now you see, I want that the label lblFullName to be automatically updated as soon as I type name in textboxes.
Now the my codebehind file looks like this:
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (value != _firstName)
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (value != _lastName)
{
_lastName = value;
OnPropertyChanged("LastName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notifies objects registered to receive this event that a property value has changed.
/// </summary>
/// <param name="propertyName">The name of the property that was changed.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public MainWindow()
{
InitializeComponent();
}
private void cmdCommand_Click(object sender, RoutedEventArgs e)
{
lblFullName.Content = FirstName + " " + LastName;
}
}
needless to say, even clicking the command button is not working...
any suggestions?
Setting the DataContext to point to the window itself will solve the immediate problem:
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
The paths in the Binding expressions in the Xaml are all relative to the DataContext of the controls so the DataContext has to be set.
However, I strongly suggest that you put the first name and last name in a separate class (e.g. PersonViewModel) That will make the code easier to read and to maintain.
public class PersonViewModel : INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (value != _firstName)
{
_firstName = value;
OnPropertyChanged("FirstName");
}
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (value != _lastName)
{
_lastName = value;
OnPropertyChanged("LastName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new PersonViewModel();
}
private void cmdCommand_Click(object sender, RoutedEventArgs e)
{
lblFullName.Content = FirstName + " " + LastName;
}
}
Now the last part of this code does not work anymore. There are two solutions.
The quick and dirty solution:
private void cmdCommand_Click(object sender, RoutedEventArgs e)
{
var person = this.DataContext as PersonViewModel;
if(person == null) return;
lblFullName.Content = string.Format("{0} {1}", person.FirstName, person.LastName);
}
A better way, when trying to do proper MVVM is to put an extra property in the ViewModel (note the change in the First and Last name properties!):
public class PersonViewModel : INotifyPropertyChanged
{
public string FullName
{
get
{
return string.Format("{0} {1}", FirstName, LastName);
}
}
private string _firstName;
public string FirstName
{
get { return _firstName; }
set
{
if (value != _firstName)
{
_firstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("FullName");
}
}
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set
{
if (value != _lastName)
{
_lastName = value;
OnPropertyChanged("LastName");
OnPropertyChanged("FullName");
}
}
}
// remainder of the class remains the same
}
Remove the button and its click handler. Bind the label to this new property:
<Label Content="{Binding FullName}"
Grid.Row="2" Grid.Column="1"
Height="25" HorizontalAlignment="Stretch"/>
When the user wants to add a new Reminder, they click the add button on the mainWindow; and once they have added the data, it should display it in a listbox on the main window using an observable collection.
This brings up a new window which brings up options of, at the moment Date and message.
When the user has entered the data, Finish method is called.
The issue is, when the user has finished inputting the data on the new window, I add it to the reminder collection, but it doesn't update on the main window. I am wondering if is a datacontext issue and if I am even going about this the right way?
Thanks for the help.
Add Window:
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class AddWindow : Window, INotifyPropertyChanged
{
private MainWindow mainW;
public AddWindow(MainWindow mW)
{
InitializeComponent();
mainW = mW;
this.Show();
DataContext = this;
}
private void Finish(object sender, RoutedEventArgs e)
{
mainW.Reminders.Add(new Remind(SelectedDate, Message));
this.Close();
}
private DateTime selectedDate = DateTime.Today;
public DateTime SelectedDate
{
get
{
return selectedDate;
}
set
{
if (value != selectedDate)
{
selectedDate = value;
RaisePropertyChange("SelectedDate");
}
}
}
private string message;
public string Message
{
get
{
return message;
}
set
{
if (message != value)
{
message = value;
RaisePropertyChange("Message");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChange(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
}
Add Xaml
<TextBox Name="Time" HorizontalAlignment="Left" Height="28" Margin="124,60,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="115"/>
<DatePicker SelectedDate="{Binding SelectedDate}" HorizontalAlignment="Left" Height="28" Margin="124,27,0,0" VerticalAlignment="Top" Width="115"/>
<TextBox Text="{Binding Msg}" HorizontalAlignment="Left" Height="58" Margin="123,93,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="144"/>
<Button Content="Finish" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="135,226,0,0" Click="Finish" />
MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private ObservableCollection<Remind> reminders = new ObservableCollection<Remind>();
public ObservableCollection<Remind> Reminders
{
get
{
return reminders;
}
}
private void Add(object sender, RoutedEventArgs e)
{
AddWindow addWindow = new AddWindow(this);
}
}
Mainwindow Xaml:
</MenuItem>
<MenuItem Header="About">
<MenuItem Header="Info"/>
</MenuItem>
</Menu>
<Button Content="New" HorizontalAlignment="Left" Height="26" Margin="6,279,0,0" VerticalAlignment="Top" Width="81" Click="Add" />
<Button Content ="Delete" HorizontalAlignment="Left" Height="26" Margin="87,279,0,0" VerticalAlignment="Top" Width="79" />
<Button Content="Change" HorizontalAlignment="Left" Height="26" Margin="166,279,0,0" VerticalAlignment="Top" Width="73" />
<ScrollViewer Name="Scroller" HorizontalAlignment="Left" Height="235" Margin="0,31,0,0" VerticalAlignment="Top" Width="346">
<ListBox ItemsSource= "{Binding Reminders}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Height="41" Width="293" >
<TextBlock Text="{Binding Path=dateT}"/>
<TextBlock Text="{Binding Path=Msg}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</ScrollViewer>
<Separator HorizontalAlignment="Left" Height="13" Margin="0,266,0,0" VerticalAlignment="Top" Width="362"/>
Remind :
public class Remind : INotifyPropertyChanged
{
public Remind(DateTime dt, string ms)
{
DateT = dt;
Msg = ms;
}
private DateTime datet;
public DateTime DateT
{
get
{
return datet;
}
set
{
if (datet != value)
{
datet = value;
RaisePropertyChange("dateT");
}
}
}
private string msg;
public string Msg
{
get
{
return msg;
}
set
{
if (msg != value)
{
msg = value;
RaisePropertyChange("Msg");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChange(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
Change dateT to DateT in your main window
<TextBlock Text="{Binding Path=DateT}"/>
and you are done.
Under the bottom line everthing with the datacontext was ok. Your the 2 wrong property names were missspelled.
Hm, I created a small solution with your code and it just works fine. The main windows's list gets updated right after I click finish. The only small problem is you use the wrong binding in AddWindow to the message. You bind to "Msg" but it should be "Message" in the 3rd line above:
<TextBox Text="{Binding Message}" HorizontalAlignment="Left" Height="58" Margin="123,93,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="144"/>
Code looks fine but I can see one issue in it:
TextBox in AddWindow is binded with Msg but the corresponding property name in code behind is Message. So, textBox is never binded properly and hence new item is added in collection with String.Empty value for Msg.
<TextBox Text="{Binding Msg}" <-- HERE. It should be Message.
However, it should still show a new object in collection on GUI with empty string and DateTime value set on AddWindow even in case of binding failure.
For updated Remind class in question:
One issue in XAML binding where you are binding with field instead of it's wrapper property.
<TextBlock Text="{Binding Path=dateT}"/> <-- HERE, Path name should be DateT.
I have a username and password box.
Underneath it I have a button.
When I click that button I want to analyse what has been put into the username and password box.
How do I do this with mvvm light?
This is where I am:
XAML
...DataContext="{Binding Main, Source={StaticResource Locator}}">...
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0">
<TextBlock HorizontalAlignment="Left" Margin="10,0,0,0" TextWrapping="Wrap" Text="Username" VerticalAlignment="Top"/>
<TextBox HorizontalAlignment="Left" Height="72" Margin="0,27,0,0" TextWrapping="Wrap" Text="{Binding Username}" VerticalAlignment="Top" Width="456"/>
<TextBlock HorizontalAlignment="Left" Margin="10,99,0,0" TextWrapping="Wrap" Text="Password" VerticalAlignment="Top"/>
<PasswordBox HorizontalAlignment="Left" Height="72" Margin="0,126,0,0" Password="{Binding Password}" VerticalAlignment="Top" Width="456"/>
<Button Content="Log in" HorizontalAlignment="Center" Margin="167,203,169,0" VerticalAlignment="Top" Command="{Binding LogInCommand}"/>
</Grid>
View Model
public class MainViewModel : ViewModelBase
{
public LoginCredentials LoginCredentials { get; set; }
public ICommand LogInCommand { get; private set; }
public MainViewModel()
{
LoginCredentials = new LoginCredentials();
LogInCommand = new RelayCommand(this.OnLogInCommand);
}
private void OnLogInCommand()
{
string testUsername = Username;
string testPassword = Password;
}
#region Properties
public string Username
{
get { return LoginCredentials.Username; }
set { LoginCredentials.Password = value; }
}
public string Password
{
get { return LoginCredentials.Password; }
set { LoginCredentials.Password = value; }
}
#endregion
}
MainPage.xaml.cs
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
}
}
what is happening at the moment:
When I click my button, the LogInCommand is run and it fires my method OnLoginCommand. I have put a break point on the testUsername declaration to see if when the button is clicked, the username and password reflect what has been put in; they are both empty. What must I do to make sure these are updated as someone is typing or when the button is pressed or however it works???
I have now spent about 4 weeks learning mvvm and trying to get a simple click event and binding to work. This is simply not making sense... doh. Thanks for any help!
P.S - Is MVVM light too confusing for new comers? the documentation is so.. light on detail. No examples :(
View
Windows Phone doesn't contain "UpdateSourceTrigger=PropertyChanged". You have to use "Explicit" and manually call "UpdateSource" in code behind, otherwise value of TextBox/PasswordBox will be raise when TextBox/PasswordBox lost focus.
And don't forget to set "Mode=TwoWay".
<TextBox
Text="{Binding Path=Username, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
TextChanged="TextBoxTextChanged" />
<PasswordBox
Password="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=Explicit}"
PasswordChanged="PasswordBoxPasswordChanged" />
<Button
Command="{Binding Path=LogInCommand}"
Content="Log in" />
View - code behind
private void PasswordBoxPasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox pb = sender as PasswordBox;
if (pb != null)
{
pb.GetBindingExpression(PasswordBox.PasswordProperty).UpdateSource();
}
}
private void TextBoxTextChanged(object sender, TextChangedEventArgs e)
{
TextBox tb = sender as TextBox;
if (tb != null)
{
tb.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
}
ViewModel
Fields
private RelayCommand _logInCommand;
private string _password;
private string _username;
Properties
public bool CanExecuteLogInCommand
{
get
{
return !string.IsNullOrWhiteSpace(this.Username) && !string.IsNullOrWhiteSpace(this.Password);
}
}
public RelayCommand LogInCommand
{
get
{
// or you can create instance in constructor: this.LogInCommand = new RelayCommand(this.ExecuteLogInCommand, () => this.CanExecuteLogInCommand);
return this._logInCommand ?? (this._logInCommand = new RelayCommand(this.ExecuteLogInCommand, () => this.CanExecuteLogInCommand));
}
}
public string Username
{
get { return this._username; }
set
{
// a) shorter alternative -> "True if the PropertyChanged event has been raised, false otherwise"
if (this.Set(() => this.Username, ref this._username, value))
{
// raise CanExecuteLogInCommand
this.LogInCommand.RaiseCanExecuteChanged();
}
// b) longer alternative
//if (value == this._username) { return; }
//this._username = value;
//this.RaisePropertyChanged(() => this.Username);
//this.LogInCommand.RaiseCanExecuteChanged();
}
}
public string Password
{
get { return this._password; }
set
{
if (this.Set(() => this.Password, ref this._password, value))
{
this.LogInCommand.RaiseCanExecuteChanged();
}
}
}
Methods
private void ExecuteLogInCommand()
{
// .... = this.Username;
// .... = this.Password;
}
Check this sample.
To get the View and ViewModel 'linked up' so that they are synchronized, you need to implement INotifyPropertyChanged (Encapsulated in ViewModelBase). i.e.:
private string userName;
public string UserName
{
get { return userName; }
set
{
if (value != userName)
{
userName = value;
RaisePropertyChanged("UserName");
}
}
}