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
Related
I have a problem with MVVM-Light. I use the version 5.3.0.0...
.XAML
<DockPanel Dock="Top">
<Button Margin="5" VerticalAlignment="Top" HorizontalAlignment="Center" Command="{Binding CancelDownloadCommand}" FontSize="20"
Background="Transparent" BorderThickness="2" BorderBrush="{StaticResource AccentColorBrush4}" ToolTip="Cancelar"
DockPanel.Dock="Right">
<StackPanel Orientation="Horizontal">
<Image Source="Images/48x48/Error.png" Height="48" Width="48"/>
<Label Content="{Binding ToolTip, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" FontFamily="Segoe UI Light"/>
</StackPanel>
</Button>
<Button Margin="5" VerticalAlignment="Top" HorizontalAlignment="Center" Command="{Binding DownloadCommand}" FontSize="20"
Background="Transparent" BorderThickness="2" BorderBrush="{StaticResource AccentColorBrush4}" ToolTip="Descargar"
DockPanel.Dock="Right">
<StackPanel Orientation="Horizontal">
<Image Source="Images/48x48/Download.png" Height="48" Width="48"/>
<Label Content="{Binding ToolTip, RelativeSource={RelativeSource AncestorType={x:Type Button}}}" FontFamily="Segoe UI Light"/>
</StackPanel>
</Button>
</DockPanel>
DownloadViewModel.cs
I used a MessageBox, but in my case, call a method that reads an XML. This example does not work, the buttons are disabled, but are not reactivated at the end of execution. I need to click on the UI to activate.
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.CommandWpf;
private async void Download()
{
Reset();
await Task.Run(() =>
{
MessageBox.Show("Hello");
});
Reset();
}
private void Reset()
{
IsEnabled = !IsEnabled;
IsEnabledCancel = !IsEnabledCancel;
}
private ICommand _downloadCommand;
public ICommand DownloadCommand
{
get { return _downloadCommand ?? (_downloadCommand = new RelayCommand(Download, () => IsEnabled)); }
}
private ICommand _cancelDownloadCommand;
public ICommand CancelDownloadCommand
{
get
{
return _cancelDownloadCommand ??
(_cancelDownloadCommand = new RelayCommand(CancelDownload, () => IsEnabledCancel));
}
}
private bool _isEnabled = true;
private bool IsEnabled
{
get { return _isEnabled; }
set
{
if (_isEnabled != value)
{
_isEnabled = value;
RaisePropertyChanged();
}
}
}
private bool _isEnabledCancel;
private bool IsEnabledCancel
{
get { return _isEnabledCancel; }
set
{
if (_isEnabledCancel != value)
{
_isEnabledCancel = value;
RaisePropertyChanged();
}
}
}
By using CommandManager.InvalidateRequerySuggested(), I fixed it. But read somewhere that is not recommended because this command checks all RelayCommand. This did not happen to me before.
But if within the Task.Run not add anything. It works perfectly. Buttons are activated and deactivated again.
private async void Download()
{
Reset();
await Task.Run(() =>
{
// WIDTHOUT CODE
// WIDTHOUT CODE
// WIDTHOUT CODE
});
Reset();
}
When you update CanExecute, in your case IsEnabled and IsEnabledCancel properties, you have to raise CanExecuteChanged event.
Even more you can little bit simplify your code.
private bool _isEnabled;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
if (Set(ref _isEnabled, value))
{
DownloadCommand.RaiseCanExecuteChanged();
}
}
}
The same way update your IsEnabledCancel property.
Of course, you have to declare your command as RelayCommand and not ICommand.
private RelayCommand _downloadCommand;
public RelayCommand DownloadCommand
{
get { return _downloadCommand ?? (_downloadCommand = new RelayCommand(Download, () => IsEnabled)); }
}
You can also read about: "A smart MVVM command".
Looking at the source code for MVVM Light, it is based around the CommandManager.InvalidateRequerySuggested() (anti) pattern. Which you rightly say is a massive performance hog, due to the global nature of the (anti)pattern.
The problem lies in the constructor. public RelayCommand(Action execute, Func<bool> canExecute)
With the canExecute being a Func<bool>, it is impossible to be able to get (at runtime) the property name, and is therefore impossible to bind on the the INotifyPropertyChanged.PropertyChanged event. Thus causing the command to re-evaluate the canExecute.
#kubakista shows you how to force the re-evaluation by calling the RaiseCanExecuteChanged method. But that really breaks the single responsibility principle, and leaks the implementation of the ICommand.
My advice is to use ReactiveUI's ReactiveCommand. This allows you to do:
DownloadCommand = ReactiveCommand.Create(Download, this.WhenAnyValue(x => x.IsEnabled).Select(enabled => enabled));
CancelDownloadCommand = ReactiveCommand.Create(CancelDownload, this.WhenAnyValue(x => x.IsEnabled).Select(enabled => false == enabled));
public bool IsEnabled
{
get {return _isEnabled; }
set
{
_isEnabled = value;
OnPropertyChanged();
}
}
One thing i did notice is that your Enabled Properties (IsEnabled, IsEnabledCancel) are private when they should be public. However that doesn't fix your issue :)
A simple fix is to get rid of the CanExecute part of your Command
eg
public ICommand DownloadCommand
{
get { return _downloadCommand ?? (_downloadCommand = new RelayCommand(Download)); }
}
and bind to your property on the Button.IsEnabledproperty in xaml
eg
<Button IsEnabled="{Binding IsEnabled}" Margin="5" VerticalAlignment="Top"
HorizontalAlignment="Center" Command="{Binding DownloadCommand}"
FontSize="20" Background="Transparent" BorderThickness="2"
BorderBrush="Red" ToolTip="Descargar" DockPanel.Dock="Right">
...
</Button>
Hope that helps
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"/>
My WPF window binds directly to an Entity Framework data context (CollectionViewSource). Users begin editing immediately once a record is found. The moment the form is dirty I'd like to disable the Add button and enable the Save and Undo buttons. Is there a simple way to do this using binding or an event?
I'm not using MVVM. I use Entity Framework database first and the EF designer. I'm hoping to avoid adding code for every field. The database is quite large.
You can do this without events if you'd like. And you can leverage the power of DataBinding without going so far as MVVM. The example below demonstrates, in a very simple way, how you can accomplish this. If your entity classes don't already have an IsDirty property (it has been a while since I've used database-first EF), you could add the property with a partial class.
XAML:
<TextBlock HorizontalAlignment="Right" VerticalAlignment="Center" Text="Name:" Margin="10"/>
<TextBox x:Name="NameTextBox" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Center" Margin="10" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
<StackPanel Grid.Row="1" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="Save" HorizontalAlignment="Right" Margin="5" IsEnabled="{Binding IsDirty}" />
<Button Content="Cancel" HorizontalAlignment="Right" Margin="5" IsEnabled="{Binding IsDirty}" Click="Cancel_Click"/>
<Button Content="Add" HorizontalAlignment="Right" Margin="5" IsEnabled="{Binding IsClean}"/>
</StackPanel>
</Grid>
</Window>
A test Entity class:
public class Entity : INotifyPropertyChanged
{
private string _name;
private bool _isDirty = false;
public string Name
{
get { return _name; }
set
{
if(!IsDirty)
IsDirty = (value != _name);
_name = value;
RaisePropertyChanged("Name");
}
}
public bool IsDirty
{
get{ return _isDirty; }
set{
_isDirty = value;
RaisePropertyChanged("IsDirty");
RaisePropertyChanged("IsClean");
}
}
public bool IsClean
{
get { return !_isDirty; }
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
And the code behind for the window:
namespace Test
{
public partial class EditTesting : Window
{
private Entity _myEntity;
public EditTesting()
{
InitializeComponent();
_myEntity = new Entity();
this.DataContext = _myEntity;
}
private void Cancel_Click(object sender, RoutedEventArgs e)
{
_myEntity.Name = string.Empty;
_myEntity.IsDirty = false;
}
}
}
I Have two views in MVVM(WPF). First View contains two Text boxes: User Name, Password, second view is having two Buttons: Submit and Clear. Both Views now set on On Form. When I press 'Clear' button both textboxes are cleared and in Submit a message of UserName and Password is displayed. I am using only MVVM+WPF, not prism.
ModelView Of First View:
class LoginView:ViewModelBase
{
string _userName;
public string UserName
{
get {return _userName ; }
set {
if (_userName != value)
{
_userName = value;
}
base.OnPropertyChanged(UserName);
}
}
string _Pwd;
public string PWD
{
get { return _Pwd; }
set
{
_Pwd = value;
base.OnPropertyChanged(_Pwd);
}
}
}
and For Button
class ButtonHandler
{
private DelegateCommand _ClearData;
public ICommand ClearCommand
{
get
{
if (_ClearData == null)
{
_ClearData = new DelegateCommand(ClearText);
}
return _ClearData;
}
}
LoginView lg = new LoginView();
private void ClearText()
{
lg.UserName = "";
lg.PWD = "";
}
}
and View Code of First Control
<Label Content="Login" Grid.Row="0" Grid.Column="1" VerticalAlignment="Top" HorizontalAlignment="Left"
FontFamily="Georgia" FontSize="24" FontWeight="UltraBold" ></Label>
<Label Content="User Name" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left"></Label>
<Label Content="Password" Grid.Row="2" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left"></Label>
<TextBox Name="username" Grid.Row="1" Grid.Column="1" Margin="100,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="{Binding Path=UserName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" ></TextBox>
<TextBox Name="pwd" Grid.Row="2" Grid.Column="1" Margin="100,0,0,0" VerticalAlignment="Center" HorizontalAlignment="Left" Text="{Binding Path=PWD,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Separator Grid.Row="0" Grid.Column="1" Height="5" Margin="0,40,0,0" Background="Green"></Separator>
and Button View
<Button x:Name="Submit" Content="Submit" Grid.Column="1"></Button>
<Button x:Name="Clear" Content="Clear" Grid.Column="2"
Command="{Binding Path=ClearCommand, Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}" >
</Button>
Why it is not working?
You are not using the MVVM pattern correctly, with this pattern the ViewModel should not have a reference to the View. A command is part of your ViewModel, therefore your reference to LoginView violates the pattern.
So you have two input fields and a button? for this I would have a single ViewModel and a single View. The ViewModel would expose two string properties (username & password) and a command that binds to the clear button. When the command executes it would clear the username and password texts on the ViewModel. The View will then update accordingly.
The basic principle of MVVM is to have a class that the view can bind to that has all the application logic inside of it. One of the main reasons is to have a separation of concerns. So if you want a username you expose a property that the view binds to and then when you want to log in you create a function that uses those bound values to submit to you business logic layer of your application.
This would seem to be one way to utilize MVVM in your example:
public class LoginViewModel
{
public string UserName {get;set;}//Implement INotifyPropertyChanged
public string PWD {get;set;}
private DelegateCommand _ClearData;
public ICommand ClearCommand
{
get
{
if (_ClearData == null)
{
_ClearData = new DelegateCommand(ClearText);
}
return _ClearData;
}
}
private void ClearText()
{
UserName = "";
PWD = "";
}
}
and then in your xaml:
<TextBox Text={Binding UserName} />
<TextBox Text={Binding PWD} />
<Button Command={Binding ClearCommand}/>
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>