Why doesnt my button update to enabled after the command finishes? - c#

So I'm currently looking into commands and what I've done as of right now is that I made a command, bound it to a button and I've passed in a bool property which will indicate whether or not the button can execute the command.
The issue is that the button starts off as enabled, and as soon as I click it, it turns disabled, and then the person gets added to the ListView, but the button stays disabled.
I'm not sure if I need to add a UpdateSourceTrigger to the command, I thought the whole point of implementing a ObservableObject was to not have to do that.
What's the proper way of achieving what I'm trying to achieve?
The Command
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
The MainWindow (DataContext)
<Window x:Class="Commands.MainWindow"
...
Title="MainWindow"
Height="450"
Width="800">
<Window.DataContext>
<viewmodel:MainViewModel />
</Window.DataContext>
<StackPanel HorizontalAlignment="Center"
VerticalAlignment="Center">
<ListView MinHeight="100"
ItemsSource="{Binding PeopleCollection}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Content="Add"
Width="100"
Height="25"
Command="{Binding AddPersonCommand}" />
</StackPanel>
</Window>
The MainViewModel
internal class MainViewModel : ObservableObject
{
public ObservableCollection<PersonModel> PeopleCollection { get; set; }
/* A slow working command */
public RelayCommand AddPersonCommand { get; set; }
private bool _canExecute;
public bool CanExecute
{
get { return _canExecute; }
set
{
_canExecute = value;
NotifyPropertyChanged("CanExecute");
}
}
public MainViewModel()
{
CanExecute = true;
PeopleCollection = new ObservableCollection<PersonModel>();
AddPersonCommand = new RelayCommand(o => AddPersonToCollection(o), (p) => CanExecute);
}
private async void AddPersonToCollection(object o)
{
CanExecute = false;
await Task.Factory.StartNew(() =>
{
/* Simulate a heavy workload*/
Thread.Sleep(5000);
Application.Current.Dispatcher.BeginInvoke(() =>
{
PeopleCollection.Add(new PersonModel
{
Id = 0,
Name = "Foo bar"
});
});
/* Property needs to be updated from the main thread? */
//CanExecute = false;
});
CanExecute = false;
}
}
The ObservableObject
internal class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public void NotifyPropertyChanged([CallerMemberName] String propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

I'm failing to see how your Button is actually being marked as disabled. I see the CanExecute bool being manipulated, but your button doesn't have IsEnabled bound. Even if that is the case, as #PaulSinnema said, you are only setting CanExecute to false (besides the first set in the constructor). I would suggest trying to set CanExecute = true inside of your Task and inside your lambda function (in the BeginInvoke) so then you can ensure the timing is correct. As you currently have it, your AddPersonToCollection method is doing the following:
Sets CanExecute to false
Fires off a task and waits for it to complete
Sets CanExecute to false
Here's my suggestion, hopefully it helps:
private async void AddPersonToCollection(object o)
{
CanExecute = false;
await Task.Factory.StartNew(() =>
{
/* Simulate a heavy workload*/
Thread.Sleep(5000);
Application.Current.Dispatcher.BeginInvoke(() =>
{
PeopleCollection.Add(new PersonModel
{
Id = 0,
Name = "Foo bar"
});
// Setting it here ensures we flip it as soon as the collection has a person added
CanExecute = true;
});
});
// Remove this line
///CanExecute = false;
}

Related

[UWP/MVVM]Enable/Disable Button in RadDataGrid Data Template Column that have commands bound to them upon conditions

I have set a bool property and have bound it to the IsEnabled in the xaml but the ICommand CanExecute method overrides the IsEnabled in xaml, so my bool property is ineffective.
When I define the conditions within the CanExecute method in the view model, It either disables all buttons in which the method is bound to, or enables all of them.
Its a grid that displays 3 different buttons for each row, and each button goes to a new xaml screen. If there is no data for the particular condition on the row the button is on then the button needs to be disabled.
How do i go about setting this so that buttons are disabled upon a condition?
Custom Command:
public class CustomCommand : ICommand
{
private Action<object> execute;
private Predicate<object> canExecute;
public CustomCommand(Action<object> execute, Predicate<object> canExecute)
{
this.execute = execute;
this.canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
}
remove
{
}
}
public bool CanExecute(object parameter)
{
//throw new NotImplementedException();
bool b = canExecute == null ? true : canExecute(parameter);
return b;
}
public void Execute(object parameter)
{
execute(parameter);
}
}
xaml
<DataTemplate>
<Button Command="{Binding Source={StaticResource VM},
Path=Command}" CommandParameter="{Binding}" >
<SymbolIcon Symbol="Edit" Foreground="AliceBlue" />
</Button>
</DataTemplate>
CanExecute in VM
private bool CanGetDetails(object obj)
{
return true;
}
You can always do your conditional statement within the CanExecute function of your custom command, no need for you to bind IsEnabled property with your button that is bound to a command. Here's a sample implementation, hope this helps.
Custom Command:
public class CustomCommand<T> : ICommand
{
private readonly Action<T> _action;
private readonly Predicate<T> _canExecute;
public CustomCommand(Action<T> action, Predicate<T> canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute((T)parameter);
}
public void Execute(object parameter)
{
_action((T)parameter);
}
public event EventHandler CanExecuteChanged;
}
As you can see here, I created an object that implements the ICommand interface, this custom command accepts a generic type parameter which is used to evaluate a condition (CanExecute: this tells whether to enable or disable a command (in UI, the button), normally use to check for permissions, and other certain conditions) this parameter is also used to execute the action (Execute: the actual logic/action to be performed), The command contructor accepts delegate parameters that contain signatures for these 2 methods, the caller may choose lambda or standard methods to fillup these parameters.
Sample ViewModel:
public class ViewModel1: INotifyPropertyChanged
{
public ViewModel1()
{
// Test Data.
Items = new ObservableCollection<ItemViewModel>
{
new ItemViewModel{ Code = "001", Description = "Paint" },
new ItemViewModel{ Code = "002", Description = "Brush" },
new ItemViewModel{ Code = "003", Description = "" }
};
EditCommand = new CustomCommand<ItemViewModel>(Edit, CanEdit);
}
public CustomCommand<ItemViewModel> EditCommand { get; }
private bool CanEdit(ItemViewModel item)
{
return item?.Description != string.Empty;
}
private void Edit(ItemViewModel item)
{
Debug.WriteLine("Selected Item: {0} - {1}", item.Code, item.Description);
}
private ObservableCollection<ItemViewModel> _items { get; set; }
public ObservableCollection<ItemViewModel> Items
{
get => _items;
set
{
_items = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<Page x:Name="root"
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vms="using:App1.ViewModels"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
d:DesignHeight="450" d:DesignWidth="800">
<Page.DataContext>
<vms:ViewModel1 x:Name="Model"/>
</Page.DataContext>
<Grid>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0 0 0 15">
<TextBlock Text="{Binding Code}" />
<TextBlock Text="{Binding Description}" />
<Button Content="Edit" Command="{Binding DataContext.EditCommand, ElementName=root}" CommandParameter="{Binding}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Page>
I think you can pick a lot of code from the RelayCommand of MVVMLight. Try to change your event to
public event EventHandler CanExecuteChanged
{
add
{
if (canExecute != null)
{
CommandManager.RequerySuggested += value;
}
}
remove
{
if (canExecute != null)
{
CommandManager.RequerySuggested -= value;
}
}
}
and add also a function
public void RaiseCanExecuteChanged()
{
CommandManager.InvalidateRequerySuggested();
}
Then, whatever you put as your Predicate on the command, at the Predicate's boolean setter do:
SomeCustomCommand.RaiseCanExecuteChanged()
Hope I helped.

How do I add item to a list when I select the item from a listview and click add button in the UI on WPF

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>

adding commands to custom control for button binding wpf

I want to add a method to my custom control which I can call from a button using command binding, in my MainWindow.xaml. I've come across a few solutions online, however one of them didn't appear to work and the other did. Can someone explain to me the correct way to set this up. The first solution produces and error as mentioned below. The second solution works but I'm not sure of any pros/cons.
Solution 1 - broken
public partial class MyControl : Control
{
...
public static readonly RoutedCommand AlignLeftCommand = null;
static MyControl()
{
binding = new CommandBinding();
binding.Command = AlignLeftCommand;
binding.Executed += new ExecutedRoutedEventHandler(AlignLeft_Executed);
CommandManager.RegisterClassCommandBinding(typeof(MyControl), binding);
}
}
Error:
Severity Code Description Project File Line
Error CS0120 An object reference is required for the non-static field, method, or property...
Solution 2
public partial class MyControl : Control
{
...
public static readonly RoutedCommand AlignLeftCommand = new RoutedCommand();
public MyControl()
{
this.CommandBindings.Add(new CommandBinding(MyControl.AlignLeftCommand, AlignLeft_Executed, null));
}
}
Here is the button calling the method.
<StackPanel Orientation="Horizontal">
<Button Content="Left Edges" FontSize="8"
Command="{x:Static JM:MyControl.AlignLeftCommand}"
CommandTarget="{Binding ElementName=mycontrol}"/>
</StackPanel>
At first, you should define a command binding on the Window like that(create handlers for Executed and CanExecuteevents):
<Window x:Class="CommandBindingWPF.MainWindow"
...The code omitted for the brevity...
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" Executed="CommandBinding_Executed" CanExecute="CommandBinding_CanExecute" />
</Window.CommandBindings>
and declare your Button ix xaml:
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Button Command="ApplicationCommands.New">New</Button>
</StackPanel>
Handlers should be created in code-behind after you command binding created:
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Hello from Command");
}
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{ }
Update:
For MVVM application:
public class RelayCommand : ICommand
{
#region Fields
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
#endregion // Fields
#region Constructors
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;
}
#endregion // Constructors
#region ICommand Members
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);
}
#endregion // ICommand Members
}
Then create a property in your viewModel. For instance:
public class YourViewModel
{
public RelayCommand YourCommand { get; set; }
public YourViewModel()
{
YourCommand = new RelayCommand(DoSmth, CanDoSmth);
}
private void DoSmth(object obj)
{
Message.Box("Hello from viewModel");
}
private bool CanDoSmth(object obj)
{
//you could implement your logic here. But by default it should be
//set to true
return true;
}
}
And XAML should be look like:
<Button Content="Click me!" Command="{Binding YourCommand}"/>
To get acquainted with MVVM, I recommend you to read Rachel Lim's blog. She has a talent to teach people and she can explain by simple terms. Read Rachel Lim's blog.
To get acquainted with MVVM commands see that post

