Context menu ICommand doesn't fire in my mvvm windows phone app - c#

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.

Related

C# WPF CheckBox Command not Binded

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>

How do I handle events in a custom control?

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));
}

How to pass information about selected item from DataGrid using MVVM

I'm doing my first app using MVVM. I have in "View" declared Datagrid. Code XAML below:
<DataGridTemplateColumn Header="delete">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type> UserControl},Mode=FindAncestor}, Path=DataContext.ClickCommand}"> Content="X" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>>
</DataGrid>
In my ViewModel class I can run function that I want after click button "delete" by part of code:
public ICommand ClickCommand => _clickCommand ?? (_clickCommand = new CommandHandler(Delete, _canExecute));
public void Delete()
{
// DataTable.Rows.RemoveAt();
}
I have problem because I can't get index of selectet row. Source of data in datagrid is dataTable.
Do you have any ideas how to do this?
I've tried something with passing parameter with command of button but I coudn't make it works.
Xmal code
<Button Command="{Binding Path=DataContext.ViewCommand,RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}}" CommandParameter="{Binding Id}" Content="X" Background="Chocolate"/>
Codebehind code
public RelayCommand DeleteCommand
{
get
{
return new RelayCommand(p => Delete(p));
}
}
public void Delete(string id)
{
// DataTable.Rows.RemoveAt();
}
This is example you can pass whatever you want in that cmd parameter.
Relay cmd
public class RelayCommand : ICommand
{
private Action<object> action;
private Func<bool> canFuncPerform;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action<object> executeMethod)
{
action = executeMethod;
}
public RelayCommand(Action<object> executeMethod, Func<bool> canExecuteMethod)
{
action = executeMethod;
canFuncPerform = canExecuteMethod;
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged(this, EventArgs.Empty);
}
public bool CanExecute(object parameter)
{
if (canFuncPerform != null)
{
return canFuncPerform();
}
if (action != null)
{
return true;
}
return false;
}
public void Execute(object parameter)
{
if (action != null)
{
action(parameter);
}
}
}
You shouldn't rely on the selected item. Instead pass the current row item as CommandParameter:
<DataGridTemplateColumn Header="delete">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl},Mode=FindAncestor}, Path=DataContext.ClickCommand}"
CommandParameter="{Binding}"
Content="X" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Then of course, use an ICommand implementation that is not discarding the command parameter and use it to identify the row to be deleted.

How to use MVVM Icommand on button? [duplicate]

This question already has answers here:
Bind button in DataTemplate to command in the form's ViewModel
(2 answers)
Closed 6 years ago.
I have a list that load items from an observable collection ComputerList
<ListView x:Name="icTodoList" ItemsSource="{Binding ComputerList}" SelectedItem="{Binding SelectedComputer}" Grid.Column="3" SelectionChanged="icTodoList_SelectionChanged_1">
<ListView.ItemTemplate>
<DataTemplate>
<Border BorderBrush="{Binding borderColor }" BorderThickness="2" Margin="0,0,0,1">
<Grid Margin="2" Height="auto" Width="auto">
<Button Height="18" Command="{Binding RemoveComputer}" HorizontalAlignment="Right" ToolTipService.ShowDuration="60000" Margin="0,1,38,0" x:Name="button1_Copy" VerticalAlignment="Top" Width="25" FontSize="11" Foreground="#FF6BADF6" Content="" BorderBrush="#FF6BADF6" Grid.Column="8"/>
</Grid>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
my button click should remove the items from observableCollection so i did build Icommand interface this way (in short version).
class ComputerViewModel : ViewModelBase
{
CustomClass _customClass = new CustomClass();
public readonly ObservableCollection<Model.Model.ControleData> _ComputerList = new ObservableCollection<Model.Model.ControleData>();
public ObservableCollection<Model.Model.ControleData> ComputerList { get { return _ComputerList; } }
public ComputerViewModel()
{
ComputerList.Add(new Model.Model.ControleData { ComputerName = "TESTMACHINE", titlePing = "online",borderColor = Genkai.BlueMain });
// TextBoxText = "init";
_canExecute = true;
}
private ICommand _RemoveComputer;
public ICommand RemoveComputer
{
get
{
return _RemoveComputer ?? (_RemoveComputer = new CommandHandler(() => RemoveComp(), _canExecute));
}
}
private bool _canExecute;
public void RemoveComp()
{
Debug.WriteLine("close Item");
}
public class CommandHandler : ICommand
{
private Action _action;
private bool _canExecute;
public CommandHandler(Action action, bool canExecute)
{
_action = action;
_canExecute = canExecute;
}
public void Execute(object parameter)
{
_action();
}
public bool CanExecute(object parameter)
{
return _canExecute;
}
public event EventHandler CanExecuteChanged;
}
}
the action removecomp is not fired when i click.
but with this in my view model its fired
var hwc = new CommandHandler(RemoveComp,true);
if (hwc.CanExecute(this))
hwc.Execute(this);
so i guess i miss something in my WPF view.
You are trying to bind VM commands in datatemplate. It cannot find this command, because it has different context.
try to bind in that way
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type controls:TypeOfYourControlOrWindow}}, Path=DataContext.YourCommand}"

Change the following event from user control to viewModel

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"

Categories

Resources