ContextMenu CanExecute is not updated - c#

I have a problem regarding the state of a menuitem in ContextMenu. I have a ObversableCollection of Cars. The cars are visualized in a ListBox, for each ListBox Item I want a ContextMenu. In that ContextMenu there is an option ReserveCar.
They problem I'm having is that the CanExecute of the Car is only executed once when I right click any car. They CanExecute will not be called anymore after that when I right click an other Car.
This causes that when I rightclick a Car which can be Reserved, the MenuItem is active, but when I then rightclick another which I should not be able to reserve the MenuItem remains active (because CanExecute is not called again).
<ListBox
ItemsSource="{Binding Cars}"
SelectedItem="{Binding SelectedCar}">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Reserve Car"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}"
Command="{Binding ReserveCarCommand}">
<MenuItem.Icon>
<Image Source="{StaticResource ReserveCarIcon}" Width="24" Height="24"/>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
My ViewModel:
private RelayCommand<Car> _reserveCarCommand;
public ICommand ReserveCarCommand
{
get { return _reserveCarCommand ?? (_reserveCarCommand = new RelayCommanReserveCar, CanReserveCar)); }
}
public bool CanReserveCar(Car car)
{
return !car.IsReserved && ReservationsAreOpen;
}
public void ReserveCar(Car car)
{
car.IsReserved = true;
}
Also when I manually refresh the Command when doing something, the CanExecute is called with a null as parameter, so thats not working either.
if (_reserveCarCommand != null) _reserveCarCommand .RaiseCanExecuteChanged();

Try binding the context menu on ListBoxItem instead of ListBox. As binding of context menu for ListBox happen only at the fist right click so CanExectute will not fire after first right click.
<ListBox Name="simpleListBox"
ItemsSource="{Binding Cars}"
SelectedItem="{Binding SelectedCar}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
...
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

Related

How can i get Menu item from menu with dataSource in WPF

I have menu with XML like this:
<MenuItem x:Name="MenuItemCameras" Header="Cameras" ItemsSource="{Binding LocalCameras}" >
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding DisplayName}" IsCheckable="True" IsChecked="{Binding IsStreamingVideo}" IsEnabled="{Binding CanStreamVideo}"
Command="{Binding DataContext.CommandSelectLocalCamera, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
Click="MenuItemCameras_OnClick"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
I want to iterate like this:
foreach (MenuItem mMenuItem in MenuItemCameras) {
//some code
}
How can I do this ?
To retrieve the corresponding MenuItem, you can do this
foreach (var item in MenuItemCameras.Items) {
MenuItem menuItem = (MenuItem)MenuItemCameras.ItemContainerGenerator.ContainerFromItem(item);
//some code
}
But there's a downside, as long as the corresponding MenuItem isn't created ItemContainerGenerator.ContainerFromItem will return null

WPF MVVM Grid With Button Column

I am following the MVVM pattern. I have a grid with few columns one column having Button in it . Clicking on button i want to open dialog box which is expected to display data related to that particular row in which button was clicked. But the problem is with binding , as i am unable to bind the control with viewmodel.
<Button Command="{Binding Path=ParentRow.DataContext,
RelativeSource={RelativeSource AncestorType={x:Type UserControl}},
UpdateSourceTrigger=PropertyChanged, Mode=Default}"
lib:Event.Binding="Click.[**NameOfViewModelMethod**]" >
</Button>
First things first, if your Button is inside a Grid with a defined DataContext you don't need to set the Path like Path=ParentRow.DataContext.
Your Command binding should be like this:
<Button Command="{Binding YourVMICommand"} />
You have to define a public ICommand in your VM and then bind it to the button.
you don't show all of your code and context but it should work like this
i assume you are in a usercontrol caling parent datacontext ...
(exemple with listview):
<ListView ItemsSource="{Binding listFromDataContext, IsAsync=True}" Margin="3,51,0,10" >
<ListView.ItemTemplate >
<DataTemplate>
<grid>
<Button Command="{Binding DataContext.MyMethode, RelativeSource={RelativeSource AncestorType={x:Type controls:thisUserControl}}}" CommandParameter="{Binding}" />
</grid>
</DataTemplate>
</ListView.ItemTemplate >
</ListView>
then in model
private ICommand _MyMethode;
public ICommand MyMethode
{
get
{
return _MyMethode ?? (_MyMethode = new CommandHandler<MyModel.item>(x => showMessage(x), _canExecute));
}
}
public void showMessage(MyModel.item x)
{
MessageBox.Show(x.Info);
}

Binding SelectedItem of ListBox

