how to add property to Window in xaml - wpf

I'm learning MVVM by write an litle app with Login function. In View layer, I have a LoginWindow with some binding like this:
<TextBox x:Name="tbxUsername" Grid.Row="0" Grid.Column="1" Width="150" Height="22" Margin="15,10,5,10"
Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged}" />
<PasswordBox View:PasswordHelper.Attach="True" View:PasswordHelper.Password="{Binding Path=Password, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
x:Name="pwdPassword" Grid.Row="1" Grid.Column="1" Width="150" Height="22" Margin="15,10,5,10" />
The problem is i want to implement a binding like this:
<Window.Authenticated={Binding Path=Authenticated, UpdateSourceTrigger=PropertyChanged, Mode=OneWay, NotifyOnTargetUpdated=True} TargetUpdated="authenticated_TargetUpdated"/>
Authenticated is a bool value which will changed in my viewmodel.
Is there an way for me?
Edit for #lain:
Here my LoginWindow.xaml (style and layout removed).
<Window x:Class="ATCheck_View.LoginWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:View="clr-namespace:ATCheck_View"
xmlns:ViewModel="clr-namespace:ATCheck_ViewModel;assembly=ATCheck_ViewModel"
Title="Login"
WindowStartupLocation="CenterScreen"
ResizeMode="CanMinimize"
SizeToContent="WidthAndHeight"
>
<Window.DataContext>
<ViewModel:LoginViewModel />
</Window.DataContext>
<Grid>
<TextBox x:Name="tbxUsername" Grid.Row="0" Grid.Column="1"
Width="150" Height="22" Margin="15,10,5,10" Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged, TargetNullValue='atcheck', NotifyOnTargetUpdated=True}"/>
<PasswordBox View:PasswordHelper.Attach="True" View:PasswordHelper.Password="{Binding Path=Password, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, TargetNullValue='123456'}" x:Name="pwdPassword" Grid.Row="1" Grid.Column="1"
Width="150" Height="22" Margin="15,10,5,10" />
<Button x:Name="btnLogin" Width="65" Height="20" Margin="5,15,10,12"
Command="{Binding LoginCommand}"
CommandParameter="">
<TextBlock VerticalAlignment="Center">Login</TextBlock>
</Button>
<Button x:Name="btnCancel" Width="60" Height="20" Margin="5,15,5,12" Click="btnCancel_Click">
<TextBlock VerticalAlignment="Center">Cancel</TextBlock>
</Button>
</Grid>
</Window>
LoginViewModel:
public class LoginViewModel: ViewModelBase
{
private string _username;
private string _password;
private bool _authenticated = false;
public string Username
{
get
{
return _username;
}
set
{
_username = value;
RaisePropertyChangedEvent("Username");
}
}
public string Password
{
get
{
return _password;
}
set
{
_password = value;
RaisePropertyChangedEvent("Password");
}
}
public bool Authenticated
{
get
{
return _authenticated;
}
private set
{
_authenticated = value;
RaisePropertyChangedEvent("Authenticated");
}
}
public ICommand LoginCommand
{
get
{
return new RelayCommand<string>(Login);
}
}
private void Login(string p)
{
Authenticated = true;
Console.WriteLine("Infomation:");
Console.WriteLine(Authenticated);
Console.WriteLine(Username);
Console.WriteLine(Password);
}
}
ViewModelBase implement INotifyPropertyChanged and RelayCommand that I folow John Smith's article.
#nit: I tried as your lead, propdp, rebuild, and type Authenticated folow "Window" tag, but nothing happened when I press commbo Ctrl + Space.
What all i want to do is an messagebox that will show when Authenticated change from False to True:
private bool _authenticated = false;
public bool Authenticated
{
get
{
return _authenticated;
}
set
{
if (value == true)
{
MessageBox.Show("Logged!");
}
}
}
public LoginWindow()
{
InitializeComponent();
LoginViewModel myViewModel = (LoginViewModel)this.DataContext;
myViewModel.PropertyChanged += myViewModel_PropertyChanged;
}
void myViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Authenticated")
{
Authenticated = ((LoginViewModel)sender).Authenticated;
}
}

