WPF Menu Item Selection - wpf

I have a MenuItem which sub items is generated through a ItemsControl items source colletion. If I add MenuItems in the xaml, each individual item display a selection on it self when mouse over, see first image. On the collection bound item it also "selects the collection" and it also gets indented, see second image, how can I disable this?
XAML
<MenuItem
Name="Template"
Header="Startmall"/>
<MenuItem
Header="Symboler"
Name="CustomerSymbols">
<MenuItem Header="Test1"/>
<MenuItem Header="Test2"/>
<MenuItem Header="Test3"/>
<ItemsControl ItemsSource="{Binding CustomerSymbols}"/>
C#
//Populating CustomerSymbols on class initalization
private void LoadCustomerSymbols()
{
List<string> files = Settings.CustomerFiles; // List of string files
foreach(string file in files)
{
MenuItem menuItem = new MenuItem();
string fileName = Path.GetFileNameWithoutExtension(file);
menuItem.Header = fileName;
CustomerSymbols.Add(menuItem);
}
}

This is not valid
<ItemsControl ItemsSource="{Binding CustomerSymbols}"/>
You can do it like this
<MenuItem Header="Symboler">
<MenuItem.ItemsSource>
<CompositeCollection>
<MenuItem Header="Test1" />
<MenuItem Header="Test2" />
<MenuItem Header="Test3" />
<CollectionContainer Collection="{Binding Source={StaticResource MyMenuItems}}" />
</CompositeCollection>
</MenuItem.ItemsSource>
</MenuItem>
Define MyMenuItems at one of MenuItem's parents' resources (ex. <Menu />)
Full example:
<Menu>
<Menu.Resources>
<CollectionViewSource x:Key="MyMenuItems" Source="{Binding CustomerSymbols}" />
</Menu.Resources>
<!-- other code -->
<MenuItem Header="Symboler">
<MenuItem.ItemsSource>
<CompositeCollection>
<MenuItem Header="Test1" />
<MenuItem Header="Test2" />
<MenuItem Header="Test3" />
<CollectionContainer Collection="{Binding Source={StaticResource MyMenuItems}}" />
</CompositeCollection>
</MenuItem.ItemsSource>
</MenuItem>
<!-- other code -->
</Menu>

Related

Enable or disable context menu from vb.net code

I have following context menu which should only be enabled if my "Globals.admin = true".
I have other context menus bound specificaly for some button, label, control, etc. My idea is to enable/disable this context menu bellow based on state of "Globals.admin" in my vb.net code
<ContextMenu x:Key="cm">
<MenuItem Header="Block" Click="MenuItem_Click" />
<MenuItem Header="Unblock" Click="MenuItem2_Click"/>
<MenuItem Header="-"/>
<MenuItem Header="Hide="MenuItem3_Click"/>
<MenuItem Header="Show="MenuItem4_Click"/>
</ContextMenu>
Bind the IsEnabled property to your static source property:
<ContextMenu x:Key="cm" IsEnabled="{Binding Source={x:Static local:Globals.admin}}">
<MenuItem Header="Block" Click="MenuItem_Click" />
<MenuItem Header="Unblock" Click="MenuItem2_Click"/>
<MenuItem Header="-"/>
<MenuItem Header="Hide" />
<MenuItem Header="Show" />
</ContextMenu>

How can I take different MenuItem controls and use them in one ContextMenu?

For example, I have a simple control below in xaml:
<ContextMenu>
<MenuItem Header = "Open in new tab"/>
<MenuItem Header = "Open in new window"/>
<MenuItem Header = "Open in incognito window"/>
<Separator Padding="0"/>
<MenuItem Header = "Edit..."/>
<Separator Padding="0"/>
<MenuItem Header = "Cut"/>
<MenuItem Header = "Copy"/>
<MenuItem Header = "Paste"/>
<Separator Padding="0"/>
<MenuItem Header = "Delete"/>
</ContextMenu>
Which produces:
I want the Cut/Copy/Paste MenuItem elements to sit in their own control. However, Wpf restricts me from just declaring 3 MenuItems in a control without a parent element. So I tried putting the MenuItems in a Menu, Grid, ItemsControl, and other controls with no luck. The formatting was weird. All i want to do is abstract the Cut/Copy/Paste into a seperate control, and have the end product look identical to the xaml above. Here were my failed attempts:
MyContextMenu.xaml:
<ContextMenu>
<MenuItem Header = "Open in new tab"/>
<MenuItem Header = "Open in new window"/>
<MenuItem Header = "Open in incognito window"/>
<Separator Padding="0"/>
<MenuItem Header = "Edit..."/>
<Separator Padding="0"/>
<ref:MyCutCopyPaste/>
<Separator Padding="0"/>
<MenuItem Header = "Delete"/>
</ContextMenu>
MyCutCopyPaste.xaml:
<Grid>
<MenuItem Header = "Cut"/>
<MenuItem Header = "Copy"/>
<MenuItem Header = "Paste"/>
<Grid>
I can probably take my solution and play around with formatting, but there has to be an easy solution that I'm oblivious too. I know I can also declare each Menu Item (Cut, Copy, and Paste) into it's own xaml file. But I'd like to keep all three in one file, if possible.
You can define those MenuItems as resources and use it in the contextmenu. Refer the below code.
<StackPanel>
<StackPanel.Resources>
<MenuItem x:Key="Cut" Header="Cut" x:Shared="false"/>
<MenuItem x:Key="Copy" Header="Copy" x:Shared="false"/>
<MenuItem x:Key="Paste" Header="Paste" x:Shared="false"/>
</StackPanel.Resources>
<Button Content="Button 1">
<Button.ContextMenu>
<ContextMenu>
<StaticResourceExtension ResourceKey="Cut"/>
<StaticResourceExtension ResourceKey="Copy"/>
<StaticResourceExtension ResourceKey="Paste"/>
<MenuItem Header="Open in new tab"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
<Button Content="Button 2">
<Button.ContextMenu>
<ContextMenu>
<StaticResourceExtension ResourceKey="Cut"/>
<StaticResourceExtension ResourceKey="Copy"/>
<StaticResourceExtension ResourceKey="Paste"/>
<MenuItem Header="Open in new window"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
</StackPanel>

