I need to move this event from the user control to the viewModel,I have read the following link
but not sure that I got it since command are true or false,my question is assume that I have the following event
how should I change it to the viewModel.?
Please assist ,Im really stuck !!!!
http://blog.magnusmontin.net/2013/06/30/handling-events-in-an-mvvm-wpf-application/
private void DropText_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var textBox = (TextBox) sender;
if (textBox == null) return;
textBox.Focus();
var dataObject = new DataObject((textBox).Text);
dataObject.SetData(DragSource, sender);
DragDrop.DoDragDrop(textBox, dataObject, DragDropEffects.Copy | DragDropEffects.Move);
}
<TextBox x:Name="Job"
AcceptsReturn="True"
AllowDrop="True"
PreviewMouseDown="DropText_PreviewMouseDown"
SelectionChanged="listbox_SelectionChanged"
HorizontalAlignment="Left"
internal class RelayCommand : ICommand
{
readonly Action _execute;
readonly Func<bool> _canExecute;
public RelayCommand(Action execute)
: this(execute, null)
{
}
public RelayCommand(Action execute, 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 event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public void Execute(object parameter)
{
_execute();
}
public event EventHandler CanExecuteChange;
public void RaiseCanExecuteChange()
{
if (CanExecuteChange != null)
CanExecuteChange(this, new EventArgs());
}
Here's your example with event triggers defined in the xaml. PreviewMouseDownCommand and SelectionChangedCommand are RelayCommands that will need to be declared in your viewmodel.
<TextBox x:Name="Job"
AcceptsReturn="True"
AllowDrop="True"
HorizontalAlignment="Left">
<i:Interaction.Triggers>
<i:EventTrigger EventName="PreviewMouseDown" >
<i:InvokeCommandAction Command="{Binding PreviewMouseDownCommand}" />
</i:EventTrigger>
<i:EventTrigger EventName="SelectionChanged" >
<i:InvokeCommandAction Command="{Binding SelectionChangedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
You will need a reference to System.Windows.Interactivity in order to use event triggers. Add this to the namespace declarations in your window:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Related
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 a custom ItemsControl just like that
public class MyControl : ItemsControl { }
My Template would something like
<Style TargetType="{x:Type local:MyControl}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Text="{Binding Text}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Now I want to act when either when ENTER or SPACE got pressed so I'd do something like this
<TextBox Text="{Binding Text}">
<TextBox.InputBindings>
<KeyBinding Key="Space"
Command="{Binding KeyPressedCommand}"
CommandParameter="{x:Static Key.Space}"/>
</TextBox.InputBindings>
</TextBox>
But how do I bind it to my Control?
One of ways to solve it is using MVVM pattern and RelayCommand class from tutorial.
RelayCommand.cs
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly 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)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
public void Execute(object parameter) => _execute(parameter);
}
Then you should set DataContext of your Window (or UserControl), it requred for resolving bindings in xaml.
One of ways doing it:
MainWindow.xaml
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
Setup Text Property and KeyPressedCommand from "something like this" example in the MainViewModel class.
MainViewModel.cs
public class MainViewModel : INotifyPropertyChanged
{
private bool _text;
public bool Text
{
get => _text;
set
{
_text = value;
OnPropertyChanged(nameof(Text));
// Text changed!
}
}
public ICommand KeyPressedCommand => new RelayCommand(obj =>
{
if (obj is Key key) {
// do something here with the 'key' provided by CommandParameter
}
});
public MainViewModel()
{
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
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);
}
}
}
The following two elements are firing in my implementation of ICommand differently and causing problems. When the implementation enters CanExecuteChanged(object parameter) for the TextBox, the value of parameter is null. When it enters the same method for the Button, the value of parameter is equal to the CommandParameter.
Ideally I'd like in both cases that the CommandParameter value is not sent to the CanExecuteChanged, only to Execute.
Implementation of ICommand
public event EventHandler CanExecuteChanged
{
add
{
canExecuteChanged += value;
CommandManager.RequerySuggested += value;
}
remove
{
canExecuteChanged -= value;
CommandManager.RequerySuggested -= value;
}
}
public bool CanExecute(object parameter)
{
if (parameter is bool)
{
this.canExecute = (bool)parameter;
}
return this.canExecute;
}
public void Execute(object parameter)
{
this.executeAction((T)parameter);
}
internal void RaiseCanExecuteChanged()
{
this.OnCanExecuteChanged();
}
private void OnCanExecuteChanged()
{
if (this.canExecuteChanged != null)
{
this.canExecuteChanged(this, EventArgs.Empty);
}
}
TextBox
<TextBox Width="80" Margin="2,2,2,2" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" MaxLength="25">
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding SearchCommand}">
<KeyBinding.CommandParameter>
<s:Boolean>True</s:Boolean>
</KeyBinding.CommandParameter>
</KeyBinding>
</TextBox.InputBindings>
</TextBox>
Button
<Button Margin="2,2,2,2" Padding="10,0,10,0" Content="Search">
<Button.InputBindings>
<MouseBinding Command="{Binding SearchCommand }" MouseAction="LeftClick">
<MouseBinding.CommandParameter>
<s:Boolean>True</s:Boolean>
</MouseBinding.CommandParameter>
</MouseBinding>
</Button.InputBindings>
</Button>
In this case, try the implementation ICommand of #JoshSmith, for me both options worked well:
RelayCommand
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);
}
}
SearchCommand
private RelayCommand _searchCommand = null;
public ICommand SearchCommand
{
get
{
if (_searchCommand == null)
{
_searchCommand = new RelayCommand(param => this.Search(param), param => true);
}
return _searchCommand;
}
}
private void Search(object param)
{
bool parameter = (bool)param;
if (parameter)
{
MessageBox.Show("Pressed the Enter Key");
}
}
I have the following the contextmenu defined in the view.
<ListBox x:Name="lstSavedTracks" ItemsSource="{Binding SavedMusicTracksDataSource}" Grid.Row="1" Margin="0,10,0,0" >
<ListBox.ItemTemplate >
<DataTemplate >
<StackPanel >
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="view" CommandParameter="{Binding}" ItemsSource="{Binding Path=PlayTrackCommand}"/>
<toolkit:MenuItem Header="delete" CommandParameter="{Binding}" Command="{Binding Path=DeleteTrackCommand}"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
<TextBlock Foreground="White" FontSize="20" Text="{Binding TrackTitle}" TextWrapping="Wrap"></TextBlock>
<Line MinHeight="5"></Line>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I have the below ViewModel set as datacontext for the above view.the ICommand works for the controls such as buttons when I am using commands binding for click events. But it doesn't work for my contextmenu commands.
public System.Windows.Input.ICommand ViewTrackCommand
{
get
{
return new DelegateCommand((o) =>
{
Task.Factory.StartNew(() =>
{
PlayTrack();
});
});
}
}
public System.Windows.Input.ICommand DeleteTrackCommand
{
get
{
return new DelegateCommand((o) =>
{
Task.Factory.StartNew(() =>
{
DeleteTrack();
});
});
}
}
I tried the similar icommand binding for the buttons click event and they work fine..but it doesn't work the context menu. Is there anything that I am missing here?
FYI: Icommand implementation which is working for buttons..
public class DelegateCommand : System.Windows.Input.ICommand
{
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
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 event EventHandler CanExecuteChanged;
}//end of class
You need point out clear Path and Source of ViewModel like this:
Command="{Binding TestVM.DeleteTrackCommand, Source={StaticResource Locator}}"
Hope this help you.