Communicate Between Two View in WPF MVVM Only - wpf

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}/>

Related

Caliburn.Micro - Passing combobox items to a button

I'm trying to pass items from my Combobox (which is binded to my Model object's lists) to my button. My problem is that I'm new to Caliburn.Micro + WPF and not quite sure how to subscribe/pass the desired values to my button (like sending strings of a PropetyName to a Button(string propetyName)).
ViewModel code:
class ShellViewModel : Screen
{
private DataModel _fileInFolder;
private BindableCollection<DataModel> _data;
public ShellViewModel()
{
// .GetData() preforms the objects' initialization
DataModel dataOutput = new DataModel();
Data = new BindableCollection<DataModel>(dataOutput.GetData());
}
public BindableCollection<DataModel> Data
{
get
{
return _data;
}
set
{
_data = value;
NotifyOfPropertyChange(() => Data);
}
}
public DataModel FileInFolder
{
get { return _fileInFolder; }
set
{
_fileInFolder = value;
NotifyOfPropertyChange(() => FileInFolder);
}
}
//This is where the items will be passed to.
public void OpenFile()
{
}
}
XAML code:
<Grid>
<!-- Folders -->
<ComboBox ItemsSource="{Binding Data}" SelectedItem="{Binding FileInFolder}"
HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="250">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Folders}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!-- Files -->
<ComboBox x:Name="FileInFolder_Files"
HorizontalAlignment="Left" Margin="280,10,0,0" VerticalAlignment="Top" Width="250"/>
<!-- Open File -->
<Button x:Name="OpenFile"
Content="Open File" HorizontalAlignment="Left" Margin="560,10,0,0" VerticalAlignment="Top" Width="90">
</Button>
</Grid>
Sorry if my description is vague/missing more clarification, I'm a new user here!
FileInFolder is the selected item of the combo.
OpenFile is in the same class and can therefore reference FileInFolder.
public void OpenFile()
{
var whatever = FileInFolder.SomeProperty;
// etc
}
You probably want some null checking in there.

Wpf change xaml when object for binding changes

I am trying to get from my database one item, and when clicking on the next or previous button, I would like to get the next item out of my database by increasing its ID. I'm at the point of having my first item in my card, but when I click on previous or next, nothing happens.
I have in xaml:
<smtx:XamlDisplay Key="cards_1" Margin="4 4 0 0">
<materialDesign:Flipper Style="{StaticResource MaterialDesignCardFlipper}">
<materialDesign:Flipper.FrontContent>
<Grid Height="350" Width="200">
<Grid.RowDefinitions>
<RowDefinition Height="250" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<materialDesign:ColorZone Mode="PrimaryMid" VerticalAlignment="Stretch">
<materialDesign:PackIcon Kind="AccountCircle" Height="128" Width="128"
VerticalAlignment="Center" HorizontalAlignment="Center" />
</materialDesign:ColorZone>
<StackPanel Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{Binding CurrentGebruiker.Naam}"></TextBlock>
<Button Style="{StaticResource MaterialDesignFlatButton}" Foreground="DarkGoldenrod"
Command="{x:Static materialDesign:Flipper.FlipCommand}"
Margin="0 4 0 0"
>SHOW DETAILS</Button>
</StackPanel>
</Grid>
</materialDesign:Flipper.FrontContent>
And my viewmodel:
public ZoekMatchViewModel()
{
LeesGebruiker(1);
KoppelenCommands();
}
private Gebruiker currentGebruiker;
public Gebruiker CurrentGebruiker
{
get
{
return currentGebruiker;
}
set
{
currentGebruiker = value;
NotifyPropertyChanged();
}
}
private void KoppelenCommands()
{
NextCommand = new BaseCommand(VolgendeGebruiker);
PrevCommand = new BaseCommand(VorigeGebruiker);
}
public ICommand NextCommand { get; set; }
public ICommand PrevCommand { get; set; }
private void LeesGebruiker(int id)
{
//instantiƫren dataservice
ZoekMatchDataService zoekMatchDS =
new ZoekMatchDataService();
currentGebruiker = zoekMatchDS.GetGebruiker(id);
}
public void VolgendeGebruiker()
{
if (CurrentGebruiker != null)
{
int id = (currentGebruiker.ID) + 1;
LeesGebruiker(id);
}
}
public void VorigeGebruiker()
{
if (CurrentGebruiker != null)
{
int id = (currentGebruiker.ID) - 1;
LeesGebruiker(id);
}
}
My buttons:
<Button Command="{Binding PrevCommand}" Background="Transparent" BorderThickness="0" Height="50">
and
<Button Command="{Binding NextCommand}" Background="Transparent" BorderThickness="0" Height="50">
So the problem is that my xaml doesn't update to the new user when I click on next or previous buttons.
If you need more information, I'm happy to provide!
I think the "right" way to do this would be with a custom Selector. Selector is the base class used in WPF for a control that lets the user select one or more items from a list. For example: ComboBox and ListBox are both Selectors. If you've never made a custom control before this may be a bit complicated though.
There are a few simpler answers. You could make a class that holds all the data you want to display for a single option, and have your Window define a property of that type. Then you could bind all the variable parts of your interface to that property and just change the property value manually when the user clicks the left or right button. Of course you'd only be able to use this on one place, whereas if you made a custom control you could reuse it anywhere, any number of times.

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

