i have the following code:
XAML Snippet:
<TextBox HorizontalAlignment="Left" Height="23" Margin="10,57,0,0" TextWrapping="Wrap" Text="{Binding Name}" VerticalAlignment="Top" Width="140">
<TextBox.DataContext>
<ViewModels:FilterViewModel/>
</TextBox.DataContext>
</TextBox>
<Button Content="Filtern" HorizontalAlignment="Left" Margin="420,57,0,0" VerticalAlignment="Top" Width="75" Command="{Binding FilterButton}" CommandParameter="{Binding Filter}">
<Button.DataContext>
<ViewModels:FilterViewModel/>
</Button.DataContext>
</Button>
FilterViewModel.cs:
class Button : ICommand
{
public delegate void ICommandOnExecute(object parameter);
public delegate bool ICommandOnCanExecute(object parameter);
private ICommandOnExecute _execute;
private ICommandOnCanExecute _canExecute;
public Button(ICommandOnExecute onExecuteMethod, ICommandOnCanExecute onCanExecuteMethod)
{
_execute = onExecuteMethod;
_canExecute = onCanExecuteMethod;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute.Invoke(parameter);
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
}
class FilterViewModel
{
public ICommand FilterButton { get; set; }
public FilterViewModel()
{
this.FilterButton = new Button(FilterExecute, canExecute);
}
public bool canExecute(object parameter)
{
return true;
}
public void FilterExecute(object paramter)
{
Console.WriteLine("Test: " + name);
}
private String name;
public String Name
{
get
{
return name;
}
set
{
Console.WriteLine(value);
name = value;
}
}
}
So when i click the button i want the content of the textbox printed to the console. e.g. input = "123" -> output should be "Test: 123".
However it doesn't matter what i am writing into the textbox, the result is always only the output: "Test: ". The value of the property "name" is ignored completely.
Thanks for your help!
You seem to be working with two instances of the view model. Move the definition to the MainWindow (or similar top-level parent) containing the TextBox and the Button. The DataContext value is then inherited by all child elements.
<Window.DataContext>
<ViewModels:FilterViewModel />
</Window.DataContext>
Then you can remove the definitions for the TextBox and the Button.
Related
I have a simple app, that should add SelectedItem from ComboBox to ListBox.
I have Model:Player
public class Player
{
public int ID { get; set; }
public string Name { get; set; }
private bool _isSelected = false;
public bool IsSelected
{
get { return _isSelected; }
set { _isSelected = value; }
}
}
And ObservableCollection property in my ViewModel (Players)
public class ViewModel
{
public ObservableCollection<Player> Players { get; set; }
public ObservableCollection<Player> PlayersInTournament { get; set; } = new ObservableCollection<Player>();
public ICommand AddPlayerCommand { get; set; }
public ViewModel()
{
DataAccess access = new DataAccess();
Players = new ObservableCollection<Player>(access.GetPlayers());//GetPlayers from DataBase
AddPlayerCommand = new RelayCommand(AddPlayer, CanAddPlayer);
}
private void AddPlayer()
{
//Something like PlayersInTournamen.Add(SelectedPlayer);
}
private bool CanAddPlayer()
{
bool canAdd = false;
foreach(Player player in Players)
{
if (player.IsSelected == true)
canAdd = true;
}
return canAdd;
}
}
Property(ItemSource) of my ComboBox is bound to the Players collection. When the application is Loaded my ComboBox is filled with objects from DataBase and when I select one of them it is displayed in my ReadOnly TextBox. I achieved this by binding the Text property to the ItemSelected.Name property of ComboBox. There is an Add button in the app that add selected player to the tournament(ListBox)(the app is about tournament). ListBox's ItemSource is PlayersInTournament collection(see in ViewModel).
XAML(DataContext of Window is set to ViewModel instance after InitializeComponents()):
<Window x:Class="ComboBoxDemoSQL.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:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:ComboBoxDemoSQL"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel>
<StackPanel HorizontalAlignment="Center"
Orientation="Horizontal" Margin="0 40 0 10">
<TextBox x:Name="HoldPlayerTextBox"
Width="100"
Text="{Binding ElementName=PlayersComboBox, Path=SelectedItem.Name}"
IsReadOnly="True">
</TextBox>
<ComboBox Name="PlayersComboBox"
VerticalAlignment="Top"
Margin="10 0 0 0"
HorizontalAlignment="Center" Width="100"
ItemsSource="{Binding Players}"
DisplayMemberPath="Name"
Text="Select player"
IsEditable="True"
IsReadOnly="True"/>
</StackPanel>
<Button Content="Add" Margin="120 0 120 0"
Command="{Binding AddPlayerCommand}"/>
<ListBox Margin="10" ItemsSource="{Binding PlayersInTournament}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding ID}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Grid>
Photo to understand better:
So basically there are 2 problems:
I don't know how to add to the PlayersInTournament collection a
player that is selected in ComboBox because I can't get the name
of that Player from TexBox(because its' Text property is bound to
another Property)
I don't know how to disable Add Button(CanAddPlayer method) when
there is no Player selected, I tried by adding IsSelected(see
Player model) property, but for it to work I have to bind to any
property in View that would change it, but I don't know which
property can be used for this thing.
ICommand implementation:
public class RelayCommand : ICommand
{
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private Action methodToExecute;
private Func<bool> canExecuteEvaluator;
public RelayCommand(Action methodToExecute, Func<bool> canExecuteEvaluator)
{
this.methodToExecute = methodToExecute;
this.canExecuteEvaluator = canExecuteEvaluator;
}
public RelayCommand(Action methodToExecute)
: this(methodToExecute, null)
{
}
public bool CanExecute(object parameter)
{
if (this.canExecuteEvaluator == null)
{
return true;
}
else
{
bool result = this.canExecuteEvaluator.Invoke();
return result;
}
}
public void Execute(object parameter)
{
this.methodToExecute.Invoke();
}
}
May I suggest the following.
You can override the ToString() method of your Player class to ease display in your ComboBox e.g.:
public class Player
{
public string Name { get; set; }
public override string ToString()
{
return Name;
}
}
By default ComboBox binding will call the ToString() method of whatever property it is bound to.
If you bind ComboBox.SelectedItem to a new Player property in the ViewModel, you can clear the selected player text in the ComboBox from code in the ViewModel.
If you add a CommandParameter to your Button binding, you can pass the selected player instance to the command, but this isn't strictly needed once you have a bound property in your ViewModel.
Thus your XAML becomes something like this:
<ComboBox x:Name="ComboBox"
HorizontalAlignment="Left"
Margin="0,0,0,0"
VerticalAlignment="Top"
Width="100"
Text="Select player"
SelectedItem="{Binding SelectedPlayer}"
ItemsSource="{Binding Players}"/>
<Button x:Name="ButtonAddPlayer"
Content="Add"
Command="{Binding AddPlayerCommand}"
CommandParameter="{Binding SelectedPlayer}"
HorizontalAlignment="Left"
Margin="62,176,0,0"
VerticalAlignment="Top"
Width="75"/>
And your ViewModel contains:
public ObservableCollection<Player> PlayersInTournament { get; set; }
public ObservableCollection<Player> Players { get; set; }
private Player _selectedPlayer;
public Player SelectedPlayer
{
get => _selectedPlayer;
set => SetField(ref _selectedPlayer, value);
}
public ICommand AddPlayerCommand { get; set; }
private bool CanAddPlayer(object obj)
{
return SelectedPlayer != null;
}
private void AddPlayer(object param)
{
if (param is Player player)
{
PlayersInTournament.Add(player);
Players.Remove(player);
SelectedPlayer = null;
};
}
Note that in the above code, as a player is added to the tournament list it is removed from the available players list preventing reselection of the same player.
Setting the SelectedPlayer property to null not only clears the ComboBox.SelectedItem display but also disables the Add button.
Also if you are likely to have several properties that you implement a helper function to handle your INotifyPropertyChanged events.
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
You can use CommandParameter in xaml:
<Button Content="Add" Margin="120 0 120 0"
Command="{Binding AddPlayerCommand}"
CommandParameter="{Binding Path=SelectedItem, Source=PlayersComboBox}"/>
in your ViewModel:
private ICommand _addPlayerCommand;
public ICommand AddPlayerCommand
{
get
{
if (_addPlayerCommand== null)
{
_addPlayerCommand= new RelayCommand(param => OnAddPlayerClicked(param));
}
return _addPlayerCommand;
}
}
private void AddPlayer(object param)
{
Player selectedPlayer = (player)param;
PlayersInTournamen.Add(SelectedPlayer);
}
RelayCommand:
public class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
/// <summary>
/// Creates a new command that can always execute.
/// </summary>
/// <param name="execute">The execution logic.</param>
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Creates a new command.
/// </summary>
/// <param name="execute">The execution logic.</param>
/// <param name="canExecute">The execution status logic.</param>
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
return _canExecute == null ? true : _canExecute(parameters);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameters)
{
_execute(parameters);
}
}
I hope this helps.
I am beginner in MVVM. I am writing simple app called Members. This is my member class (model):
class Member: INotifyPropertyChanged
{
public Member(string name)
{
Name = name;
_infoCommand = new InfoCommand(this);
}
string _name;
public string Name
{
get
{
return _name;
}
set
{
_name= value;
notify("Name");
notify("CanShowInfo");
}
}
public override string ToString()
{
return Name;
}
public event PropertyChangedEventHandler PropertyChanged;
void notify(string property_name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property_name));
}
}
private ICommand _infoCommand;
public ICommand InfoCommand
{
get
{
return _infoCommand;
}
set
{
_infoCommand = value;
}
}
public bool CanShowInfo
{
get
{
return _infoCommand.CanExecute(null);
}
}
}
This is my InfoCommand class:
class InfoCommand : ICommand
{
Member _member;
public InfoCommand(Member member)
{
_member = member;
}
public bool CanExecute(object parameter)
{
if (_member.Jmeno.Length > 0)
return true;
else
return false;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show("I am " + _member.Name);
}
}
This is my MemberViewModel class:
class MembersViewModel : INotifyPropertyChanged
{
ObservableCollection<Member> _members = new ObservableCollection<Member>();
public MembersViewModel()
{
Members.Add(new Member("Member1"));
Members.Add(new Member("Member2"));
Members.Add(new Member("Member3"));
Members.Add(new Member("Member4"));
Members.Add(new Member("Member5"));
}
public event PropertyChangedEventHandler PropertyChanged;
protected void notify(string property_name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property_name));
}
Member _selectedMember;
public Member SelectedMember
{
get
{
return _selectedMember;
}
set
{
_selectedMember= value;
notify("SelectedMember");
}
}
public ObservableCollection<Member> Members
{
get
{
return _members;
}
set
{
_members = value;
}
}
AddCommand _addCommand;
public AddCommand AddCommand
{
get
{
if (_addCommand == null)
_addCommand = new AddCommand(this);
return _addCommand;
}
}
}
This is my AddCommand:
class AddCommand : ICommand
{
MembersViewModel _vm;
public AddCommand(MembersViewModel vm)
{
_vm = vm;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_vm.Members.Add(new Member("New Member")); //<-------------------------
}
}
And finally my View:
<Window x:Class="mvvm_gabriel.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:mvvm_gabriel.ViewModel"
Title="MainWindow" Height="482" Width="525">
<Window.Resources>
</Window.Resources>
<Window.DataContext>
<ViewModels:MembersViewModel />
</Window.DataContext>
<Grid>
<ListView ItemsSource="{Binding Members}"
SelectedItem="{Binding SelectedMember, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Name}" />
<Button Grid.Column="1" Content="Info" Width="50" HorizontalAlignment="Left" Command="{Binding InfoCommand}" IsEnabled="{Binding Path=CanShowInfo, Mode=OneWay}" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBox Text="{Binding SelectedMember.Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="Add" Command="{Binding AddCommand}" />
</Grid>
When I click some member in my ListView, his name is shown in TextBox. Now I can edit this name and property of my Member object is updated automatically. When I delete name of some member completely (string.Length == 0), Info button in my member template is disabled.
I can also add new members by clicking Add button. Member is added to my observable collection and automatically shown in ListView.
Everything works perfectly as far as here.
But now: look at line marked like this <---------------------- in my AddCommand.Execute method. When I add new member to my collection, I automatically give him name "New Member" and everything works fine. I can then adit my member's name and my button is disabled automatically as described above. But when I give empty string as the name for new member in constructor on marked line, enabling of my Info button quits working. I can give my new member any name and my Info button is still disabled.
Can anyone explain it and suggest some solution, please?
Your button in the mainwindow is binding the IsEnabled of the button to a property in the model, but the command binding will also cause the button to interrogate the CanExecute() of the command.
<Button Grid.Column="1" Content="Info" Width="50" HorizontalAlignment="Left" Command="{Binding InfoCommand}" IsEnabled="{Binding Path=CanShowInfo, Mode=OneWay}" />
This can lead to confusing behavior, as seen in your case.
You can basically remove the IsEnabled binding of the button, and add the property changed handler to the InfoCommand.
public class InfoCommand : ICommand
{
Member _member;
public InfoCommand(Member member)
{
_member = member;
_member.PropertyChanged += _member_PropertyChanged;
}
private void _member_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == "Name")
RaiseCanExecuteChanged();
}
private void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
public bool CanExecute(object parameter)
{
if (_member.Name.Length > 0)
return true;
else
return false;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MessageBox.Show("I am " + _member.Name);
}
}
I'm a newbie so excuse my question if it's too fade or if it's unclear.
any way, In my UI (WPF), i have a ListView that i created containing an observable collection of Type Collection = new ObservableCollection<type> and i have two Buttons "Add" & "Delete" I want to do this:
1-Whenever i select an item from my ListView in the UI(just click on it) , and click the "Add" button, the item is stored in a List called Scenario (Scenario = new List<type>).
2- Whenever i click the "Delete" button the Scenario list becomes empty.
I've tried something out but it doesn't work like it should, i can only add one item to the list Scenario and then it is blocked (when debugging) in
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
Can someone tell me why? and how to fix it?
As for the "Delete" Button i didn't get to it yet because the other one doesn't work properly.
if you can propose a new solution or a solution for this problem i would be so thankful.
This is what i've done so far.
This is the code in the MainWindowModel :
private ObservableCollection<Type> _collection,_scenario;
public MainWindowModel()
{
Collection = new ObservableCollection<type>();
Scenario=new ObservableCollection<Type>();
DeleteCommand = new RelayCommand(o => DeleteExecute());
AddTypeCommand = new RelayCommand(o => AddTypeExecute());
}
private Type _isSelected;
public Type IsSelected;
{
get { return _isSelected; }
set
{
if (_isSelected != value)
{
_isSelected = value;
RaisePropertyChanged(nameof(IsSelected));
}
}
}
public ICommand DeleteCommand
{
get;
private set;
}
private RelayCommand _addTypeCommand;
public ICommand AddTypeCommand
{
get
{
if (_addTypeCommand == null)
{
_addTypeCommand = new RelayCommand(o => AddTypeExecute());
}
return _addTypeCommand;
}
set { }
}
private void DeleteExecute()
{
Scenario.Clear(); // Would this Work ?
}
private bool CanExecuteAddTypeCommand()
{
return true;
}
private void AddTypeExecute()
{
if (IsSelected != null)
{
Scenario.Add(IsSelected);
}
}
public ObservableCollection<Type> collection
{
get { return _collection; }
set { SetPropertyAndFireEvent(ref _collection, value); }
}
public ObservableCollection<Type> Scenario
{
get { return _scenario; }
set { SetPropertyAndFireEvent(ref _scenario, value); }
}
as for the MainWindowModel
<Window.DataContext>
<viewModels:MainWindowModel />
</Window.DataContext>
<Grid>
<ListView Grid.Row="2"
Grid.Column="0"
ItemsSource="{Binding Collection}"
SelectedItem="{Binding IsSelected}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Command="{Binding AddTypeCommand}"
Width="100"
Height="100"
Content="Add"
Grid.Row="0"
Grid.Column="2"/>
<Button Command="{Binding DeleteCommand}"
Content="Delete"
Width="100"
Height="100"
Grid.Row="2"
Grid.Column="2" />
</Grid>
As for the RelayCommand.cs
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
//Notifies the Button bounded to the ICommand that the value returned by CanExecute has changed
public event EventHandler CanExecuteChanged
{
//raised whenever the commandmanager thinks that something has changed that will affect the ability of commands to execute
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
Try passing selectedItem as parameter for command,you dont pass anything and try to add...
name your ListView:
<ListView x:Name="listView"
and pass selectedItem as commandParameter
<Button Command="{Binding AddTypeCommand}"
CommandParameter="{Binding ElementName=listView, Path=SelectedItem}"
Width="100"
Height="100"
Content="Add"
Grid.Row="0"
Grid.Column="2" />
and then do your logic for adding, now you have parameter to add to your list.
EDIT: Here is some code that works, as i have understand that u need something like this.
ViewModel _> where all collection and command are created:
public class TestVM : INotifyPropertyChanged
{
public TestVM()
{
ListOne = new ObservableCollection<string>()
{
"str1","str2","str3"
};
// command
AddTypeCommand = new RelayCommand(OnAddExecute);
DeleteTypeCommand = new RelayCommand(OnDeleteExecuted);
}
private void OnDeleteExecuted()
{
ListTwo.Clear();
}
private void OnAddExecute()
{
if (SelectedItem != null)
{
ListTwo.Add(SelectedItem);
}
}
private string _selectedItem;
public string SelectedItem
{
get { return _selectedItem; }
set
{
if (_selectedItem != value)
{
_selectedItem = value;
OnPropertyChanged();
}
}
}
private ObservableCollection<string> _listOne;
public ObservableCollection<string> ListOne
{
get
{
return _listOne;
}
set
{
if (_listOne != value)
{
_listOne = value;
OnPropertyChanged();
}
}
}
public ObservableCollection<string> ListTwo { get; set; } = new ObservableCollection<string>();
public RelayCommand AddTypeCommand { get; private set; }
public RelayCommand DeleteTypeCommand { get; private set; }
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public virtual void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
RellayCommand how i implement it:
public class RelayCommand : ICommand
{
private Action _executeMethod;
private Func<bool> _canExecuteMethod;
#region RelayCommand ctor
public RelayCommand(Action executeMethod)
{
_executeMethod = executeMethod;
}
public RelayCommand(Action executeMethod, Func<bool> canExecuteMethod)
{
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
}
#endregion
public void RaiseCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
#region ICommand Members
bool ICommand.CanExecute(object parameter)
{
if (_canExecuteMethod != null)
return _canExecuteMethod();
if (_executeMethod != null)
return true;
return false;
}
void ICommand.Execute(object parameter)
{
if (_executeMethod != null)
_executeMethod();
}
public event EventHandler CanExecuteChanged = delegate { };
#endregion
}
//--------------------------------------------------------------------------------------------
public class RelayCommand<T> : ICommand
{
private Action<T> _executeMethod;
private Func<T, bool> _canExecuteMethod;
#region RelayCommand ctor
public RelayCommand(Action<T> executeMethod)
{
_executeMethod = executeMethod;
}
public RelayCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
{
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
}
#endregion
public void RaiseCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
#region ICommand Members
bool ICommand.CanExecute(object parameter)
{
var Tparam = (T)parameter;
if (_canExecuteMethod != null)
return _canExecuteMethod(Tparam);
if (_executeMethod != null)
return true;
return false;
}
void ICommand.Execute(object parameter)
{
if (_executeMethod != null)
_executeMethod((T)parameter);
}
public event EventHandler CanExecuteChanged = delegate { };
#endregion
}
And MainWindow.xaml just to show purpose. Selecting on one item in 1rst list and pressing button Add will add it to second ListView. DeleteButton will clear second list.
<Window x:Class="WpfApp5.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:WpfApp5"
mc:Ignorable="d"
Title="MainWindow"
Height="350"
Width="525">
<Window.DataContext>
<local:TestVM />
</Window.DataContext>
<Grid>
<ListView x:Name="listViewOne"
ItemsSource="{Binding ListOne}"
SelectedItem="{Binding SelectedItem,Mode=TwoWay}"
Width="100"
Height="200"
Margin="17,17,400,105" />
<ListView x:Name="listViewTwo"
ItemsSource="{Binding ListTwo}"
Width="100"
Height="200"
Margin="339,17,78,105" />
<Button Command="{Binding AddTypeCommand}"
Content="Add"
Grid.Row="0"
Margin="208,111,198,178" />
<Button Command="{Binding DeleteTypeCommand}"
Content="Delete"
Grid.Row="0"
Margin="208,157,198,132" />
</Grid>
</Window>
I want to accomplish a simple task.
Need to implement textbox lostfocus, As the user puts in data, as soon as one field is filled and he reaches on to the next, it should fire a validation function on the previous field.
Also, I am using MVVM pattern.
So I have this class
public class data : INotifyPropertyChanged
{
public string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
OnPropertyChanged("Name");
}
}
public string firstname;
public string FirstName
{
get
{
return firstname;
}
set
{
firstname = value;
OnPropertyChanged("FirstName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
// Raise the PropertyChanged event
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
In the Viewmodel I got this
data1 = new data() { name = "Eddie Vedder", firstname = "Eddie" }; //this line in initialization
public data _data1;
public data data1
{
get { return _data1; }
set
{
_data1 = value;
ValidateThis();
NotifyPropertyChanged(new PropertyChangedEventArgs("data1"));
}
}
In Xaml:
<StackPanel Orientation="Horizontal" >
<Label Width="90" Content="Name" Height="28" HorizontalAlignment="Left" Name="lblName" VerticalAlignment="Top" />
<TextBox Text="{Binding Path=data1.name, UpdateSourceTrigger=LostFocus, Mode=TwoWay}" MaxLength="40" TabIndex="2" Height="25" Margin="0,3,0,0" HorizontalAlignment="Left" Name="txtName" VerticalAlignment="Top" Width="200" />
</StackPanel>
<StackPanel Orientation="Horizontal" >
<Label Width="90" Content="First Name" Height="28" HorizontalAlignment="Left" Name="lblFirstName" VerticalAlignment="Top" />
<TextBox Text="{Binding Path=data1.firstname, UpdateSourceTrigger=LostFocus, Mode=TwoWay}" MaxLength="40" TabIndex="3" Name="txtFirstName" Height="25" Margin="0,3,0,0" VerticalAlignment="Top" Width="200" >
</TextBox>
</StackPanel>
My binding is working as it shoes the default name Eddie Vedder when I execute it.
When I debug it, it doesn't enter the class data.
As you use MVVM pattern I assume that you have some binding to view model property and it looks like:
Xaml:
<StackPanel>
<!--Pay attention on UpdateSourceTrigger-->
<TextBox Text="{Binding Text, UpdateSourceTrigger=LostFocus}" />
<TextBox />
</StackPanel>
c#:
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
Validate(); // Desired validation
OnPropertyChanged();
}
}
If you set UpdateSourceTrigger to LostFocus, property changed will be fired when you lost focus.
There is a very nice article for this: MVVM WPF commands
First create a class: the DelegateCommand.cs
public class DelegateCommand<T> : System.Windows.Input.ICommand where T : class
{
private readonly Predicate<T> _canExecute;
private readonly Action<T> _execute;
public DelegateCommand(Action<T> execute)
: this(execute, null)
{
}
public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
return true;
return _canExecute((T)parameter);
}
public void Execute(object parameter)
{
_execute((T)parameter);
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
Add the delegate into your ViewModel:
private readonly DelegateCommand<string> _lostFocusCommand;
public DelegateCommand<string> LostFocusCommand
{
get { return _lostFocusCommand; }
}
private string _input;
public string Input
{
get { return _input; }
set
{
_input = value;
}
}
And initialize it in the constructor of the ViewModel:
// _input will be the property you have with a binding to the textbox control in the view.
// in the canExecute part add the conditions you want to use to check if the lostfocus command will be raised
_lostFocusCommand = new DelegateCommand<string>(
(s) => { /* perform some action */
MessageBox.Show("The lostfocuscommand works!");
}, //Execute
(s) => { return !string.IsNullOrEmpty(_input); } //CanExecute
);
View:
you need to add the following namespace
xmlns:b="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
And the control
<TextBox Grid.Column="0"
Text="{Binding Input, UpdateSourceTrigger=PropertyChanged}">
<b:Interaction.Triggers>
<b:EventTrigger EventName="LostFocus">
<b:InvokeCommandAction Command="{Binding LostFocusCommand}" CommandParameter="{Binding Input}"/>
</b:EventTrigger>
</b:Interaction.Triggers>
</TextBox>
Well this kinda did the trick
public class Validate
{
public static ErrorProperties ep = new ErrorProperties();
public static bool ValidateThis(string PropertyName, string PropertyValue)
{
if (PropertyValue.Length > 10)
{
ep.ErrorPropertyName = PropertyName;
return true;
}
return false;
}
}
public class ErrorProperties
{
public string ErrorPropertyName { get; set; }
public string Error { get; set; }
}
public class data : INotifyPropertyChanged
{
private ObservableCollection<ErrorProperties> _ErrorList = new ObservableCollection<ErrorProperties>();
public ObservableCollection<ErrorProperties> ErrorList
{
get
{
return _ErrorList;
}
set
{
if (_ErrorList != value)
{
_ErrorList = value;
OnPropertyChanged("ErrorList");
}
}
}
private string _Name;
public string Name
{
get
{
return _Name;
}
set
{
if (_Name != value)
{
_Name = value;
if (Validate.ValidateThis("Name", value))
ErrorList.Add(Validate.ep);
OnPropertyChanged("Name");
}
}
}
private string _FirstName;
public string FirstName
{
get
{
return _FirstName;
}
set
{
if (_FirstName != value)
{
_FirstName = value;
if (Validate.ValidateThis("FirstName", value))
ErrorList.Add(Validate.ep);
OnPropertyChanged("FirstName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
// Raise the PropertyChanged event
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Hi i want to bind button with other listView.Item. What i want is to have something like we have on stackoverflow.
But i have problem with having increasing/decreasing value. I have event Click but i dont knew how to get corresponding item on list and increase/decrease value.
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label Width="706" Height="75" Content="{Binding feedback}"/>
<StackPanel Orientation="Vertical">
<Button Name="buttonUp" Content="^" Command="{Binding upVoteCommand}" />
<Label HorizontalContentAlignment="Center" Width="50" Content="{Binding grade}"/>
<Button Name="buttonDown" Content="v" Command="{Binding upVoteCommand}"/>
</StackPanel>
</StackPanel>
<Label>-</Label>
</StackPanel >
EDIT
class A {
public string feedback {
get;
set;
}
public int grade {
get;
set;
}
private ICommand _upVoteCommand;
private ICommand _downVoteCommand;
public ICommand upVoteCommand {
get {
return _upVoteCommand;
}
set {
_upVoteCommand = value;
}
}
public ICommand downVoteCommand {
get {
return _downVoteCommand;
}
set {
_downVoteCommand = value;
}
}
}
EDIT I used this button.Commmand but still it not working. I dont knew what to do with this commands.
First you'll need your implementation of ICommand so you can bind commands from view model to controls, something like this:
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute) : this(execute, null) { }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null) throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter) { _execute(parameter); }
}
then in you class, where you publish Feedback, you'll need to publish 2 new RelayCommand for up/down vote that will modify Feedback property accordingly. Below you can find my class that I used for tests:
public class MyClass : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
private int _feedback = 0;
public int Feedback
{
get { return _feedback; }
set
{
if (_feedback == value) return;
_feedback = value;
OnPropertyChanged("Feedback");
}
}
private RelayCommand _upVoteCmd;
public ICommand UpVoteCmd
{
get
{
if (_upVoteCmd == null) _upVoteCmd = new RelayCommand(o => Feedback += 1);
return _upVoteCmd;
}
}
private RelayCommand _downVoteCmd;
public ICommand DownVoteCmd
{
get
{
if (_downVoteCmd == null) _downVoteCmd = new RelayCommand(o => Feedback -= 1);
return _downVoteCmd;
}
}
}
and then you bind your new commands in XAML like this:
<Button Content="+" Command="{Binding Path=UpVoteCmd}"/>
<TextBlock Text="{Binding Path=Feedback}"/>
<Button Content="-" Command="{Binding Path=DownVoteCmd}"/>
RoutedEvents don't work so easily with DataTemplates, because you don't have a code behind where your event code could be placed. While there are ways to do that, you can just use Commands to do the same. In the view model for each item (i just assume you use MVVM) create properties called UpVoteCommand and DownVoteCommand of type ICommand, DelegateCommands are quiet handy for this. Bind them to the Command property and remove the Click handler in your DataTemplate.
[EDIT]
Small example of a possible Viewmodel for one entry in the list, which can be up or downvoted.
class MyEntryViewModel : INotifyPropertyChanged
{
public MyEntryViewModel()
{
UpVoteCommand = new DelegateCommand(OnUpVoteCommand);
}
public int Votes
{
get {return mVotes;}
set {mVotes = value; RaiseProperty("Votes");}
}
public ICommand UpVoteCommand
{
get; private set;
}
void OnUpVoteCommand(object aParameter)
{
Votes++;
}
}
i left the implementation of INotifyPropertyChanged and the down vote command for sake of simplicity.