You will have to add DependencyProperty to your Window class like below:
public static readonly DependencyProperty AuthenticatedProperty =
DependencyProperty.Register( "Authenticated", typeof(bool),
typeof(YOURWINDOWCLASS), new FrameworkPropertyMetadata(false));
// .NET Property wrapper
public bool Authenticated
{
get { return (bool)GetValue(AuthenticatedProperty ); }
set { SetValue(AuthenticatedProperty , value); }
}
Then you can bind
<Window Authenticated={Binding Path=Authenticated, UpdateSourceTrigger=PropertyChanged, Mode=OneWay, NotifyOnTargetUpdated=True} TargetUpdated="authenticated_TargetUpdated"/>

Related

Login Window in WPF Prism

I'm implementing a login window before my shell runs. How will I run the code and say that after authentication in the login window, proceed with the execution of shell application.
Here is my initial code:
LoginViewModel.cs
public event EventHandler LoginCompleted;
private void RaiseLoginCompletedEvent()
{
LoginCompleted?.Invoke(this, EventArgs.Empty);
}
Bootstraper.cs from the Shell
My problem here is that I cannot instantiate my LoginModel since the constructor of my Model has a parameter to use my Services Interface.
Is there any solution for this? Thanks!
New up an instance of a class that implements the ILoginAuth interface yourself:
var loginVM = new LoginViewModel(new LoginAuth());
...or let the container do it for you:
var loginVM = new LoginViewModel(Container.Resolve<ILoginAuth>());
For the latter option to work, you must register your type mappings by overriding the RegisterTypes method:
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<ILoginAuth, LoginAuth>();
}
Another method of doing this:
ILoginAuth auth = CommonServiceLocator.ServiceLocator.Current.GetInstance<ILoginAuth>();
This way you don't have to include ILoginAuth in your constructor. Also, you can do this from any where!
<pre>
/*Brian code modified */
public interface ICloseWindow {
Action Close {
get;
set;
}
}
public partial class LoginWindow: Window {
public LoginWindow(ILoginViewModel viewModel) {
InitializeComponent();
DataContext = viewModel;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e) {
if (DataContext is ICloseWindow vm) {
vm.Close += () =>{
DialogResult = true;
};
}
}
private void Close_Window(object sender, RoutedEventArgs e) {
DialogResult = false;
}
}
/*App.xaml.cs*/
protected override void InitializeShell(Window shell) {
Window login = Container.Resolve < LoginWindow > ();
var result = login.ShowDialog();
if (!result.Value) {
Application.Current.Shutdown();
}
else {
base.InitializeShell(shell);
}
}
public class LoginViewModel: BindableBase,
ILoginViewModel,
ICloseWindow {
private string _userName;
private string _password;
private bool _isAuthenticated;
private ICommand _authenticateUserCommand;
private IEventAggregator _eventAggregator;
public Action Close {
get;
set;
}
public ICommand AuthenticateUserCommand {
get =>_authenticateUserCommand;
set =>_authenticateUserCommand = value;
}
public LoginViewModel(IEventAggregator eventAggregator) {
AuthenticateUserCommand = new DelegateCommand < object > (AuthenticateUser);
_eventAggregator = eventAggregator;
}
private void AuthenticateUser(object parameter) {
var passwordBox = parameter as PasswordBox;
var password = passwordBox.Password;
if (password == "password") {
_isAuthenticated = true;
_eventAggregator.GetEvent < MessageSentEvent > ().Publish("Login Data");
}
if (_isAuthenticated) {
Close ? .Invoke();
}
}
public string UserName {
get =>_userName;
set =>_userName = value;
}
public string Password {
get {
return _password;
}
set {
if (_password != value) {
_password = value;
RaisePropertyChanged(nameof(Password));
}
}
}
}
<Window x:Class="YourApplication.Views.LoginWindow"
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:YourApplication"
mc:Ignorable="d"
Title="LoginWindow"
Height="450"
Width="800"
WindowStartupLocation="CenterScreen"
WindowStyle="None"
x:Name="WindowBorder"
RenderOptions.BitmapScalingMode="HighQuality">
<Grid SnapsToDevicePixels="True"
UseLayoutRounding="True"
TextOptions.TextFormattingMode="Display"
TextOptions.TextRenderingMode="ClearType">
<DockPanel>
<DockPanel DockPanel.Dock="Left"
Width="400"
LastChildFill="True">
<Canvas>
<Canvas.Background>
<ImageBrush ImageSource="pack://application:,,,/YourApplication;component/Images/Splash.png" />
</Canvas.Background>
<TextBlock Margin="4,0,0,4"
VerticalAlignment="Center"
Foreground="#FFAAAAAA"
TextWrapping="NoWrap"
Text="Library Management System"
FontSize="16"
Background="Transparent"
DockPanel.Dock="Top" />
</Canvas>
</DockPanel>
<DockPanel LastChildFill="True">
<Button x:Name="PART_CLOSE"
DockPanel.Dock="Top"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Margin="5"
Height="20"
Width="20"
Style="{DynamicResource MetroWindowButtonStyle}"
Click="Close_Window">
<Path Data="F1M54.0573,47.8776L38.1771,31.9974 54.0547,16.1198C55.7604,14.4141 55.7604,11.6511 54.0573,9.94531 52.3516,8.23962 49.5859,8.23962 47.8802,9.94531L32.0026,25.8229 16.1224,9.94531C14.4167,8.23962 11.6511,8.23962 9.94794,9.94531 8.24219,11.6511 8.24219,14.4141 9.94794,16.1198L25.8255,32 9.94794,47.8776C8.24219,49.5834 8.24219,52.3477 9.94794,54.0534 11.6511,55.7572 14.4167,55.7585 16.1224,54.0534L32.0026,38.1745 47.8802,54.0534C49.5859,55.7585 52.3516,55.7572 54.0573,54.0534 55.7604,52.3477 55.763,49.5834 54.0573,47.8776z"
Stretch="Uniform"
Fill="#FFAAAAAA"
Width="10"
Margin="0,0,0,0"></Path>
</Button>
<StackPanel>
<PasswordBox x:Name="txtPassword"
Margin="2,20,10,10"
Height="22"
Width="100" />
<Button Width="100"
Height="22"
Margin="10,150,10,10"
Content="Login"
Command="{Binding AuthenticateUserCommand}"
CommandParameter="{Binding ElementName=txtPassword}" />
</StackPanel>
</DockPanel>
</DockPanel>
</Grid>
</Window>
</pre>