Not getting updates as per Selected date in view contains Datagrid binded with Generic List?

I have used tab control for view my usercontrols..
in 1st usercontrol
I have used datagrid to diplay Record and for Binding I have used generic List.
When want to change this List as per selected date then that collection is changed in database and in viewmodel also as List's set propery get executes but in view when i selected new tab and then go back to prevois tab at that time List's get property executes & then i am able get view as per selected date.
My main view and which contain 1st usercontrol as 1st tab item is given below:
Xaml code for above view is given below:
<DataGrid
Background="Transparent"
CanUserAddRows="True"
CanUserReorderColumns="False"
ItemsSource="{Binding Model_Transactions_TransactionsDetails_Jama,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HeadersVisibility="Column">
</DataGrid>
<Grid DockPanel.Dock="Bottom" VerticalAlignment="Bottom" >
<Border BorderBrush="Black" BorderThickness="1" >
<Label HorizontalAlignment="Left" HorizontalContentAlignment="Right" Width="75" Content="{Binding SumOfWeightJama,UpdateSourceTrigger=PropertyChanged}" FontFamily="Segoe UI" FontWeight="Bold" FontSize="16" />
</Border>
</Grid>
<DataGrid
Background="Transparent"
CanUserAddRows="True"
CanUserReorderColumns="False"
ItemsSource="{Binding Model_Transactions_TransactionsDetails_Udhar,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
HeadersVisibility="Column">
</DataGrid>
<Grid DockPanel.Dock="Bottom" VerticalAlignment="Bottom">
<Border BorderBrush="Black" BorderThickness="1">
<Label Width="75" HorizontalAlignment="Left" HorizontalContentAlignment="Right" Content="{Binding SumOfWeightUdhar,UpdateSourceTrigger=PropertyChanged}" FontFamily="Segoe UI" FontWeight="Bold" FontSize="16"/>
</Border>
</Grid>
And View Model for above View is given below:
private DateTime _FilterDate ;
public DateTime FilterDate
{
get
{
return _FilterDate;
}
set
{
_FilterDate = value;
OnPropertyChanged("FilterDate");
Model_Transactions_TransactionsDetails_Jama = (ViewModel.AllDataCollactions.AllTransactionsDetails.Where(s => s.TransactionDate.Equals(FilterDate) && s.IsJama).OrderBy(s => s.TransactionsID)).ToList();
Model_Transactions_TransactionsDetails_Udhar = (ViewModel.AllDataCollactions.AllTransactionsDetails.Where(s => s.TransactionDate.Equals(FilterDate) && !s.IsJama).OrderBy(s => s.TransactionsID)).ToList();
}
}
public List<Model_TransactionsDetails> Model_Transactions_TransactionsDetails_Jama
{
get
{
return model_Transactions_TransactionsDetails_Jama;
}
set
{
model_Transactions_TransactionsDetails_Jama = value;
OnPropertyChanged("Model_Transactions_TransactionsDetails_Jama");
}
}
public List<Model_TransactionsDetails> Model_Transactions_TransactionsDetails_Udhar
{
get
{
return model_Transactions_TransactionsDetails_Udhar;
}
set
{
model_Transactions_TransactionsDetails_Udhar = value;
OnPropertyChanged("Model_Transactions_TransactionsDetails_Udhar");
}
}
public ViewModel_MasterBook()
{
FilterDate = DateTime.Now.AddDays(-1).Date;
InsertCommand = new RelayCommand(AddExecute, CanAdd);
}
Can any one help me How can i get view as per selected date immediately..
actually it should work i cant see an error. but when i use some kind of Lists in my WPF projects i use observablecollection with clear, add, delete.
but first i would change the binding
ItemsSource="{Binding Model_Transactions_TransactionsDetails_Jama,Mode=OneWay}"
because Mode=TwoWay makes no sense, you never set the itemssource from your datagrid to the viewmodel.
second i would change to ObservableCollection
public ObservableCollection<Model_TransactionsDetails> Model_Transactions_TransactionsDetails_Jama
{
get; private set;
}
with private setter because just initialize once.
//ctor
this.Model_Transactions_TransactionsDetails_Jama = new ObservableCollection<Model_TransactionsDetails>();
and then in your FilterDate setter fill the collection
this.Model_Transactions_TransactionsDetails_Jama.Clear();
var newdata = (ViewModel.AllDataCollactions.AllTransactionsDetails.Where(s => s.TransactionDate.Equals(FilterDate) && s.IsJama).OrderBy(s => s.TransactionsID)).ToList();
this.Model_Transactions_TransactionsDetails_Jama.AddRange(newdata);//AddRange is simply an extension method i wrote, you can simply use foreach and .Add()

How pass two parameters into EventToCommand

