xaml binding not working as expected - c#

I have created a simple credo (create, review, edit, delete, overview) wpf application for cars to learn about c# and have run into an issue. When either adding or editing an item to my observable collection, I want to allow the user to be able to browse the computer for a picture associated with the car. I originally had accomplished this in code behind with the following:
namespace CarApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
/// <summary>
/// Open a browser window when the user clicks on the 'browse' button
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Add_Browse_Button_Click(object sender, RoutedEventArgs e)
{
//send path to textbox
AddPicturePath.Text = this.Browse();
}
///// <summary>
///// Open a browser window when the user clicks on the 'browse' button
///// </summary>
///// <param name="sender"></param>
///// <param name="e"></param>
private void Edit_Browse_Button_Click(object sender, RoutedEventArgs e)
{
//send path to textbox
EditPicturePath.Text = this.Browse();
}
/// <summary>
/// Open browser window and return user selection
/// </summary>
/// <returns>filename</returns>
private string Browse()
{
//open browser window
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
//search only for .png files
dlg.DefaultExt = ".png";
//show browser
dlg.ShowDialog();
return dlg.FileName;
}
}
}
This worked perfectly but then I was requested to remove all code behind (which seemed fine to be because this is purely a UI element, tell me if I'm wrong) from the application. So I moved the code into an ICommand:
namespace CarApp.Commands
{
public class BrowseCommand : ICommand
{
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
Car param = parameter as Car;
try
{
//open browser window
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
//search only for .png files
dlg.DefaultExt = ".png";
//show browser
dlg.ShowDialog();
//send path to textbox
param.PicturePath = dlg.FileName;
}
catch (NullReferenceException)
{
Console.Write("Param is null in browse");
}
}
}
}
Now when I run the application, the path does not show up in the textbox, when when I click the "add to list" button, the item is added displaying the proper image. It seems as though the textbox is not updating, even though I have INotification implemented. Am I missing something obvious? Here is relevant the xaml code:
<Button Width="75"
Height="23"
Margin="10,0,0,0"
HorizontalAlignment="Left"
Command="{Binding BrowseCommand}"
CommandParameter="{Binding NewCar}"
Content="Browse" />
and
<TextBox x:Name="AddPicturePath"
Width="200"
Height="23"
Margin="10,0,0,0"
HorizontalAlignment="Left"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Text="{Binding NewCar.PicturePath,
UpdateSourceTrigger=PropertyChanged}"
TextWrapping="Wrap" />

If I understand this correctly, you are binding to NewCar.PicturePath but only NewCar is changing. When you Notify of property changed, I am guessing you only notify that the NewCar object is changing (not the PicturePath). Try this; within the NewCar setter, after setting _NewCar = value, also update _NewCar.PicturePath and assuming PicturePath also is notifying properly, it should update properly and also give you some insight as to how the binding is working... Hope this helps!

Related

What is wrong when trying to enable a combobox using radiobuttons?

I'm trying to use radiobuttons to enable a combobox, though there is an error within an if statement that is saying that I am not able to add .Checked or .IsChecked. As far as I am aware there is no namespace required that isn't already added to the classes by default.
I have tried copying online tutorials exactly yet the same error still occurs or even more errors occur.
<Grid>
<ComboBox x:Name="comboBox" HorizontalAlignment="Left" Margin="354,186,0,0" VerticalAlignment="Top" Width="120"/>
<RadioButton x:Name="rdoEnable" Content="Enable" HorizontalAlignment="Left" Margin="293,143,0,0" VerticalAlignment="Top"/>
<RadioButton x:Name="rdoDisable" Content="Disable" HorizontalAlignment="Left" Margin="471,143,0,0" VerticalAlignment="Top"/>
</Grid>
namespace RadioButtonTest
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
string[] array = new string[] { "One", "Two", "Three" };
foreach (string number in array)
{
comboBox.Items.Add(number);
}
rdoEnable.Checked = true;
}
private void RdoEnable_Checked(object sender, RoutedEventArgs e)
{
if (rdoDisable.Checked)
{
comboBox.Enabled = false;
}
else
{
comboBox.Enabled = true;
}
}
}
}
I've posted a couple of examples of things that I have tried to imgur if it is any help.
https://imgur.com/a/1sfqDfK
RadioButton.Checked is an event, you can not assign bool value to it, only attach event handler (method, which will be invoked when event is raised)
RadioButton.IsChecked is a property which gives current state. It has Nullable<bool> type, so condition should be in form
if (rdoDisable.IsChecked == true) {} else {}
or simple:
comboBox.IsEnabled = (rdoDisable.IsChecked == true);