WPF MVVM button command that gets textbox value

I'm having a great trouble with understanding how button command works. I have something like this
{Binding TxtBox} gets value from model, let's say it's "aaa". I would like click the button and the value should appear in the second textbox (the one with {Binding TxtBox2}).
This is my xaml:
<TextBox Text="{Binding TxtBox, Source={StaticResource viewModel}}" />
<TextBox Text="{Binding TxtBox2, Source={StaticResource viewModel}}" />
<Button Command="{Binding ClickCommand}"/>
This is my ViewModel:
public class CommandHandler : ICommand
{
private Action _action;
private bool _canExecute;
public CommandHandler(Action action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
_action();
}
}
Do I really need this CommandHandler class? I copied the code from the net.
public string TxtBox
{
get { return Model.TxtBoxValue; }
set { Model.TxtBoxValue = value; }
}
public string TxtBox2 { get; set; }
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute)); // I believe that when the button is clicked MyAction() is triggered, right?
}
}
private bool _canExecute = true;
public void MyAction()
{
this.TxtBox2 = this.TxtBox; // should something like this work? Because right now it doesn't
}
The second textbox's binding never gets notified that it's bound property is changed. When you set this.TxtBox2 you should fire the propertychanged event for that property so the binding will be updated.
See think link for everything on bindings
I don't know if you are using prism as mvvm framework but that comes with the DelegateCommand class. I don't think there is a simple/lightweight implementation in the .net framework. See this link for the mvvm framework and the delegate command
The View reacts to binding changes through PropertyChanged events, of which you have none. Have anything that binds to the View implement INotifyPropertyChanged and then fire events when props change, and you're all set for your bindings to work (one way or two way).
Change your model to look like this and it should work for you.
public class MyViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged = null;
protected virtual void RaisePropertyChanged(string propName)
{
if(PropertyChanged != null)
{
Task.Run(() => PropertyChanged(this, new PropertyChangedEventArgs(propName)));
}
}
#endregion
public string TxtBox
{
get { return Model.TxtBoxValue; }
set
{
Model.TxtBoxValue = value;
RaisePropertyChanged("TxtBox");
}
}
// presuming TxtBox2Value is in Model...else use a field
public string TxtBox2
{
get { return Model.TxtBox2Value; }
set
{
Model.TxtBox2Value = value;
RaisePropertyChanged("TxtBox2");
}
}
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute)); // I believe that when the button is clicked MyAction() is triggered, right?
}
}
private bool _canExecute = true;
public void MyAction()
{
this.TxtBox2 = this.TxtBox; // should something like this work? Because right now it doesn't
}
}
IMO - it is better to have your Model implement INotifyPropertyChanged and then bind directly to it rather than wrap it in your ViewModel. If Model : INotifyPropertyChanged, then your ViewModel now looks like this:
public class MyViewModel
{
// fire prop changed event here if this model will be swapped out after the ctor...otherwise don't worry about it
public Model Model { get; set; }
private ICommand _clickCommand;
public ICommand ClickCommand
{
get
{
return _clickCommand ?? (_clickCommand = new CommandHandler(() => MyAction(), _canExecute));
}
}
private bool _canExecute = true;
public void MyAction()
{
Model = new Model();
Model.TxtBox2 = "Some new value";
}
}
...and your xaml changes to this:
<TextBox Text="{Binding Model.TxtBox, Source={StaticResource viewModel}}" />
<TextBox Text="{Binding Model.TxtBox2, Source={StaticResource viewModel}}" />
<Button Command="{Binding ClickCommand}"/>

Wpf binding button with listview.Item

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.

Categories