I have next xaml code.
<StackPanel Grid.Row="1" x:Name="spLogin">
<TextBlock Text="E-mail:"></TextBlock>
<TextBox Name="tbLogin" Text="{Binding User.Email, Mode=TwoWay}"></TextBox>
<TextBlock Text="Password:"></TextBlock>
<TextBox Name="tbPassword" Text="{Binding User.Password, Mode=TwoWay}"></TextBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Name="btnLogin" Content="Login">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="Click"
x:Name="SelectChangeEvent">
<Command:EventToCommand
Command="{Binding Login, Mode=TwoWay}" PassEventArgsToCommand="False" CommandParameter="{Binding}" />
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</Button>
<Button Name="btnClear" Content="Clear"/>
</StackPanel>
</StackPanel>
</Grid>
How I can pass tbLogin.Text and tbPassword.Text to ViewModel like as a field of one object User
public class User
{
public string Login{get;set;}
public string Password(get;set;)
}
Normally I would just say ... you don't need to, as the your ViewModel (deduced from your bindings) should contain both, a User property, and the Login command iself, so that you just can access the User property from your command's Excecute method.
However, I noticed that the user class your presented user class does not implement INotifyPropertyChanged. Therefore, the binding will not work correctly. To make it work you have two possibilities:
Implement INotifyPropertyChanged on your user class. -- Depending on your model and how it is generated this is not allways possible.
Duplicate the properties on your ViewModel and implementing INotifyPropertyChanged there. -- If you don't have control over the definition of you model (e.g. as it is generated by a web service proxy) this is the only way of doing it. But, even if you have control over your model this option is worth considering as gives your greater control over what is passed to your View.
So, the following sample assumes yoou go for the second option:
public class MvvmViewModel1 : ViewModelBase
{
private User _user;
#region [Login]
public const string LoginPropertyName = "Login";
public string Login {
get {
return this._user.Login;
}
set {
if (this._user.Login == value) {
return;
}
var oldValue = this._user.Login;
this._user.Login = value;
RaisePropertyChanged(LoginPropertyName);
}
}
#endregion
#region [Password]
public const string PasswordPropertyName = "Password";
public string Password {
get {
return this._user.Password;
}
set {
if (this._user.Password == value) {
return;
}
var oldValue = this._user.Password;
this._user.Password = value;
RaisePropertyChanged(PasswordPropertyName);
}
}
#endregion
#region [LoginCommand]
public RelayCommand _loginCommand;
public RelayCommand LoginCommand {
get {
return _loginCommand ?? (_loginCommand = new RelayCommand(
() => {
// perform your login action here
// access Login with: this.Login
// access Password with: this.Password
},
() => {
// can execute method - sample implementation
return (!string.IsNullOrEmpty(this.Login) && !string.IsNullOrEmpty(this.Password));
}
));
}
}
#endregion
}
Obviously you will have to modify your XAML to match this ViewModel:
<StackPanel Grid.Row="1" x:Name="spLogin">
<TextBlock Text="E-mail:"></TextBlock>
<TextBox Name="tbLogin" Text="{Binding Login, Mode=TwoWay}"></TextBox>
<TextBlock Text="Password:"></TextBlock>
<TextBox Name="tbPassword" Text="{Binding Password, Mode=TwoWay}"></TextBox>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Name="btnLogin" Content="Login">
<Interactivity:Interaction.Triggers>
<Interactivity:EventTrigger EventName="Click"
x:Name="SelectChangeEvent">
<Command:EventToCommand
Command="{Binding LoginCommand}" PassEventArgsToCommand="False" />
</Interactivity:EventTrigger>
</Interactivity:Interaction.Triggers>
</Button>
<Button Name="btnClear" Content="Clear"/>
</StackPanel>
</StackPanel>
Normally you only need to use CommandParameter if you are inside a data template (e.g. an item template inside a ListBox) and/or your command and your properties are not in the same ViewModel. In this case you have to define your command to accept a parameter:
#region [LoginCommand]
public RelayCommand _loginCommand;
public RelayCommand LoginCommand {
get {
return _loginCommand ?? (_loginCommand = new RelayCommand(
(p) => {
// perform your login action here
},
(p) => {
// can execute method - sample implementation
return (!string.IsNullOrEmpty(this.Login) && !string.IsNullOrEmpty(this.Password));
}
));
}
}
#endregion
And now you can use
CommandParameter="{Binding}"
inside your XAML to pass in the data context of the data template to your ViewModel.
And another side note: You normally do not need two way binding - except in the case when you want to write information back from your View to your ViewModel.
Also as a side note: You cannot generate/convert classes in XAML; so if you have a User class with Email/Password properties there in your ViewModel there is no way of creating a new user class with Login/Password properties out of thin air in XAML and pass that to your ViewModel. Binding is powerful but not allmighty ... ;-)
Since your tbLogin.Text and tbPassword.Text two-way bound to User.Email and User.Password correspondently you can simply bind CommandParameter to the User. Bigger question is: why do you need CommandParameter at all then your view model can access User property directly?

Resources