Shared resources problem with multithreading in WPF C#

So i'm working on a simple WPF app that should read informations from a "dedicated page " on our LAN and output them more efficiently. So now i'm trying to make the labels content update automatically after an ip to get datas from is selected. The plan is to basically refresh the page every minute or so to update the output.
Problem is I tryed a lot of solutions about multithreading but still get the same error : InvalidOperationException: The calling thread cannot access this object because a different thread owns it.
basically what i understand from that is that the mainWindows thread owns the labels' contents so that i can't update them after reading from said file/string.
Here's my xaml for the window:
<Window x:Class="PartCountBello.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PartCountBello"
mc:Ignorable="d"
Title="Controllo Macchine" Height="337.42" Width="366.586"
ResizeMode="CanMinimize">
<Grid Margin="0,10,2,12">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="0*"/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="cmbNomiMacchine" HorizontalAlignment="Left"
Margin="44,74,0,0" VerticalAlignment="Top" Width="129" Height="22"
SelectionChanged="cmbNomiMacchine_SelectionChanged"/>
<Label x:Name="lblNomeDato" Content="PartCount :"
HorizontalAlignment="Left" Margin="44,162,0,0" VerticalAlignment="Top"
Height="26" Width="129"/>
<Label x:Name="lblPartCount" Content="" HorizontalAlignment="Left"
VerticalAlignment="Top" Margin="178,162,0,0" Height="26" Width="144"
RenderTransformOrigin="0.071,0.731"/>
<Label x:Name="lblSelectInfos" Content="Selezionare macchina"
HorizontalAlignment="Left" Margin="44,43,0,0" VerticalAlignment="Top"
Width="129" Height="26"/>
<Label x:Name="lblLavoro" Content="Mansione : "
HorizontalAlignment="Left" Margin="44,210,0,0" VerticalAlignment="Top"
Width="129"/>
<Label x:Name="lblMansione" Content="" HorizontalAlignment="Left"
Margin="178,210,0,0" VerticalAlignment="Top" Width="144"/>
<Button x:Name="btnRefresh" Content="Aggiorna"
HorizontalAlignment="Left" Margin="247,74,0,0" VerticalAlignment="Top"
Width="75" Height="22" Click="btnRefresh_Click"/>
<Label x:Name="lblMoreInfos" Content="" HorizontalAlignment="Left"
Margin="10,241,0,0" VerticalAlignment="Top" Width="339" Height="35"/>
</Grid>
</Window>
and here is my mainWindows Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace PartCountBello
{
/// <summary>
/// Logica di interazione per MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
List<string> datas = new List<string> { }; //list that contains machines names and relatives ips. each ip is in the previous index...
//respect to the machine.
public MainWindow()
{
InitializeComponent();
try
{
ReadFile rf = new ReadFile(); //creates the file reader we 're using to...
datas = rf.getListFromFile("MyIPsFile"); //get all our datas from our file.. Not posting the real file name here cuz you never know. still contains only the ips to connect to.
for(int i = 2;i<=datas.Count-1;i+=3)
{
cmbNomiMacchine.Items.Add(datas[i]);
}
}
catch (Exception ex)
{
lblMoreInfos.Content = ex.Message;
}
finally
{
}
}
/// <summary>
/// when the selection is changed to an item it will update the labels contents through a method in the class activityChecker.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cmbNomiMacchine_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
updateLabelsContent();
}
/// <summary>
/// gets the index the item selected in the combobox has in the file to pass it to the fileread and get the ip.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
private int getSelectedItemIndex(int index)
{
int FIRST_REAL_INDEX = 2;
int realIndex;
if (index == 0)
return 0;
else
return realIndex=FIRST_REAL_INDEX+index*2;
}
/// <summary>
/// checks if the machine we are trying to connect to is on, if so updates the dedicated lables' content, else prints a simple message down below.
/// </summary>
private void updateLabelsContent()
{
string toShow;//the auxiliary string we are going to use to output on labels.
ActivityChecker ac = new ActivityChecker();
lblMoreInfos.Content = "";
if (getSelectedItemIndex(cmbNomiMacchine.SelectedIndex) != 0)
{
if (PingHost(datas[getSelectedItemIndex(cmbNomiMacchine.SelectedIndex) - 1]))
{
toShow = ac.getPartCount(datas[getSelectedItemIndex(cmbNomiMacchine.SelectedIndex)]);
lblPartCount.Content = toShow;
toShow = ac.getJob(datas[getSelectedItemIndex(cmbNomiMacchine.SelectedIndex)]);
lblMansione.Content = toShow;
}
else
{
lblMoreInfos.Content = "La macchina รจ al momento spenta.";
}
}
else
{
lblMansione.Content = "";
lblPartCount.Content = "";
}
}
/// <summary>
/// updates the content of the machine dedicated labels.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnRefresh_Click(object sender, RoutedEventArgs e)
{
updateLabelsContent();
}
/// <summary>
/// allows to verify if the machine we are trying to connect to is on before we actually try to, avoids some freezes.
/// </summary>
/// <param name="nameOrAddress"></param>
/// <returns></returns>
public static bool PingHost(string nameOrAddress)
{
bool pingable = false;
Ping pinger = new Ping();
try
{
PingReply reply = pinger.Send(nameOrAddress);
pingable = reply.Status == IPStatus.Success;
}
catch (PingException)
{
// Discard PingExceptions and return false;
}
return pingable;
}
}
}
Ok i found a solution.
added this to mainwindows.cs:
/// <summary>
/// private method that manages the thread for the automatic update.
/// </summary>
private void updateLabelsContentThread()
{
while(true)
{
Thread.Sleep(TimeSpan.FromSeconds(10));
Dispatcher.Invoke(new Action(() => { updateLabelsContent(); }));
}
}
and changed the event method for machine selection to this.
/// <summary>
/// when the selection is changed to an item it will update the labels contents through a method in the class activityChecker.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void cmbNomiMacchine_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
updateLabelsContent();
Thread t = new Thread(updateLabelsContentThread);
t.Start();
}
works perfectly for me, leaving this here in case it helps someone.
I am not advocating this WinForms-like approach, there are probably better solutions using bindings, but it would require a lot of changes. If you need to do it this way and now:
public partial class MainWindow : Window
{
....
private readonly SynchronizationContext uiContext;
public MainWindow()
{
InitializeComponent();
//controls created on a specific thread can only be modified by that thread.
//(99.99999%) of the time controls are created and modified on the UI thread
//SynchronizationContext provides a way to capture and delegate work to the UI thread (it provides more functionality than this, but in your case this is what interests you)
//We capture the UI Synchronization Context so that we can queue items for execution to the UI thread. We know this is the UI thread because we are in the constructor for our main window
uiContext = SynchronizationContext.Current;
....
}
private void cmbNomiMacchine_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
UpdateOnUIThread();
}
...............
///I Chose to write another method for clarity, feel free to rework the code anyway you like. Ideally you want to only delegate short work to the UI thread (say textbox.Text = "". This is just here to show the concept
private void UpdateOnUIThread()
{
//Post is asynchronous so will give controll back immediately. If you want synchronous operation, use Send
uiContext.Post(new SendOrPostCallback((o) => { updateLabelsContent(); }), null);
}
..............
}
}