WPF TextBox Binding ElementName null?

I have a ListBox control with TypeUsers.When I select some record in Listbox and update Name in TextBox the Name property/textbox return always null. Never take value from TextBox, always null ?
Image description here
This is my code
<ListBox x:Name="LstTypeUsers"
Grid.Row="0" Grid.Column="4"
Width="220" Height="120"
ScrollViewer.VerticalScrollBarVisibility="Visible"
ItemsSource="{Binding TypeUsers}"
DisplayMemberPath="Name">
</ListBox>
<TextBox
Grid.Row="0" Grid.Column="2"
x:Name="txtName"
HorizontalAlignment="Left" Height="23"
TextWrapping="Wrap"
VerticalAlignment="Top" Width="170"
Text="{Binding ElementName=LstTypeUsers, Path=SelectedItem.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"
Validation.ErrorTemplate="{x:Null}"/>
<Button
Grid.Column="0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="100" Height="30"
Command="{Binding UpdateTypeUserCmd}"
Grid.ColumnSpan="3" Margin="20,90,0,0">
<StackPanel Orientation="Horizontal">
<Image Source="/Images/Save.png" />
<TextBlock Width="55" Height="18" ><Run Text=" "/><Run Text="Update"/></TextBlock>
</StackPanel>
</Button>
EDIT
// Model class
public class UserType: INotifyPropertyChanged
{
[Key]
private int usertypeId;
public int UserTypeId
{
get
{
return this.usertypeId;
}
set
{
this.usertypeId = value;
OnPropertyChanged("UserTypeId");
}
}
[MaxLength(200)]
private string name;
public string Name
{
get
{
return this.name;
}
set
{
this.name = value;
OnPropertyChanged("Name");
}
}
[Required]
private bool status;
public bool Status
{
get
{
return this.status;
}
set
{
this.status = value;
OnPropertyChanged("Status");
}
}
public virtual ObservableCollection<User> User { get; private set; }
public UserType()
{
this.User = new ObservableCollection<User>();
}
}
// ViewModelBase class
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
// UserTypeViewModel
public class UserTypeViewModel
private UserType _userType;
private ObservableCollection<UserType> _UserTypeList;
// Constructor
public UserTypeViewModel()
{
_userType = new UserType();
_UserTypeList = new ObservableCollection<UserType>(GetUserTypeAll());
}
public ObservableCollection<TypeUsers> TypeUsers
{
get
{
return _UserTypeList;
}
set
{
_UserTypeList = value;
//OnPropertyChanged("TypeUsers");
}
}
public string Name
{
get
{
return _userType.Name;
}
set
{
_userType.Name = value;
//OnPropertyChanged("Name");
}
}
Thank you.
Implement INotifyPropertyChanged interface in UserType class.
You're binding directly to the WPF control (ListBox) and not the ViewModel. I suggest you add a property in your ViewModel that will bind to the TextBox.Text property, once the data changes or the user had changed the value in the TextBox, then the data will update and be reflected in the UI.
Also, if I remember correctly, at launch, the SelectedItem property of the ListBox is null, so there might be a problem there too, but I'm not certain about that...
I have resolved my problem. I have also implemented INotifyPropertyChanged interface in Model class. I'm new in WPF MVVM and I read that this interface is implemented only in the ViewModel for connection with View. Now I have implemented in both classes and everything works great.
Thanks Everyone.