ContextMenu items Visibility

I have some ContextMenu With some menuItems.
One of the menuItems is "Add Item...". when the user will selects this item, he should see submenu with list of available items;
here is description of my contextMenu:
Add Item
Item 1
Item 2
Item 3
Item 4
Delete
Copy
the ItemsSource of 'Add Item' menuItem is binded to some observable collection. Each model in the collecion has 'Name' and 'IsEnabled' (I have converter which convert bool to visibility). Everything works fine except the fact that the items which has 'IsEnable' = false, are not visible, but I can see there space.
for example: let say that Item 3 has IsEnable = false:
Add Item
Item 1
Item 2
Item 4
My bool2Vis converter return 'Collapsed' in case of false value.
Whay I'm doing wrong?
Here is the ContextMenu Code:
<ContextMenu x:Key="mainContextMenu" DataContext="{Binding Source={x:Static fw:UIMainManager.Instance},Path=layoutManager}">
<MenuItem Header="Add Item" Name="addItemMenu" ItemsSource="{Binding ControlBoxItems}" >
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding ControlName}" Visibility="{Binding IsEnabled,Converter={StaticResource boolToVisibilityConverter}}"
Command="{Binding Source={x:Static fw:ApplicationCommands.AddControlToScene}}" CommandParameter="{Binding}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
<MenuItem Header="Copy" Command="{Binding Source={x:Static fw:ApplicationCommands.Copy}}" />
<MenuItem Header="Paste" Command="{Binding Source={x:Static fw:ApplicationCommands.Paste}}" />
<MenuItem Header="Cut" Command="{Binding Source={x:Static fw:ApplicationCommands.Cut}}" />
<MenuItem Header="Duplicated" Command="{Binding Source={x:Static fw:ApplicationCommands.DuplicateControl}}" />
<MenuItem Header="Delete" Command="{Binding Source={x:Static fw:ApplicationCommands.DeleteControl}}" />
</ContextMenu>
I think the MenuItem is collapsed but not the ItemContainer of MenuItem. Try this:
<MenuItem Header="Add Item" Name="addItemMenu" ItemsSource="{Binding ControlBoxItems}">
<MenuItem.ItemContainerStyle>
<Style>
<Setter Property="Visibility" Value="{Binding IsEnabled, Converter={StaticResource boolToVisibilityConverter}}" />
</Style>
</MenuItem.ItemContainerStyle>
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding ControlName}"
Command="{Binding Source={x:Static fw:ApplicationCommands.AddControlToScene}}"
CommandParameter="{Binding}" />
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>

XAML and Binding Submenu items in a ContextMenu?

I have a ContextMenu defined on a Datagrid but want to bind submenu items to a collection on my viewmodel. Can anybody suggest how this should be done?
The following is a simple example of what I'm trying to achieve, BUT I want "Test1", "Test2" to come from a collection on my viewmodel, not hardcoded. I know how to bind my collection to the whole ContextMenu, but not how to bind it to just the one submenu...
<ContextMenu>
<MenuItem Header="Add to">
<MenuItem Header="Test1" />
<MenuItem Header="Test2" />
</MenuItem>
<MenuItem Header="Remove from All" />
</ContextMenu>
I'm using 3.5 SP1 and the WPF Toolkit.
Guess I should have experimented more. Turns out this was relatively simple:
<my:DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Add to" ItemsSource="{Binding MyItems}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem CommandTarget="{Binding}" Click="AddClick">
<MenuItem.Header>
<TextBlock>
<TextBlock.Text><Binding StringFormat="Add to {0}" /></TextBlock.Text>
</TextBlock>
</MenuItem.Header>
</MenuItem>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
<MenuItem Header="Remove from All" />
</ContextMenu>
</my:DataGrid.ContextMenu>
There is a bug when using MenuItem.ItemTemplate. The color when do mouse over on the sub menu make user misunderstand that they can click to select the menu but it doesn't work for all area even if it's highlighted. See the picture
Then I used this code instead and it worked fine for me.
<ContextMenu>
<MenuItem Header="Add to" ItemsSource="{Binding MyItems}"
DisplayMemberPath="{Binding ItemName}">
<MenuItem.ItemContainerStyle>
<Style>
<EventSetter Event="MenuItem.Click" Handler="Menu_Click"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
<MenuItem Header="Remove from All" />
</ContextMenu>

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