Specifying data bindings in WPF

I have a simple WPF app with 3 textboxes, 2 of the text boxes input numbers and the third textbox shows the sum of inputs when another button is clicked.
I come from WinForms and MFC background and for me, the intuitive thing to do is to right click the textBoxes, open their properties and specify local variables to read the data from the boxes. For example, MFC has the DDX mechanism for this.
However, in WPF, the only way to specify a binding seems to add XAML code directly to App.XAML, as shown here on MSDN. Is there a way to create a binding without coding it manually into XAML? XAML coding seems a little daunting to me, since I am new to it.
My WPF form is as follows :
<Window x:Class="SimpleAdd.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBox HorizontalAlignment="Left" Height="23" Margin="174,43,0,0" TextWrapping="Wrap" Text="{Binding dataModel.Value1}" VerticalAlignment="Top" Width="120"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="174,84,0,0" TextWrapping="Wrap" Text="{Binding dataModel.Value2}" VerticalAlignment="Top" Width="120"/>
<TextBox HorizontalAlignment="Left" Height="23" Margin="174,127,0,0" TextWrapping="Wrap" Text="{Binding dataModel.Value3}" VerticalAlignment="Top" Width="120"/>
<Button Content="Add" HorizontalAlignment="Left" Margin="393,84,0,0" VerticalAlignment="Top" Width="75" Click="OnAdd"/>
</Grid>
</Window>
My C# file is as follows :
namespace SimpleAdd
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void OnAdd(object sender, RoutedEventArgs e)
{
dataModel m1 = new dataModel();
m1.Value3 = m1.Value1 + m1.Value2; // BUG : All Properties are 0 even after updating the boxes.
}
}
public class dataModel
{
private int val1, val2, val3;
public int Value1
{
get {return val1;}
set { val1 = value; }
}
public int Value2
{
get { return val2; }
set { val2 = value; }
}
public int Value3
{
get { return val3; }
set { val3 = value; }
}
}
}
EDIT : Adding implementation for INotifyPropertyChanged
namespace SimpleAdd
{
public abstract class ObservableObject : INotifyPropertyChanged
{
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public virtual void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#region INotifyPropertyChanged Members
/// <summary>
/// Raises the PropertyChange event for the property specified
/// </summary>
/// <param name="propertyName">Property name to update. Is case-sensitive.</param>
public virtual void RaisePropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
OnPropertyChanged(propertyName);
}
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion // INotifyPropertyChanged Members
}
}
Your TextBox are not being updated because you haven't set the data source (DataContext typically) behind the bindings.
When you write
<TextBox Text="{Binding dataModel.Value1}" />
What you are really saying "pull the value for this field from TextBox.DataContext.dataModel.Value1". If TextBox.DataContext is null, then nothing will be displayed.
The DataContext is inherited automatically, so the following code would work :
public partial class MainWindow : Window
{
public dataModel _data { get; set; }
public MainWindow()
{
InitializeComponent();
_data = new dataModel();
this.DataContext = _data;
}
private void OnAdd(object sender, RoutedEventArgs e)
{
_data.Value3 = _data.Value1 + _data.Value2;
}
}
assuming you also change your TextBox bindings to remove the dataModel. from them
<TextBox Text="{Binding Value1}" />
This sets the DataContext of the entire form to the _data object, and in your OnAdd method we can update the _data object properties in order to have the UI update.
I like to blog a bit about beginner WPF stuff, and you may be interested in checking out a couple of the posts there which explain these concepts :
Understanding the change in mindset when switching from WinForms to WPF
What is this "DataContext" you speak of?
Technically, that's not in App.xaml (which is a special file in WPF).
That said, yes you can do it. You can set up the binding in code like so:
textBox1.Text = new Binding("SomeProperty");
...
Okay, that gets really annoying so we just do it in XAML:
<TextBox Text="{Binding SomeProperty}"/>
Both pieces of code do the same thing, but when you get into more advanced bindings, the XAML syntax is a lot easier to use. Plus, its more obvious where you text is coming from rather than having to open two files.
The FrameworkElement class and the FrameworkContentElement class both expose a SetBinding method. If you are binding an element that inherits either of these classes, you can call the SetBinding method directly.
The following example creates a class named, MyData, which contains a property named MyDataProperty.
public class MyData : INotifyPropertyChanged
{
private string myDataProperty;
public MyData() { }
public MyData(DateTime dateTime)
{
myDataProperty = "Last bound time was " + dateTime.ToLongTimeString();
}
public String MyDataProperty
{
get { return myDataProperty; }
set
{
myDataProperty = value;
OnPropertyChanged("MyDataProperty");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string info)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
The following example shows how to create a binding object to set the source of the binding. The example uses SetBinding to bind the Text property of myText, which is a TextBlock control, to MyDataProperty.
MyData myDataObject = new MyData(DateTime.Now);
Binding myBinding = new Binding("MyDataProperty");
myBinding.Source = myDataObject;
myText.SetBinding(TextBlock.TextProperty, myBinding);

MVVM Pattern Query

I am learning WPF with MVVM Design pattern and trying to understand how to get some things done outside of the code behind.
I have a login page, pictured below.
I have a password control I took from http://www.wpftutorial.net/PasswordBox.html.
I would for now for the case of simple understanding, like to ask you if my code is in the right class/set correctly to abide to MVVVM regulations with seperation of concerns.
I currently have an if statement to check if the details match a name string and a password string.
The code is in the code behind. I just wonder if this is correct with regards to MVVM. I wonder how you implement this in a ViewModel?
private void OK_Click(object sender, RoutedEventArgs e)
{
if (emp.Name == "ep" && emp.Password == "pass")
{
MessageBox.Show("namd and Pw accepted");
//open new page
var HomeScreen = new HomeScreen();
HomeScreen.Show();
}
else
{
//deny access
MessageBox.Show("Incorrect username and password");
}
}
Instead of implementing the button click handler in the code behind, use an ICommand and bind it to the button's event in XAML.
Here is a really great tutorial that got me starting in MVVM:
WPF Apps With The Model-View-ViewModel Design Pattern
[Edited to add sample code]
Heres a simple code example to do just what your example does, but in MVVM style and without any code-behind code at all.
1) Create a new WPF Solution, for this small example I named it simply "WpfApplication".
2) Edit the code of the automatically created MainWindow.xaml:
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:WpfApplication"
Title="MainWindow" Height="234" Width="282">
<!-- Create the ViewModel as the initial DataContext -->
<Window.DataContext>
<viewModel:MainWindowViewModel />
</Window.DataContext>
<Grid>
<TextBox Height="23"
HorizontalAlignment="Left"
Margin="70,31,0,0"
Name="textBox1"
VerticalAlignment="Top"
Width="120"
Text="{Binding Path=Name}"/>
<TextBox Height="23"
HorizontalAlignment="Left"
Margin="70,72,0,0"
Name="textBox2"
VerticalAlignment="Top"
Width="120"
Text="{Binding Path=Password}" />
<Label Content="Name"
Height="28"
HorizontalAlignment="Left"
Margin="22,29,0,0"
Name="label1"
VerticalAlignment="Top" />
<Label Content="PW"
Height="28"
HorizontalAlignment="Left"
Margin="22,70,0,0"
Name="label2"
VerticalAlignment="Top" />
<Button Content="OK"
Height="23"
HorizontalAlignment="Left"
Margin="70,119,0,0"
Name="button1"
VerticalAlignment="Top"
Width="120"
Command="{Binding Path=LoginCommand}"
CommandParameter="{Binding Path=.}"
/>
</Grid>
</Window>
(Ignore the Width, Height, Margin values, these are just copied & pasted from my designer and were quick & dirty adjusted to roughly look like your screenshot ;-) )
3) Create the Command class that will handle your log in logic. Note that I did not implement it as a RelayCommand like in Josh Smith's tutorial but it would be easy to modify the code accordingly:
namespace WpfApplication
{
using System;
using System.Windows;
using System.Windows.Input;
/// <summary>
/// Checks the user credentials.
/// </summary>
public class LoginCommand : ICommand
{
/// <summary>
/// Defines the method to be called when the command is invoked.
/// </summary>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
public void Execute(object parameter)
{
MainWindowViewModel viewModel = parameter as MainWindowViewModel;
if (viewModel == null)
{
return;
}
if (viewModel.Name == "ep" && viewModel.Password == "pass")
{
MessageBox.Show("namd and Pw accepted");
//open new page
var HomeScreen = new HomeScreen();
HomeScreen.Show();
}
else
{
//deny access
MessageBox.Show("Incorrect username and password");
}
}
/// <summary>
/// Defines the method that determines whether the command can execute in its current state.
/// </summary>
/// <returns>
/// true if this command can be executed; otherwise, false.
/// </returns>
/// <param name="parameter">Data used by the command. If the command does not require data to be passed, this object can be set to null.</param>
public bool CanExecute(object parameter)
{
// Update this for your application's needs.
return true;
}
public event EventHandler CanExecuteChanged;
}
}
4) Now add the ViewModel that will communicate with the View and provide it the command interface and values:
namespace WpfApplication
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Windows.Input;
/// <summary>
/// TODO: Update summary.
/// </summary>
public class MainWindowViewModel : INotifyPropertyChanged
{
#region Implementation of INotifyPropertyChanged
/// <summary>
/// Occurs when a property value changes.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Signal that the property value with the specified name has changed.
/// </summary>
/// <param name="propertyName">The name of the changed property.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion Implementation of INotifyPropertyChanged
#region Backing Fields
/// <summary>
/// Gets or sets the value of Name.
/// </summary>
private string name;
/// <summary>
/// Gets or sets the value of Password.
/// </summary>
private string password;
/// <summary>
/// Gets or sets the value of LoginCommand.
/// </summary>
private LoginCommand loginCommand;
#endregion Backing Fields
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="MainWindowViewModel"/> class.
/// </summary>
public MainWindowViewModel()
{
this.loginCommand = new LoginCommand();
}
#endregion Constructor
#region Properties
/// <summary>
/// Gets or sets the name of the user.
/// </summary>
public string Name
{
get
{
return this.name;
}
set
{
if (this.name == value)
{
return;
}
this.name = value;
this.OnPropertyChanged("Name");
}
}
/// <summary>
/// Gets or sets the user password.
/// </summary>
public string Password
{
get
{
return this.password;
}
set
{
if (this.password == value)
{
return;
}
this.password = value;
this.OnPropertyChanged("Password");
}
}
/// <summary>
/// Gets or sets the command object that handles the login.
/// </summary>
public ICommand LoginCommand
{
get
{
return this.loginCommand;
}
set
{
if (this.loginCommand == value)
{
return;
}
this.loginCommand = (LoginCommand)value;
this.OnPropertyChanged("LoginCommand");
}
}
#endregion Properties
}
}
5) Finally, do not forget to add an additional Window HomeScreen that will be opened by the LoginCommand to the solution. :-)
To implement that, you should bind the button's command like this -
<Button Content ="OK" Command = {Binding OKCommand} />
In your ViewModel, create an ICommand property for this binding like this -
public class MyViewModel() : INotifyPropertyChanged
{
ICommand _OKCommand;
public ICommand OKCommad
{
get { return _OKCommand; }
set { _OKCommand = value; PropertyChanged(OKCommad); }
}
public MyViewModel()
{
this.OKCommand += new DelegateCommand(OKCommand_Execute);
}
public void OKCommand_Execute()
{
// Code for button click here
}
}
Also note that for using this delegate command, you need to add reference to Microsoft.Practices.Prism.dll

