C# WPF -> context menu bounded command won't be called - wpf

I have a little problem with wpf.
I have a listbox and in it a context menu,
but the bounded command won't be called if I am click on a menu item.
I think there is an error with the scope, but I dont have an idia to solve this problem, so I hope, that some guy here can help me.
Here is my code from the ui:
<ListBox Name="CompanyListBox" Margin="5" ItemsSource="{Binding Path=Companys}">
<ListBox.ItemTemplate>
<DataTemplate>
<Border CornerRadius="10" Background="LightGray" Margin="5">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="Akt. Item in einem neuen Fenster öffnen" Command="{Binding Path=OpenInNewWindowCommand}">
<MenuItem.Icon>
<Image Width="24" Height="24" Source="path/to/icon/icon.ico"></Image>
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Border.ContextMenu>
<StackPanel Orientation="Vertical" Margin="5">
//Here is the layout of the item
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And here the code from the viewmodel:
public ICommand OpenInNewWindowCommand
{
get
{
return new RelayCommand(OpenInNewWindow);
}
}
Yes, the DataContext are be setted to the view model, everything works excpet the context menu.
The method "OpenInNewWindow" will be not called from the ui if I clicked on the menu item, why?
(Sorry for bad englisch ;) I´m not from there ^^)

Related

WPF: ContextMenu MenuItem from DataContext/ItemsSource?

I'm building a simple App to store television shows. I have a Video class for the shows with some fields and properties including one reference to an object of type VideoSeason, representing the seasons of TV shows. The corresponding UI element of a Video object is a Button, with a ContextMenu with some actions.
I would like to create a MenuItem inside the ContextMenu, which contains all the seasons added to a TV show represented as submenuitems. I know that to do this, I have to mark the ObservableCollection Seasons as the ItemsSource of the MenuItem Seasons and indicate that any submenuitem inside the MenuItem is bound to the Property SeasonNumber inside VideoSeason.
My problem is that I dont't really know, how to go about binding these submenuitems in XAML, not if this is actually possible. I've already tried some options (e.g., WPF ContextMenu itemtemplate, menuitem inside menuitem, or Binding WPF ContextMenu MenuItem to UserControl Property vs ViewModel Property), but I want only my MenuItem to be bound, not the whole CntextMenu.
Here is the relevant part of the Video class:
public string Name { get; set; }
public int NextEpisode { get; set; }
public ObservableCollection<VideoSeason> Seasons { get; set; }
And here is the relevant part of the XAML code:
<ScrollViewer>
<StackPanel Name="filmHolder"
Grid.Row="1" Grid.Column="0" >
<ItemsControl Name="VideoUIElment">
<ItemsControl.ItemTemplate>
<DataTemplate x:Uid="videoTemplate">
<Border CornerRadius="10" Padding="10, 10" Background="Silver">
<Button Name="filmLabel" Content="{Binding Name}" FontSize="30" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"
Click="FilmLabel_Click" BorderThickness="0">
<Button.ContextMenu>
<ContextMenu Name="LocalMenu">
<MenuItem Header="Rename"/>
<MenuItem Header="Delete"/>
<MenuItem Header="Add New Season" Name="NewSeason" Click="NewSeason_Click"/>
<MenuItem Header="Seasons" ItemsSource="{Binding Seasons}">
<!--<MenuItem.ItemTemplate This is one of the things I tried in vain>
<DataTemplate>
<MenuItem Header="{Binding SeasonNumber}"/>
</DataTemplate>
</MenuItem.ItemTemplate>-->
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
As it can be seen, the problematic part is nested in the DataTemplate belonging to the UI of the Video, which might be the cause of the problem, but I'm not sure.
If you bind the ItemsSource property of the ItemsControl to an IEnumerable<Video>, this should work:
<ItemsControl Name="VideoUIElment" ItemsSource="{Binding Videos}">
<ItemsControl.ItemTemplate>
<DataTemplate x:Uid="videoTemplate">
<Border CornerRadius="10" Padding="10, 10" Background="Silver">
<Button Name="filmLabel" Content="{Binding Name}" FontSize="30" Foreground="Black" VerticalAlignment="Center" HorizontalAlignment="Center"
Click="FilmLabel_Click" BorderThickness="0">
<Button.ContextMenu>
<ContextMenu Name="LocalMenu">
<MenuItem Header="Rename"/>
<MenuItem Header="Delete"/>
<MenuItem Header="Add New Season" Name="NewSeason" Click="NewSeason_Click"/>
<MenuItem Header="Seasons" ItemsSource="{Binding Seasons}">
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding SeasonNumber}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Make sure that SeasonNumber is a public property of the VideoSeason class.