hi i have one list box that every row contain one textbox and one button;
with click button that row delete from listbox; this work with mvvm pattern
i use command for this.
this is my xaml:
<DataTemplate x:Key="CategoryTemplate">
<Border Width="400" Margin="5" BorderThickness="1" BorderBrush="SteelBlue" CornerRadius="4">
<StackPanel Grid.Row="0" Orientation="Horizontal">
<TextBlock Width="300" Margin="5" Text="{Binding Path=Name}"></TextBlock>
<Button Name="btnDeleteCategory" Width="50" Margin="5"
Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource AncestorType=ListBox}}"
CommandParameter="{Binding}" Content="-" />
</StackPanel>
</Border>
</DataTemplate>
<ListBox Grid.Column="0" Grid.Row="0" Name="lstCategory"
ItemTemplate="{StaticResource CategoryTemplate}"
ItemsSource="{Binding Path=GetAllCategories}">
</ListBox>
and in viewmodel class i have this command :
private ObjectButtonCommand<Category> _deleteCommand;
public ObjectButtonCommand<Category> DeleteCommand
{
get
{
return _deleteCommand
?? (_deleteCommand = new ObjectButtonCommand<Category>(
_category =>
{
GetAllCategories.Remove(_category);
}));
}
}
that GetAllCategories is observecollection propert;
and this is my ObjectButtonCommand code:
public class ObjectButtonCommand<T> : ICommand
where T:class
{
private Action<T> WhatToExecute;
public ObjectButtonCommand(Action<T> What)
{
WhatToExecute = What;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
WhatToExecute((T)parameter);
}
}
now every thing is ok and when click button that row delete;
now i want that this process repeat when i select one row of listbox
i try this code :
<ListBox Grid.Column="0" Grid.Row="0" Name="lstCategory" ItemTemplate="{StaticResource CategoryTemplate}" ItemsSource="{Binding Path=GetAllCategories}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding DataContext.DeleteCommand , RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding Path=SelectedItems,ElementName=lstCategory}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
but i get this error at this code : WhatToExecute((T)parameter);
{"Unable to cast object of type 'System.Windows.Controls.SelectedItemCollection' to type 'Sepand.WPFProject.Model.Model.Category'."}
what should i do?
You pass the selection to the delete command which is a list, hence you cannot use the same command for both cases unless you would wrap the individual items (as passed from the DataTemplate) in a list first.
You should probably define a new command which takes an IList as parameter (type of ListBox.SelectedItems), whose items you then cast to Category and remove individually.
If you just want to delete a single selected item you need to change the binding to SelectedItem and need be able to handle the case of SelectedItem being null in your existing command. e.g. change CanExecute to parameter != null if that is respected by InvokeCommandAction.
Hi Mohammad can you try this:
For your Listbox, make sure your SelectionMode is set to Single. Then change that you refer to SelectedItem in the CommandParameter instead of SelectedItems. Like so:
<ListBox Grid.Column="0" Grid.Row="0" Name="lstCategory" ItemTemplate="{StaticResource CategoryTemplate}" ItemsSource="{Binding Path=GetAllCategories}" SelectionMode="Single">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding DataContext.DeleteCommand , RelativeSource={RelativeSource AncestorType=ListBox}}" CommandParameter="{Binding Path=SelectedItem,ElementName=lstCategory}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>

Accessing objects in resource (WPF)

I have the following code and what I want is be able to access (get reference to) the MenuItem objects in my ListBox resource but I have no idea how.
<ListBox Grid.Column="1" Grid.Row="1" MouseDoubleClick="MainListBox_MouseDoubleClick" Name="mainListBox" SelectionChanged="MainListBox_SelectionChanged">
<ListBox.Resources>
<ContextMenu x:Key="ContextMenu">
<MenuItem Click="OpenMenuItem_Click" Header="Open" Name="openMenuItem"/>
<Separator/>
<MenuItem Click="CutMenuItem_Click" Header="Cut" Name="cutMenuItem"/>
<MenuItem Click="CopyMenuItem_Click" Header="Copy" Name="copyMenuItem"/>
<Separator/>
<MenuItem Click="DeleteMenuItem_Click" Header="Delete" Name="deleteMenuItem"/>
<MenuItem Click="RenameMenuItem_Click" Header="Rename" Name="renameMenuItem"/>
</ContextMenu>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I would like to be able to do something like:
renameMenuItem.IsEnabled = false;
But first I need to get reference to the object.
Here is an example:
var ctx = (ContextMenu)mainListBox.FindResource("ContextMenu");
var renameMenuItem = (MenuItem)ctx.Items[6];
renameMenuItem.IsEnabled = false;
You can find any MenuItems and Separators in ctx.Items
UPDATE :
This is an alternative to find MenuItem by name as you asked in comment:
foreach (var item in ctx.Items)
{
if (item is MenuItem && ((MenuItem)item).Name == "renameMenuItem")
{
var renameMenuItem = (MenuItem) item;
renameMenuItem.IsEnabled = false;
}
}

Need the ListBoxItem that spawned the Context Menu

There are similar questions, but as I will explain, their answers do not seem to work for me.
I have a Listbox with an ItemsSource feeding it data...
<ListBox Name="lbPatternResults" SelectionMode="Multiple">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Get current values" Click="GetCurrentValuesForID"/>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
The ItemsSource property is set elsewhere to an IEnumerable.
When clicking the menuitem, the sender is the MenuItem, and its parent is the ContextMenu. Usually, to get the ListBoxItem, people are told to use the context menu's PlacementTarget, but in my case it's not a ListBoxItem... it's the ListBox. How do I get from here? I just want the index or the value of the row in the list...
EDIT: It's not the SelectedIndex of the ListBox (There can be one selected index, and one other that you get the menu from).
This works fine for me:
<ListBox>
<ListBox.Resources>
<ContextMenu x:Key="MyContextmenu">
<MenuItem Click="MenuItem_OnClick"
Header="Test" />
</ContextMenu>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu"
Value="{DynamicResource MyContextmenu}" />
</Style>
</ListBox.ItemContainerStyle>
<ListBoxItem Content="A" />
<ListBoxItem Content="B" />
<ListBoxItem Content="C" />
<ListBoxItem Content="D" />
<ListBoxItem Content="E" />
</ListBox>
and code-behind:
private void MenuItem_OnClick(object sender, RoutedEventArgs e) {
var menuItem = (sender as MenuItem);
if (menuItem == null)
return;
var parentMenu = menuItem.Parent as ContextMenu;
if (parentMenu == null)
return;
var lbItem = parentMenu.PlacementTarget as ListBoxItem;
if (lbItem != null)
MessageBox.Show((string)lbItem.Content);
}

Categories