WPF CheckBox IsChecked Property binding issues

Please help, I was trying to do this small example.
My aim is to when I keep the checkbox ticked the app should show the the Ip address of the Host I enter. But The checkbox IsChecked property is never updated in the view model, Even it is been changed in the UI
My View `
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Offset="0.00" Color="LavenderBlush" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Grid.Background>
<StackPanel Grid.Row="0" Margin="150,30,69,236" Grid.ColumnSpan="2">
<TextBox x:Name="inputBox" Text="{Binding TxtHostName, Mode=TwoWay}" Foreground="Azure" Background="YellowGreen" VerticalAlignment="Bottom" Height="45"/>
</StackPanel>
<Button Command="{Binding StartCommand }" Content="Get IP" HorizontalAlignment="Left" Margin="257,89,0,0" VerticalAlignment="Top" Width="75" RenderTransformOrigin="0.013,-0.273"/>
<TextBlock Text="{Binding IpAddress}" Background="BlueViolet" Margin="150,153,69,104" Grid.ColumnSpan="2" />
<Button Content="Close" Command="{Binding CloseCommand}" HorizontalAlignment="Left" Margin="257,250,0,0" VerticalAlignment="Top" Width="75"/>
<CheckBox Content="CheckBox" IsChecked="{Binding IsSelected, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" HorizontalAlignment="Left" Margin="150,111,0,0" VerticalAlignment="Top"/>
</Grid>
`
My ViewModel:
public class ViewModel:INotifyPropertyChanged
{
#region INPC
public void RaisePropertyChanged(string propName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private string txtHostName;
public string TxtHostName
{
get { return txtHostName; }
set { txtHostName = value;
RaisePropertyChanged("TxtHostName");
}
}
private string ipAddress;
public string IpAddress
{
get { return ipAddress; }
set { ipAddress = value;
RaisePropertyChanged("IpAddress");
}
}
private bool checkbox;
public bool CheckBox
{
get { return checkbox; }
set { checkbox = value;
RaisePropertyChanged("IsSelected");
}
}
public event EventHandler RequestClose;
protected void OnRequestClose()
{
if (RequestClose != null)
RequestClose(this, EventArgs.Empty);
}
private RelayCommand _StartCommand;
public ICommand StartCommand
{
get
{
if (this._StartCommand == null)
this._StartCommand = new RelayCommand(StartClick);
return this._StartCommand;
}
}
private RelayCommand _CloseCommand;
public ICommand CloseCommand
{
get
{
if(this._CloseCommand==null)
this._CloseCommand=new RelayCommand(CloseClick);
return this._CloseCommand;
}
}
private void CloseClick(object obj)
{
OnRequestClose();
}
private void StartClick(object obj)
{
if (checkbox)
{
string HostName = TxtHostName;
IPAddress[] ipaddress = Dns.GetHostAddresses(HostName);
foreach (IPAddress ipaddr in ipaddress)
{
IpAddress = ipaddr.ToString();
}
}
else
{
IpAddress = "Please tick the checkbox";
}
}
}
}
The RealyCommand is as it should be.
The CheckBox Property value never changes weather I change it in the UI or not.
Your raising your property changed event against IsSelected, but your bindable property is called Checkbox, rename Checkbox to IsSelected and update your private variable to something like isSelected.
In this case Id rename the variable to IsChecked or ComboBoxIsChecked.
I'm not sure if there is a copy-and-paste error but your View Model property is called Checkbox while you are raising the property changed event using the label IsSelected.
This and the error in the binding might be your problem. Based on your View Model the binding should be:-
<CheckBox Content="CheckBox" IsChecked="{Binding Checkbox, Mode=TwoWay}" HorizontalAlignment="Left" Margin="150,111,0,0" VerticalAlignment="Top"/>
Update: Recommendation if you are using C# 5.0 or above
To avoid typo's when creating setters and raising IPropertyNotifyChange events I would recommend using the CallerMemberName attribute as follows:-
public void RaisePropertyChanged([CallerMemberName] string propName = "")
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
Then your setter in your example becomes:-
private bool checkbox;
public bool CheckBox
{
get { return checkbox; }
set { checkbox = value;
RaisePropertyChanged();
}
}
Meaning as you refactor your View Model then the compiler will insert the name of the calling property to ensure the label in the INotifyProertyChanged event matches your property name without you having to remember to manually update it yourself.

