I have a ListView that, right now opens a Popup on SelectedItem.
What I want is that if the user decides to remove an Item from the List he can click the Button and it gets removed - right now the Button does fire, but how do I tell the Button in the VM what Item to delete - without "SelectedItem"? p.E..
<ListView
SelectedItem="{Binding...}"
x:Name="lv">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<Image Source="{Binding...}"/>
<Button Command="{Binding ElementName=lv,Path=DataContext.RemoveXCommand}" />
</Stackpanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
VM
public void RemoveXCommand()
{
foreach(var item in pseudo)
{
if(item.Name == ?????)
pseudo.Remove(item);
}
}
Is there a way, or do I have to remove the opening of the Popup, and implement it as another Button, so I can use the SelectedItem to get the comparison?
Thank you.
EDIT1:
Thanks to Fruchtzwerg I got it working
public RelayCommand<string> RemoveXCommand{ get; private set; }
//... in then Constructor
RemoveXCommand = new RelayCommand<string>((s) => RemoveXCommandAction(s));
public void RemoveXCommand(object temp)
{
foreach(var item in pseudo)
{
if(item.Name == (string) temp)
pseudo.Remove(item);
}
}
You can pass the item you need to remove as CommandParameter
<Button Command="{Binding ElementName=lv, Path=DataContext.RemoveXCommand}"
CommandParameter="{Binding}"/>
and remove it like
public void RemoveXCommand(object itemToRemove)
{
pseudo.Remove(itemToRemove);
}
Your approach deleting the item by name is also possible. Bind the Name of the item as CommandParameter
<Button Command="{Binding ElementName=lv, Path=DataContext.RemoveXCommand}"
CommandParameter="{Binding Name}"/>
and remove it like
public void RemoveXCommand(object nameToRemove)
{
foreach(var item in pseudo)
{
if(item.Name == (string)nameToRemove)
{
pseudo.Remove(item);
}
}
}
Note that the second approach is removing all items having the name of the item you selected. The first approach removes only the item you selected because the specific instance is removed.
To allow a parameter in RelayCommand a new or modified implementeation of ICommand is required. Here is a possible solution:
public class ParameterRelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged;
public ParameterRelayCommand(Action<object> execute)
: this(execute, null)
{ }
public ParameterRelayCommand(Action execute<object>, Func<bool> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
Related
In my WPF app I have a datagrid
<DataGrid SelectedItem="{Binding SelItm ,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Bills}"
SelectionMode="Extended"
Name="myGrid"...
Now I want to enable a button if any rows of the datagrid are selected, if no rows are selected the button should be disabled, pretty simple stuff.
My button xaml is like
<Button
Command="{Binding PreviewButtonClicked}"
CommandParameter="{Binding SelItm, ElementName=myGrid}"
I've create a standard RelayCommand class
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);
}
}
and my viewmodel class contains:
public class myVM()
{
private string _SelItm;
public string SelItm
{
get { return _SelItm; }
set
{
SetValue(ref _SelItm, value);
}
}
public RelayCommand PreviewButtonClicked { get; set; }
public myVM()
{
PreviewButtonClicked = new RelayCommand(ShowPDF, CanShowPDF);
}
public void ShowPDF(object param)
{
//do stuff
}
public bool CanShowPDF(object param)
{
if (SelItm.Any())
{
return true;
}
return false;
}
}
But when I run the app I get the below error on line if (SelItm.Any())
System.ArgumentNullException: Value cannot be null.
What am I doing incorrectly ?
There are at least three options.
Option 1 - Codebehind
Add an event handler to the SelectionChanged event on the DataGrid.
<DataGrid SelectedItem="{Binding SelItm ,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Bills}"
SelectionMode="Extended"
SelectionChanged="DataGridSelectionChanged"
Name="myGrid"...
The SelectionChanged event will be invoked when there's a full row selected. (You can set the SelectionUnit as "Rows" on the DataGrid explicitly) Otherwise use the SelectedCellsChanged event.
In the handler of the SelectionChanged event:
private void DataGridSelectionChanged(object sender, SelectionChangedEventArgs args)
{
myButton.IsEnabled = myGrid.SelectedItems.Count > 0;
}
Option 2
You can bind the SelectedItem property on the DataGrid, to your viewmodel.
<DataGrid SelectedItem="{Binding SelItm ,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Bills}"
SelectionMode="Extended"
SelectedItem="{Binding SelectedItemInDataGrid}"
Name="myGrid"...
In the viewmodel, simplified and also assuming your vm implements INotifyPropertyChanged:
private object _selectedItemInDg;
public object SelectedItemInDataGrid
{
set {
_selectedItemInDg = value;
OnPropertyChanged(nameof(SelectedItemInDataGrid ));
OnPropertyChanged(nameof(IsButtonEnabled));
PreviewButtonClicked?.UpdateCanExecute();
}
get => _selectedItemInDg;
}
public bool IsButtonEnabled => _selectedItemInDg != null;
private void OnPropertyChanged(string name)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
And your button:
<Button IsEnabled="{Binding IsButtonEnabled}" Command="..." CommandParameter="...">Content of button</Button>
Option 3 - In CanExecute
You've got a method in your viewmodel, CanShowPDF(object param), which determines for the RelayCommand wether the command can be executed or not. WPF enables/disables the button automatically based on the CanExecute method of an ICommand instance.
I'd add a method to the RelayCommand class:
public void UpdateCanExecute() {
CanExecuteChanged?.Invoke();
}
Call the UpdateCanExecute method on your RelayCommand instance every time something changes that would affect the result of CanShowPDF.
The exception
You ask about an exception you get:
System.ArgumentNullException: Value cannot be null.
Probably you are calling a method on something that is null at that moment.
But it's hard to figure out the cause with the given information in the question and I don't actually know what SelItm is, although seen the name we could guess.
View:
<DataGridTemplateColumn.Header>
<CheckBox x:Name="chk_Top" HorizontalAlignment="Center" HorizontalContentAlignment="Center"
Command="{Binding Chk_GridTop}"/>
</DataGridTemplateColumn.Header>
View Model:
public partial class ViewModel_AC: INotifyPropertyChanged
{
ICommand _chkGridTop;
public ICommand Chk_GridTop
{
get { return _chkGridTop ?? (_chkGridTop = new DelegateCommand(_chk_GridTop)); }
}
public void _chk_GridTop(object check)
{
//Empty
}
}
DelegateCommand
public class DelegateCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new NullReferenceException("execute can no null");
_execute = execute;
_canExecute = canExecute;
}
public DelegateCommand(Action<object> execute) : this(execute, null)
{
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute.Invoke(parameter);
}
}
There are other Commands bind with buttons which working well, but checkBox command is not working
I want to fire Chk_GridTop when checkBox is checked or unchecked
Is it something wrong what i use that?
add CommandParameter
<DataGridTemplateColumn.Header>
<CheckBox x:Name="chk_Top" HorizontalAlignment="Center" HorizontalContentAlignment="Center"
Command="{Binding Chk_GridTop}" CommandParameter="{Binding RelativeSource={RelativeSource Self},Path=IsChecked}"/>
</DataGridTemplateColumn.Header>
I have searched in Google using a progress bar in WPF that will be manually incremented and decremented using a plus and minus button. But, to no avail haven't found one that is similar to what I want though.
How do I implement a WPF progress bar that will be manually incremented or decremented (using buttons) in an MVVM way. The screen capture below shows the mock-up UI design.
The image show that when the user click the plus button, the progress bar will be incremented by 10 minutes. On the other hand, the minus button when clicked, decrements the progress bar by 10 minutes.
I'm just starting to learn WPF and MVVM. Any help is greatly appreciated.
I created a simple example which uses WPF and MVVM to show how one model can be displayed with different views. Here in xaml I placed on a form Slider and ProgressBar - they are Views for our ViewModel. The properties we need (Minimum, Maximum, Value) are binded to the ViewModel's properties. "Plus" and "Minus" buttons' properties "Command" are also binded to the corresponding props in the ViewModel(IncreaseCommand, DecreaseCommand).
<Window>
<StackPanel Orientation="Horizontal">
<Button Width="50" Height="40" Content="-" Command="{Binding DecreaseCommand}"/>
<StackPanel Width="400" Orientation="Vertical">
<Slider Height="40" Margin="0,50,0,0" Minimum="{Binding Minimum}" Maximum="{Binding Maximum}" Value="{Binding Value}"/>
<ProgressBar Height="40" Margin="0,100,0,0" Minimum="{Binding Minimum}" Maximum="{Binding Maximum}" Value="{Binding Value}"/>
<TextBlock TextAlignment="Center" Margin="0,50,0,0" Text="{Binding Value}"/>
</StackPanel>
<Button Width="50" Height="40" Content="+" Command="{Binding IncreaseCommand}"/>
</StackPanel>
</Window>
For implementing the commands functionality in ViewModel you will need to create an implementation of ICommand interface:
public class RelayCommand : ICommand
{
private Predicate<object> _canExecute;
private Action<object> _execute;
public RelayCommand(Predicate<object> canExecute, Action<object> execute)
{
_canExecute = canExecute;
_execute = execute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
And here's the ViewModel class, it implements INotifyPropertyChanged interface to keep views updated.
public class ViewModel:INotifyPropertyChanged
{
public ViewModel()
{
_value = 0;
_minimum = 0;
_maximum = 180;
_step = 10;
}
private int _step;
private int _minimum;
private int _maximum;
private ICommand _increaseCommand;
public ICommand IncreaseCommand
{
get
{
if (_increaseCommand == null)
{
_increaseCommand = new RelayCommand(
p => _value + _step <= _maximum,
Increase);
}
return _increaseCommand;
}
}
private ICommand _decreaseCommand;
public ICommand DecreaseCommand
{
get
{
if (_decreaseCommand == null)
{
_decreaseCommand = new RelayCommand(
p => _value - _step >= _minimum,
Decrease);
}
return _decreaseCommand;
}
}
private void Increase(object param)
{
Value = Value + _step;
}
private void Decrease(object param)
{
Value = Value - _step;
}
private int _value;
public int Value
{
get { return _value; }
set { _value = value; InvokePropertyChanged(new PropertyChangedEventArgs("Value")); }
}
public int Minimum
{
get { return _minimum; }
}
public int Maximum
{
get { return _maximum; }
}
public event PropertyChangedEventHandler PropertyChanged;
public void InvokePropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
}
And the last thing to get it all working is to create new ViewModel and set DataContext of a window to this model:
public MainWindow()
{
InitializeComponent();
var model = new ViewModel();
DataContext = model;
}
I think you should solve that by use custom Slider control of WPF instead Progress bar.
This link can help you : http://www.codescratcher.com/wpf/custom-slider-control-in-wpf/
I have a TextBox in a UserControl which is binded to a property in the MainWindow's ViewModel.
Now when I type something in the Textbox, it updates the property in the viewmodel but if I change Textbox's text in code behind, the viewmodel property isn't updating.
Actually the textbox is getting the value from FileDialog which is opened when I click the button, so Textbox is getting its text from code behind.
UserControl XAML:
<StackPanel Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Left">
<TextBox x:Name="TextBoxFileOrFolder" Text="{Binding FolderOrFileName}" Grid.Row="1" Width="200" Height="100" HorizontalAlignment="Left"></TextBox>
<Button x:Name="ButtonRun" Content="Run" Click="ButtonRun_OnClick" Width="200" Height="100" Margin="10"></Button>
</StackPanel>
UserControl code behind
private void ButtonRun_OnClick(object sender, RoutedEventArgs e)
{
TextBoxFileOrFolder.Text = "FileName" + new Random().Next();
}
ViewModel:
public class MainViewModel: INotifyPropertyChanged
{
public MainViewModel()
{ }
private string folderOrFileName;
public string FolderOrFileName
{
get { return folderOrFileName; }
set
{
if (folderOrFileName!=value)
{
folderOrFileName = value;
RaisePropertyChanged();
}
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises the property changed.
/// </summary>
/// <param name="propertyName">Name of the property.</param>
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
# endregion
}
but if I change Textbox's text in code behind, the viewmodel property isn't updating.
That’s because if you set the Text property of the text box in the code-behind, you are overwriting the binding. So while you update the view, the link to your view model is gone, so there is nothing that will update it. And also, when the view model update the values, the view also will not be updated.
To solve this, simply don’t set properties that have a binding in the code-behind.
Instead of handling the button event in the code-behind and updating the view, you should have your button command bind to your view model and update the FolderOrFileName in the view model.
If you binding to Text property you should set property in the ViewModel to change value of TextBox:
public partial class MainWindow : Window
{
private MainViewModel _vm;
public MainWindow()
{
InitializeComponent();
_vm = new MainViewModel();
DataContext = _vm;
}
private void ButtonRun_OnClick(object sender, RoutedEventArgs e)
{
_vm.FolderOrFileName = "FileName" + new Random().Next();
}
}
In your case you should use command to modify data.
1) You should create class which inherit from ICommand:
public class DelegateCommand : ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action<object> execute)
: this(execute, null)
{
}
public DelegateCommand(Action<object> execute,
Predicate<object> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
{
return true;
}
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
{
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
2) Next you should create command in ViewModel:
public class MainViewModel : INotifyPropertyChanged
{
public MainViewModel()
{
ChangeFileName = new DelegateCommand(OnChangeFileName);
}
public ICommand ChangeFileName { get; private set; }
private void OnChangeFileName(object param)
{
FolderOrFileName = "FileName" + new Random().Next();
}
private string folderOrFileName;
...
3) And finally you should add binding to Button.Command property in View:
<Button x:Name="ButtonRun" Content="Run" Command="{Binding ChangeFileName}" Width="200" Height="100" Margin="10"></Button>
Make sure your binding is set up as "TwoWay" - UI -> VM and VM -> UI
<TextBox x:Name="TextBoxFileOrFolder" Text="{Binding FolderOrFileName, Mode=TwoWay}" Grid.Row="1" Width="200" Height="100" HorizontalAlignment="Left"></TextBox>
I'm trying to send a variable from the ViewModel as a parameter to a command. The command looks like this:
public class EditPersonCommand : ICommand
{
private bool _CanExecute = false;
public bool CanExecute(object parameter)
{
PersonModel p = parameter as PersonModel;
CanExecuteProperty = (p != null) && (p.Age > 0);
return CanExecuteProperty;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter) { }
private bool CanExecuteProperty
{
get { return _CanExecute; }
set
{
if (_CanExecute != value)
{
_CanExecute = value;
EventHandler can_execute = CanExecuteChanged;
if (can_execute != null)
{
can_execute.Invoke(this, EventArgs.Empty);
}
}
}
}
}
The ViewModel looks like this:
public class PersonViewModel : ViewModelBase
{
private PersonModel _PersonModel;
private EditPersonCommand _EditPersonCommand;
///<remarks>
/// must use the parameterless constructor to satisfy <Window.Resources>
///</remarks>
public PersonViewModel()
: this(new PersonModel())
{
}
public PersonViewModel(PersonModel personModel)
{
_PersonModel = personModel;
}
public ICommand EditPersonCommand
{
get
{
if (_EditPersonCommand == null)
{
_EditPersonCommand = new EditPersonCommand();
}
return _EditPersonCommand;
}
}
}
The xaml looks like this:
<Button Content="Edit" HorizontalAlignment="Right" Height="20" Width="80"
Command="{Binding EditPersonCommand}"
CommandParameter="{Binding _PersonModel}" />
I've tried creating a property in the ViewModel instead of using the private local variable name, but that didnt work either. The object parameter always shows null in the call to CanExecute and the button is never enabled. If I change the CommandParameter value to Hello, then I receive Hello in the call to CanExecute, so I'm not sure why the variable doesnt work. Any help would be appreciated.
Update: I've also tried making a public property to the model (which I dont really want to expose the model, but just tried it to see if it works, but it doesnt).
// Added this to the ViewModel
public PersonModel PersonModelProp
{
get
{
return _PersonModel;
}
set
{
_PersonModel = value;
OnPropertyChanged("PersonModelProp");
}
}
And changed the xaml to this:
<Button Content="Edit" HorizontalAlignment="Right" Height="20" Width="80"
Command="{Binding EditPersonCommand}"
CommandParameter="{Binding PersonModelProp}" />
But still no luck. The ViewModel does implement INotifyPropertyChanged
Is the CommandParameter always null or are you only checking the first time it is being executed?
It appears that the order in which you declare your properties matters in this case since setting the Command property causes the CanExecute to fire immediately before the CommandParameter has been set.
Try moving the CommandParameter property before the Command property:
<Button Content="Edit" HorizontalAlignment="Right" Height="20" Width="80"
CommandParameter="{Binding PersonModelProp}"
Command="{Binding EditPersonCommand}" />
Also, see here and here.
Edit
To ensure that your events are being raised properly you should raise the CanExecuteChanged event when the PersonModelProp value changes.
The Command:
public class EditPersonCommand : ICommand
{
public bool CanExecute(object parameter)
{
PersonModel p = parameter as PersonModel;
return p != null && p.Age > 0;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
//command implementation
}
public void RaiseCanExecuteChanged()
{
var handler = CanExecuteChanged;
if(handler != null)
{
handler(this, EventArgs.Empty);
}
}
}
And the view model:
public class PersonViewModel : ViewModelBase
{
private PersonModel _PersonModel;
private EditPersonCommand _EditPersonCommand;
///<remarks>
/// must use the parameterless constructor to satisfy <Window.Resources>
///</remarks>
public PersonViewModel()
: this(new PersonModel())
{
_EditPersonCommand = new EditPersonCommand();
}
public PersonViewModel(PersonModel personModel)
{
_PersonModel = personModel;
}
public ICommand EditPersonCommand
{
get
{
return _EditPersonCommand;
}
}
public PersonModel PersonModelProp
{
get
{
return _PersonModel;
}
set
{
_PersonModel = value;
OnPropertyChanged("PersonModelProp");
EditPersonCommand.RaiseCanExecuteChanged();
}
}
}
Two points to the answer:
First, as #akton mentioned, you can only bind to public properties. It doesn't have to be a DependencyProperty though.
Second, which took me some tome to figure out, is that you have to set the binding for the CommandParameter before the Command property. i.e.
<Button Content="Edit" HorizontalAlignment="Right" Height="20" Width="80"
CommandParameter="{Binding PersonModelProp}"
Command="{Binding EditPersonCommand}" />
Hope this helps :)
_PersonModel is private and so cannot be accessed. Create a public property that exposes it and bind to that in the CommandParameter. Remember to make the property a dependency property (technically not required but it helps) and the ViewModel should implement INotifyProperty changed and fire the PropertyChanged event so the binding is updated.
I think you have a problem in your EditPersonCommand (it not fired ok).I check it with relayCommand and it work!
This is the code:
ViewModel:
public class PersonViewModel : ViewModelBase
{
private PersonModel _PersonModel;
private ICommand _EditPersonCommand;
///<remarks>
/// must use the parameterless constructor to satisfy <Window.Resources>
///</remarks>
public PersonViewModel()
: this(new PersonModel())
{
}
public PersonViewModel(PersonModel personModel)
{
PersonModelProp = personModel;
}
public ICommand EditPersonCommand
{
get
{
if (_EditPersonCommand == null)
{
_EditPersonCommand = new RelayCommand(ExecuteEditPerson,CanExecuteEditPerson);
}
return _EditPersonCommand;
}
}
private bool CanExecuteEditPerson(object parameter)
{
PersonModel p = parameter as PersonModel;
return (p != null) && (p.Age > 0);
}
private void ExecuteEditPerson(object o)
{
}
public PersonModel PersonModelProp
{
get
{
return _PersonModel;
}
set
{
_PersonModel = value;
NotifyPropertyChanged("PersonModelProp");
}
}
}
And this RelayCommand (Fire events ok!)
public class RelayCommand : ICommand
{
#region Constants and Fields
private readonly Predicate<object> canExecute;
private readonly Action<object> execute;
#endregion
#region Constructors and Destructors
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.execute = execute;
this.canExecute = canExecute;
}
#endregion
#region Events
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
#endregion
#region Implemented Interfaces
#region ICommand
[DebuggerStepThrough]
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
#endregion
#endregion
}
Xmal:
<Button Content="Edit" HorizontalAlignment="Right" Height="20" Width="80"
CommandParameter="{Binding PersonModelProp}"
Command="{Binding EditPersonCommand}" />