Accessing objects in resource (WPF) - c#

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

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 lazy binding context menu items source

I successfully bind MenuModel to MenuItem.ItemsSource. However, the items should be contextualized. Right now, I populate the ViewModel collection and the data is ready to be bound to items source, but that's not covering all my scenarios.
I need to re-populate the ViewModel collection when the context menu is opening, that is, my goal is to only populate the collection at context menu opening, because earlier don't make sense (the items should be contextualized by ListBox SelectedItem).
XAML
<Style x:Key="ActionMenuItemStyle" TargetType="MenuItem" BasedOn="{StaticResource MetroMenuItem}">
<Setter Property="Visibility" Value="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}" />
<Setter Property="Icon" Value="{StaticResource ActionMenuItemIcon}" />
<Setter Property="IsCheckable" Value="{Binding IsCheckable, Mode=OneWay}" />
<Setter Property="IsChecked" Value="{Binding IsChecked, Mode=OneWay}" />
<Setter Property="Command" Value="{Binding Action}" />
<Setter Property="CommandParameter" Value="{Binding ActionParameter}" />
</Style>
<HierarchicalDataTemplate DataType="{x:Type model:MenuModel}" ItemsSource="{Binding Children}">
<MenuItem Header="{Binding Path=Header}" Style="{StaticResource ActionMenuItemStyle}"
UsesItemContainerTemplate="True" ItemContainerTemplateSelector="{StaticResource ActionMenuItemContainerTemplateSelector}"/>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type model:SeparatorMenuModel}">
<Separator Style="{StaticResource {x:Static MenuItem.SeparatorStyleKey}}"/>
</DataTemplate>
<ContextMenu x:Key="OneItem">
<MenuItem Header="{x:Static resx:StudioResources.Advanced}">
<MenuItem Header="{x:Static resx:StudioResources.MoveToRoutineMenu}"
ItemsSource="{Binding PlacementTarget.Tag.Routines, RelativeSource={RelativeSource FindAncestor, AncestorType=ContextMenu}}"
UsesItemContainerTemplate="True"
ItemContainerTemplateSelector="{StaticResource ActionMenuItemContainerTemplateSelector}"/>
</MenuItem>
</ContextMenu>
ViewModel
public IObservableCollection<IMenuModel> Routines { get; private set; }
protected override void OnViewLoaded(object view)
{
base.OnViewLoaded(view);
GetParent().Routines.CollectionChanged += (s, e) =>
{
Routines.Clear();
var names = GetParent().Routines.Where(r => r != GetParent().ActiveRoutine).Take(5).OrderBy(r => r);
var menus = names.Select(name => new MenuModel(name, new MenuAction<string>(MoveToRoutine), name)).ToList();
menus.ForEach(m => m.WithEditor(GetParent()));
Routines.AddRange(menus);
};
}
I listen to an event to re-populate the collection, but actually I need to listen to several other events, and I wonder if there is a clean way, like populate on context menu opening.

ContextMenu CanExecute is not updated

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>

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

How to using from ContextMenu in a gridCell?

I have a CustomControl like this:
<Button Name="b" Height="20" Click="b_Click" Content="operation" Width="60">
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Copy"></MenuItem>
<MenuItem Header="cut"></MenuItem>
<MenuItem Header="delete"></MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
in CodeBehind ia have :
if (b.ContextMenu != null && b.ContextMenu.IsOpen == false)
{
b.ContextMenu.PlacementTarget = b;
b.ContextMenu.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
ContextMenuService.SetPlacement(b, System.Windows.Controls.Primitives.PlacementMode.Bottom);
b.ContextMenu.IsOpen = true;//I have error in this Line
}
I use from my conrol in a DataGrid Cell ...when i click on my button i get an error in this line
b.ContextMenu.IsOpen = true;
"ContextMenu" cannot have logical or visual parent
how can i resolve this erorr
A sample of working app, hope this gives you required clue...
<DataGrid Margin="0,0,195,72" x:Name="A">
<DataGrid.Resources>
<ContextMenu x:Key="ContextMenu">
<MenuItem Header="Copy" Click="MenuItem_Click"></MenuItem>
<MenuItem Header="cut" Click="MenuItem_Click"></MenuItem>
<MenuItem Header="delete" Click="MenuItem_Click"></MenuItem>
</ContextMenu>
</DataGrid.Resources>
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="ContextMenu" Value="{StaticResource ContextMenu}">
</Setter>
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="A" Binding="{Binding}">
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>

Categories