2 way binding in wpf calls property twice

Ive been playing with wpf and 2 way data binding to better understand it and ive noticed that when a textbox has 2 way data binding to a property the property is called twice. I have verified this by writing a value to the output window when the property is called. My code is below:-
My xaml
<Page
x:Class="_2waybindTest.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:_2waybindTest"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
<TextBox HorizontalAlignment="Left" Margin="55,93,0,0" TextWrapping="Wrap" Text="{Binding TestProperty, Mode=TwoWay}" VerticalAlignment="Top" Width="540"/>
<Button Content="Button" HorizontalAlignment="Left" Margin="55,31,0,0" VerticalAlignment="Top" Click="Button_Click_1"/>
<TextBox HorizontalAlignment="Left" Margin="55,154,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="540"/>
</Grid>
</Page>
my simple viewmodel class to test
public class viewmodel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _TestProperty;
public void SetTestProperty()
{
this.TestProperty = "Set Test Property";
}
public string TestProperty{
get
{
return this._TestProperty;
}
set
{
this._TestProperty = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("TestProperty"));
}
Debug.WriteLine("this._TestProperty = " + this._TestProperty);
}
}
}
my xaml code behind
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
DataContext = new viewmodel();
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
var vm = (viewmodel)DataContext;
vm.SetTestProperty();
}
}
Why is it called twice. Is this expected behaviour?
Generally, you should check if value actually changed, before firing a propertyChanged event, otherwise you may get into infinte cycle of binding updates. In your case, textbox is probably checking for change, preventing such cycle.
public string TestProperty{
set
{
if(this._TestProperty == value)
{
return;
}
this._TestProperty = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("TestProperty"));
}
}
}

Categories