How to get menu items command CanExecute to apply when using images and text

If I convert my menu code from:
<MenuItem Header="Remove Special Event"
Command="{Binding RemoveSpecialEventCommand, Mode=OneWay}" />
To:
<MenuItem Command="{Binding RemoveSpecialEventCommand, Mode=OneWay}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<Image Source="Images/RemoveSpecialEvent.png" Stretch="None" Margin="0,0,5,0"/>
<TextBlock>Remove Special Event</TextBlock>
</StackPanel>
</MenuItem.Header>
</MenuItem>
Now the Command CanExecute is not being applied to the menu item. They are all enabled.
Update
I don't particularly expect this to work.
It worked!
I can't reproduce your issue, and I'm going to take you at your word that it's absolutely disabled, but yet does not gray out the text. I suppose it might be a theme thing.
Here's one thing you can try: Instead of giving the Header property a visual tree fragment, try templating the plain string header content instead.
<MenuItem
Header="Remove Special Event"
Command="{Binding RemoveSpecialEventCommand, Mode=OneWay}"
>
<MenuItem.HeaderTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image
Source="Images/RemoveSpecialEvent.png"
Stretch="None"
Margin="0,0,5,0"
/>
<ContentControl
Content="{Binding}"
/>
</StackPanel>
</DataTemplate>
</MenuItem.HeaderTemplate>
</MenuItem>
I don't particularly expect this to work.

WPF button with drop down list and arrow

Can someone suggest the best way to have a button with an arrow and dropdown list like in visual studio toolbar button new item. As you can find in VS the mouse hover is highlighting both default button and arrow button and after selecting an item from list the default button is changing according your selection.
Here is a piece of code which is showing drop down menu, but not for full functionality described above:
<StackPanel Orientation="Horizontal">
<Border CornerRadius="0" BorderBrush="Black" BorderThickness="1">
<Button Name="CreateButton" Click="CreateButton_Click" Background="Transparent" BorderBrush="{x:Null}">
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/add1.png" />
<Button.ContextMenu>
<ContextMenu HorizontalAlignment="Right">
<MenuItem Header=" doc" Click="CreateDocButton_Click">
<MenuItem.Icon>
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/add_sheet.png" Width="24" Height="24" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header=" xls" Click="CreateXlsButton_Click">
<MenuItem.Icon>
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/add_sheet.png" Width="24" Height="24" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header=" txt" Click="CreateTxtButton_Click">
<MenuItem.Icon>
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/add_sheet.png" Width="24" Height="24" />
</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</Button.ContextMenu>
</Button>
</Border>
<Border CornerRadius="0" BorderBrush="Black" BorderThickness="1">
<Button HorizontalAlignment="Left" VerticalAlignment="Stretch" Background="Transparent" BorderBrush="{x:Null}"
ContextMenuService.IsEnabled="False" Click="AddButtonContextMenu_Click">
<Image Source="/OMS.Resources;component/Resources/Images/LibraryImages/arrow_down.png" VerticalAlignment="Center" Width="9" />
</Button>
</Border>
</StackPanel>
The solution is to make use a menu item and decorate it.
XAML Code:
<MenuItem Click="AddPresetButton_Click" x:Name="AddPresetButton">
<MenuItem.Icon>
<Image Source="/MyApp.Application;component/Resources/add.png" Height="20"/>
</MenuItem.Icon>
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Add Preset"/>
<Image Source="/MyApp.Application;component/Resources/arrow_down_simple.png"
Height="10" Margin="2,0,0,0"/>
</StackPanel>
</MenuItem.Header>
<MenuItem.ContextMenu>
<ContextMenu>
<MenuItem Header="Add 1"/>
<MenuItem Header="Add 2"/>
<MenuItem Header="Add 3"/>
</ContextMenu>
</MenuItem.ContextMenu>
</MenuItem>
C# Code:
When the menu is pressed the context menu is opened.
private void AddPresetButton_Click(object sender, RoutedEventArgs e)
{
var addButton = sender as FrameworkElement;
if (addButton != null)
{
addButton.ContextMenu.IsOpen = true;
}
}
It looks like you have three problems to solve:
Styling / Layout
Highlight dropdown and button OnMouseOver
Change default button according to menu's last selection
Styling / Layout
Here are a couple of examples:
http://dvoituron.wordpress.com/2011/01/06/toolbar-dropdownbutton-in-wpf/
http://blogs.msdn.com/b/llobo/archive/2006/10/25/split-button-in-wpf.aspx
I am sure there are many other ways (e.g. using a plain button and ComboBox styled appropriately)
Highlighting dropdown and button OnMouseOver
Experiment with triggers; e.g:
WPF Mouseover Trigger Effect for Child Controls
WPF - How to change children's style on mouseover of parent
Change default button according to menu's last selection
Try the MVVM approach:
The button element will be bound to a property on your ViewModel. Each menu item will call an action (ICommand) in your ViewModel. This ViewModel will know which menu item was called, and update the button's property on the ViewModel. The button will automatically update using data binding.