Navigation between view using MVVM in WPF

Im new to WPF and MVVM.
Im trying to create Login window using MVVM and i succeeded to create.
here is the Login.xmal code.
<Button x:Name="btnLogin" Content="Login" HorizontalAlignment="Left" Margin="51,0,0,10"
VerticalAlignment="Bottom" Width="124" Height="57" Grid.Column="1"
CommandParameter="{Binding ElementName=txtPassword}"
Command="{Binding LoginCommand}"
>
</Button>
<Button x:Name="btnClose" Content="Close" HorizontalAlignment="Left" Margin="180,0,0,10"
VerticalAlignment="Bottom" Width="124" Height="57" Grid.Column="1" Command="{Binding ExitCommand}">
</Button>
<Label Content="User Name" Margin="10,74,0,0" VerticalAlignment="Top" Height="49"
VerticalContentAlignment="Center" Grid.Column="1" HorizontalAlignment="Left" Width="130"/>
<TextBox x:Name="txtUserName" HorizontalAlignment="Right" Height="49" Margin="0,74,10,0"
TextWrapping="Wrap" VerticalAlignment="Top" Width="185"
VerticalContentAlignment="Center" Grid.Column="1" FontSize="18">
<TextBox.Text>
<Binding Path="Username" Mode="OneWayToSource">
<Binding.ValidationRules>
<ExceptionValidationRule></ExceptionValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Label Content="Password" Margin="10,128,0,0" VerticalAlignment="Top" Height="49"
VerticalContentAlignment="Center" Grid.Column="1" HorizontalAlignment="Left" Width="130"/>
<PasswordBox x:Name="txtPassword" HorizontalAlignment="Right"
Height="49" Margin="0,128,10,0"
VerticalAlignment="Top" Width="185"
VerticalContentAlignment="Center" Grid.Column="1" FontSize="18">
</PasswordBox>
after this i have created the viewModeBase.cs class in which i implemented INotifyPropertyChanged and this included in LoginViewModel.cs...
here is LoginViewModel.cs code
public class LoginViewModel : ViewModelBase
{
private string m_username;
public string Username
{
get { return m_username; }
set
{
m_username = value;
OnPropertyChanged("Username");
}
}
private string m_password;
public string Password
{
get { return m_password; }
set
{
m_password = value;
OnPropertyChanged("Password");
}
}
private DelegateCommand exitCommand;
public ICommand ExitCommand
{
get
{
if (exitCommand == null)
{
exitCommand =new DelegateCommand(Exit);
}
return exitCommand;
}
}
private void Exit()
{
Application.Current.Shutdown();
}
public LoginViewModel()
{
}
private DelegateCommand<object> loginCommand;
public ICommand LoginCommand
{
get
{
if (loginCommand == null)
{
loginCommand = new DelegateCommand<object>(Login);
}
return loginCommand;
}
}
public void Login(object pPasswordBox)
{
try
{
if (string.IsNullOrEmpty(Username))
{
MessageBox.Show("Username cannot be blank.");
return;
}
if (string.IsNullOrEmpty(((PasswordBox)pPasswordBox).Password))
{
MessageBox.Show("Password cannot be blank.");
return;
}
dlUsers odlUsers = new dlUsers();
bool lResult = odlUsers.UserAuthentication(clsGymManagment.ConnectionString, Username,
((((PasswordBox)pPasswordBox).Password)));
if (lResult)
{
///TODO: Need code to Hide Login Window and Open New XAML.....
}
else
{
MessageBox.Show("Username/Password is wrong.");
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
As i want to Hide LOGIN.XAML file and open UI.XAML file.. (UI.XAML you can consider any XAML window.)...
also it would be help full if you could assist me to navigation between Usercontrol on UI.XAML
You need to control the login window from a separate block of code, for instance App.xaml.cs. Set app.xaml to call code rather than show a window.
Have App_Startup create LoginViewModel, new up a form, set the data context of the form to your ViewModel and show the it.
Updates to the form will update the ViewModel, when it closes it will return control to your calling code.
Login.xaml.cs
private void btnOk_Click(object sender, RoutedEventArgs e)
{
if (anything incorrect)
{
MessageBox.Show("Enter a username and password");
}
else
DialogResult = true;
}
App.xaml.cs
Login.DataContext = LoginViewModel;
if (Login.ShowDialog() ?? false)
{
//Check the LoginViewModel for a correct password.
}
Fortunately the ability to hide and display different controls as you move through different pages inside an application is already written for you. See http://msdn.microsoft.com/en-us/library/ms750478.aspx.
Navigation Window is really powerful and can quite easily be skinned to provide very completely different looks too. See http://alski.net/post/2012/01/13/WPF-Wizards-part-2-Glass.aspx

Messaging problem using MVVM-Light

Initially I wanted to pass an "ObservableCollection< customClass>" between two VMs , but even simple messaging isn't working for me.
MainViewModel
private void openNewSpecialCustomer()
{
GalaSoft.MvvmLight.Messaging.Messenger.Default.Send("Musaab");
Console.WriteLine("send done");
AddNewSpecialCustomer a = new AddNewSpecialCustomer();
_dialogService.showDialoge(a);
}
AddNewSpecialCustomerViewModel
public AddNewSpecialCustomerViewModel()
{
GalaSoft.MvvmLight.Messaging.Messenger.Default.Register<string>(this, doSomething);
Console.WriteLine("Should now Receive");
validProperties = new Dictionary<string, bool>();
validProperties.Add("specialCustomerName",false);
validProperties.Add("tel", false);
allPropertiesValid = false;
}
public void doSomething(string s)
{
Console.WriteLine("Should be received");
specialCustomerName = s;
Console.WriteLine("s value " + s);
}
public String specialCustomerName
{
get { return _specialCustomerName; }
set
{
if (_specialCustomerName != value)
{
_specialCustomerName = value;
OnPropertyChanged("specialCustomerName");
}
}
}
now XAML for AddNewSpecialCustomer
<Window FlowDirection="RightToLeft" x:Class="GlassStore.AddNewSpecialCustomer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:local="clr-namespace:GlassStore.ViewModels"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="AddNewSpecialCustomer" Height="318" Width="458">
<Window.DataContext>
<local:AddNewSpecialCustomerViewModel/>
</Window.DataContext>
<Grid Background="{DynamicResource NormalBrush}">
<Button Command="{Binding Save}" Content="موافق" Height="29" HorizontalAlignment="Left" Margin="31,218,0,0" Name="button1" VerticalAlignment="Top" Width="75" />
<Label Content="إسم العميل" Height="27" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" Width="120" />
<TextBox Text="{Binding specialCustomerName,Mode=TwoWay,ValidatesOnDataErrors=True,ValidatesOnExceptions=True,UpdateSourceTrigger=PropertyChanged}" Height="27" HorizontalAlignment="Left" Margin="155,12,0,0" Name="textBox1" VerticalAlignment="Top" Width="210" />
<Label Content="المنطقة/المكان" Height="27" HorizontalAlignment="Left" Margin="12,67,0,0" Name="label2" VerticalAlignment="Top" Width="120" />
<TextBox Text="{Binding region}" Height="27" HorizontalAlignment="Left" Margin="155,67,0,0" Name="textBox2" VerticalAlignment="Top" Width="210" />
<TextBox Text="{Binding tel,ValidatesOnDataErrors=True,ValidatesOnExceptions=True,UpdateSourceTrigger=PropertyChanged}" Height="27" HorizontalAlignment="Left" Margin="155,119,0,0" Name="textBox3" VerticalAlignment="Top" Width="210" />
<Label Content="رقم الهاتف " Height="27" HorizontalAlignment="Left" Margin="12,119,0,0" Name="label3" VerticalAlignment="Top" Width="120" />
<Button Content="إلغاء" Height="29" HorizontalAlignment="Left" Margin="143,218,0,0" Name="button2" VerticalAlignment="Top" Width="75" />
<Label Content="" Height="29" HorizontalAlignment="Left" Margin="12,177,0,0" Name="label4" VerticalAlignment="Top" Width="412" />
</Grid>
you can see that there is textBox, with a Text property bounded to specialCustomerName Property , which I'm trying to change it via the messenger , the data-bind mode is TwoWay, so I expect my textBox to have my name on it when loaded , (I'm sending my name via the messenger) which is not the case, I hope this may be more clear code
thanks in advance
Is your second window constructor called after the first one ?
You should first register and only then send the message. Previously sent messages are not received. Sorry for being C.O. but this kind of mistake is rather possible :)
As both the sending of the message and the receiving of the message happens in constructors it could lead to a race condition. Register for the receiving of the message in the constructor, but send the message at a later point in time, e.g. in the load or a command handler.
Edit:
Could not reproduce the behaviour, here is the code I used to test this:
ViewLocator:
public class ViewModelLocator
{
private static MainViewModel _main;
public ViewModelLocator() {
CreateMain();
}
public static MainViewModel MainStatic {
get {
if (_main == null) {
CreateMain();
}
return _main;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main {
get {
return MainStatic;
}
}
public static void ClearMain() {
if (_main != null) {
_main.Cleanup();
_main = null;
}
}
public static void CreateMain() {
if (_main == null) {
_main = new MainViewModel();
}
}
#region [SecondViewModel]
private static SecondViewModel _secondViewModel;
public static SecondViewModel SecondViewModelStatic {
get {
if (_secondViewModel == null) {
CreateSecondViewModel();
}
return _secondViewModel;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public SecondViewModel SecondViewModel {
get {
return SecondViewModelStatic;
}
}
public static void ClearSecondViewModel() {
if (_secondViewModel != null) {
_secondViewModel.Cleanup();
_secondViewModel = null;
}
}
public static void CreateSecondViewModel() {
if (_secondViewModel == null) {
_secondViewModel = new SecondViewModel();
}
}
#endregion
public static void Cleanup() {
ClearMain();
ClearSecondViewModel();
}
}
MainViewModel
public class MainViewModel : ViewModelBase
{
public MainViewModel() {
if (IsInDesignMode) {
// Code runs in Blend --> create design time data.
} else {
// Code runs "for real"
}
Messenger.Default.Send("Initializer - does not show becaus of race condition!");
}
public string Welcome {
get {
return "Welcome to MVVM Light";
}
}
#region [TestCommand]
private RelayCommand _cmdTest;
public RelayCommand TestCommand {
get {
return _cmdTest ?? (
_cmdTest = new RelayCommand(
() => {
// Execute delegate
Messenger.Default.Send("Hello!");
}
)
);
}
}
#endregion
public override void Cleanup() {
// Clean up if needed
base.Cleanup();
}
}
SecondViewModel
public SecondViewModel() {
Messenger.Default.Register<string>(this, (s) => this.DoSomething(s));
if (IsInDesignMode) {
// Code runs in Blend --> create design time data.
} else {
// Code runs "for real": Connect to service, etc...
}
}
#region [Message]
public const string MessagePropertyName = "Message";
private string _message = default(string);
public string Message {
get {
return _message;
}
set {
if (_message == value) {
return;
}
_message = value;
RaisePropertyChanged(MessagePropertyName);
}
}
#endregion
public void DoSomething(string s) {
this.Message = s;
}
public override void Cleanup() {
base.Cleanup();
}
}
MainWindow XAML
<Window x:Class="MvvmLightTests.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="300"
Width="300"
DataContext="{Binding Main, Source={StaticResource Locator}}">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Skins/MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<StackPanel>
<StackPanel>
<TextBlock FontSize="36"
FontWeight="Bold"
Foreground="Purple"
Text="{Binding Welcome}"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap" />
<Button Content="click to send message" Margin="0,40,0,0" Command="{Binding TestCommand}" />
</StackPanel>
<StackPanel DataContext="{Binding SecondViewModel, Source={StaticResource Locator}}" Margin="0,40,0,0">
<TextBlock Text="{Binding Message, TargetNullValue='--'}" FontWeight="Bold" HorizontalAlignment="Center"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>

Resources