Contextmenu on listview datatemplate not working

I have a simple ListView bound to a collection of Calculations. The view calls the calc Name property in the display. I have set the contextmenu to the individual items in the listview but on right click the context menu shows up as a tiny box with nothing in. What am i missing?
<ListView x:Name="CalcList" ItemsSource="{Binding Calculations}">
<ListView.ItemTemplate>
<DataTemplate DataType="x:Type lib:Calculation">
<DataTemplate.Resources>
<ContextMenu x:Key="CalcMenu">
<MenuItem Header="Delete Calculation" Click="MenuItem_Click"/>
<MenuItem Header="Another"/>
<MenuItem Header="Another"/>
</ContextMenu>
</DataTemplate.Resources>
<Border BorderBrush="Black" BorderThickness="1" Margin="2">
<Border.ContextMenu>
<ContextMenu ContextMenu="{StaticResource CalcMenu}"/>
</Border.ContextMenu>
<TextBlock MouseLeftButtonDown="DisplayCalc" Text="{Binding Path=Name}"></TextBlock>
</Border>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Thanks.
If anything, you have a syntax error the way you define the Border.ContextMenu element. The correct syntax (of the Border Element):
<Border BorderBrush="Black" BorderThickness="1"
Margin="2"
ContextMenu="{StaticResource CalcMenu}">
<TextBlock ... />
</Border>
Saying that, there is a lot of optimization you can do. First, instead of instantiating a context menu for each item, you can move the CalcMenu to the section (one level up), or even farther up - to the main window.
Second, why do you define a separate context menu for each item? Is it really important context menu won't popu when mouse is in the margin between items? simply set the context menu to the entire list:
<ListView x:Name="CalcList" ContextMenu="{StaticResource CalcMenu}">
...
and define the CalcMenu as a main window resources, or inline the ListView element (not a static resource).

Bind Command for MenuItem in DataTemplate without Tag

I want to bind a command in my ViewModel to a menuItem which is in DataTemplate. I can do that with using Tag. Is there any method which can do the same task but without using tag.
<Window.Resources>
<DataTemplate x:Key="StudentListBoxItemTemplate">
<StackPanel Tag="{Binding DataContext, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}">
<TextBlock Text="{Binding Name}"/>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Header="Trigger" Command="{Binding PlacementTarget.Tag.TriggerCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"/>
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
</DataTemplate>
</Window.Resources>
<StackPanel>
<ListBox
ItemsSource="{Binding StudentList}"
ItemTemplate="{StaticResource StudentListBoxItemTemplate}">
</ListBox>
</StackPanel>
My ViewModel
public class MainViewModel {
public ICommand TriggerCommand { ... }
public ObservableList<Student> StudentList { ... }
}
With your current design, you need to get from the ContextMenu through the StackPanel and back to the DataContext of the containing ListBox. What makes this awkward is that the DataContext of the StackPanel is already narrowed down to a particular student.
There are at least two ways to make this easier:
Provide a TriggerCommand property in Student so the command is right there where you need it
Provide a Parent property in the Student to escape the narrowed scope
You can try to add click event to the menuItem like following
<Menu Style="{StaticResource bellRingersFontStyle}" Height="23" Name="menu1" Width="Auto" DockPanel.Dock="Top" VerticalAlignment="Top">
<MenuItem Header="_File">
<MenuItem Header="_New Member" Name="newMember" Click="newMember_Click" >
<MenuItem.Icon>
<Image Source="Face.bmp" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Save Member Details" Name="saveMember" IsEnabled="False" Click="saveMember_Click">
<MenuItem.Icon>
<Image Source="Note.bmp" />
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Header="E_xit" Name="exit" Click="exit_Click" />
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="_About Middleshire Bell Ringers" Name="about" Click="about_Click" >
<MenuItem.Icon>
<Image Source="Ring.bmp" />
</MenuItem.Icon>
</MenuItem>
</MenuItem>
</Menu>
Try binding the command to Click. My VS is down so can't check at this moment.
One way would be to get the context menu collection presentation defined in your viewmodel, which will contain the header string and command action (maybe with predicate).
The viewmodel creates an observable collection of the contextmenu items and the view binds that to ContextMenu itemssource and sets the displaymember path to header string.

Resources