I've setup a custom command with PageDown as the key gesture, but for the MenuItem that is bound to that command the gesture shows up as "Next". The problem is that I have commands that use PageUp, Home, and End for related tasks and they all show up as expected for their MenuItems. Is there some way to make the MenuItem show "PageDown" instead of "Next" for consistency?
Here's the xaml that defines the commands.
<Window.Resources>
<RoutedUICommand x:Key="FirstPageCommand"
Text="FirstPage">
<RoutedUICommand.InputGestures>
<KeyGesture>Home</KeyGesture>
</RoutedUICommand.InputGestures>
</RoutedUICommand>
<RoutedUICommand x:Key="LastPageCommand"
Text="LastPage">
<RoutedUICommand.InputGestures>
<KeyGesture>End</KeyGesture>
</RoutedUICommand.InputGestures>
</RoutedUICommand>
<RoutedUICommand x:Key="PreviousPageCommand"
Text="PreviousPage">
<RoutedUICommand.InputGestures>
<KeyGesture>PageUp</KeyGesture>
</RoutedUICommand.InputGestures>
</RoutedUICommand>
<RoutedUICommand x:Key="NextPageCommand"
Text="NextPage">
<RoutedUICommand.InputGestures>
<KeyGesture>PageDown</KeyGesture>
</RoutedUICommand.InputGestures>
</RoutedUICommand>
</Window.Resources>
And here is where I use them in a Menu
<MenuItem Header="_View">
<MenuItem Header="_First Page"
Command="{StaticResource FirstPageCommand}">
<MenuItem.Icon>
<Image Source="Images\Backward_01.png"
Stretch="Uniform"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Previous Page"
Command="{StaticResource PreviousPageCommand}">
<MenuItem.Icon>
<Image Source="Images\Backward.png"
Stretch="Uniform"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Next Page"
Command="{StaticResource NextPageCommand}">
<MenuItem.Icon>
<Image Source="Images\Forward.png"
Stretch="Uniform"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Last Page"
Command="{StaticResource LastPageCommand}">
<MenuItem.Icon>
<Image Source="Images\Forward_01.png"
Stretch="Uniform"/>
</MenuItem.Icon>
</MenuItem>
</MenuItem>
My menu looks like this
View
First Page Home
Last Page PageUp
Next Page Next
Last Page End
Apparently this is only possible if you define your commands in code like this.
public class MyCommands
{
public static RoutedUICommand NextPage { get; private set; }
public static RoutedUICommand PreviousPage { get; private set; }
static OCRopingCommands()
{
NextPage = new RoutedUICommand("NextPage", "NextPage", typeof(MyCommands));
NextPage.InputGestures.Add(
new KeyGesture(Key.PageDown, ModifierKeys.None, "PageDn"));
PreviousPage = new RoutedUICommand("PreviousPage", "PreviousPage", typeof(MyCommands));
PreviousPage.InputGestures.Add(
new KeyGesture(Key.PageUp, ModifierKeys.None, "PageUp"));
}
}
And here's how you'd wire them up
<MenuItem Header="_Previous Page"
Command="local:MyCommands.PreviousPage">
<MenuItem.Icon>
<Image Source="Images\Backward.png"
Stretch="Uniform"/>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="_Next Page"
Command="local:MyCommands.NextPage">
<MenuItem.Icon>
<Image Source="Images\Forward.png"
Stretch="Uniform"/>
</MenuItem.Icon>
</MenuItem>
Why the KeyGesture doesn't allow you to set the display name in xaml is beyond me.
Related
With the help of this article, I created TreeView by using WPF with MVVM. Now I want to add different context menu to every items in the one level.
A google research allows me to create a same context menu to every items in one level.
So my question Do you have any idea that allow me to create context Menu to every item in the same level with full respect to the architecture MVVM ?
Below the code that allow me to create same context menu to every items in one level:
<TreeView.Resources>
<!-- Begin Context Menu -->
<ContextMenu x:Key="TreeViewContextualMenuLevel2" >
<MenuItem Command="{Binding AddLevelTwoCommand}" Header="Add"/>
</ContextMenu>
<ContextMenu x:Key="TreeViewContextualMenuLevel3" >
<MenuItem Command="{Binding EditCommand}" Header="Edit" />
<MenuItem Command="{Binding RemoveCommand}" Header="Remove " />
</ContextMenu>
<!-- End context Menu -->
<!-- Begin Level 1 -->
<HierarchicalDataTemplate
DataType="{x:Type local:FirstLevelViewModel}"
ItemsSource="{Binding Children}" >
<StackPanel Orientation="Horizontal">
<Image Width="16" Height="16" Margin="3,0" Source="/Images\RedCircle.png" />
<TextBlock Text="{Binding DefEntity1Name}" />
</StackPanel>
</HierarchicalDataTemplate>
<!-- End Level 1 -->
<!-- Begin Level 2: Root -->
<HierarchicalDataTemplate DataType="{x:Type local:SecondLevelViewModel}" ItemsSource="{Binding Children}">
<StackPanel Orientation="Horizontal" ContextMenu="{StaticResource TreeViewContextualMenuLevel2}">
<Image Width="16" Height="16" Margin="3,0" Source="/Images\RedCircle.png" />
<TextBlock Text="{Binding DefEntity2Name}" />
</StackPanel>
</HierarchicalDataTemplate>
<!-- End Level 2: Root -->
<!-- Begin Level 3 -->
<DataTemplate DataType="{x:Type local:ThirdLevelViewModel}">
<StackPanel Orientation="Horizontal" ContextMenu="{StaticResource TreeViewContextualMenuLevel3}">
<Image Width="16" Height="16" Margin="3,0" Source="/Images\GreenCircle.png" />
<TextBlock Text="{Binding ThirdLevelEntityName}" />
</StackPanel>
</DataTemplate>
<!-- End Level 3 -->
</TreeView.Resources>
You add collection of MenuItems to your view model and bind ContextMenu.ItemsSource to it:
Example:
public class ViewModel
{
public List<MenuItem> ContextMenuItems { get; set; }
public ViewModel()
{
ContextMenuItems = new List<MenuItem>();
ContextMenuItems .Add(new MenuItem() { Header = "item1", Command = new RelayCommand(() => { MessageBox.Show("Item 1 is clicked", "test", MessageBoxButton.OK, MessageBoxImage.Error); }) });
ContextMenuItems.Add(new MenuItem() { Header = "item2", Command = new RelayCommand(() => { MessageBox.Show("Item 2 is clicked", "test", MessageBoxButton.OK, MessageBoxImage.Error); }) });
}
}
And in your xaml resources:
<ContextMenu x:Key="TreeViewContextualMenuLevel2" ItemsSource="{Binding ContextMenuItems}" />
So, you could create individual context menu for every item based on conditions you have
Example of RelayCommand can be found here
Hope, it helps
I have a prism delegate command setup for refresh the data in my application.
I also have setup shortcuts for close and about window using delegate command.
Using F5 also does not trigger the refresh in the view model. If I use Alt+F4, the application closes. But it does not execute the CloseApplication method in the view model. What is missing in the following code to execute the ViewModel using the Gesture Shorcuts?
UserControl:
<UserControl.InputBindings>
<KeyBinding Gesture="ALT+F4" Command="{Binding CloseCommand}" />
<KeyBinding Gesture="F5" Command="{Binding RefreshCommand}" />
</UserControl.InputBindings>
<DockPanel>
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Header="E_xit" Command="{Binding CloseCommand}" InputGestureText="Alt+F4" />
<MenuItem Header="Refresh" Command="{Binding RefreshCommand}" InputGestureText="F5" />
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="_About" Command="{Binding AboutCommand}" />
</MenuItem>
</Menu>
<ToolBar ToolBarTray.IsLocked="True">
<Button Content="Home" MinHeight="50" MinWidth="80" VerticalContentAlignment="Bottom" />
<Button Content="Page 1" MinHeight="50" MinWidth="80" VerticalContentAlignment="Bottom" />
<Button Content="Page 2" MinHeight="50" MinWidth="80" VerticalContentAlignment="Bottom" />
</ToolBar>
</DockPanel>
ViewModel of the UserControl
public class TopMenuViewModel:NotificationObject
{
private DelegateCommand _closeCommand;
private DelegateCommand _aboutCommand;
private DelegateCommand _refreshCommand;
public TopMenuViewModel()
{
_closeCommand = new DelegateCommand(CloseApplication);
_aboutCommand = new DelegateCommand(ShowAboutWindow);
_refreshCommand = new DelegateCommand(RefreshApplication);
}
private void ShowAboutWindow()
{
Console.WriteLine("Show about window");
}
private void CloseApplication()
{
Application.Current.Shutdown();
}
private void RefreshApplication()
{
Console.WriteLine("Refreshing..");
}
public ICommand CloseCommand
{
get { return _closeCommand; }
}
public ICommand AboutCommand
{
get { return _aboutCommand; }
}
public ICommand RefreshCommand
{
get { return _refreshCommand; }
}
}
The view model methods are called if I use the mouse to click the menu or use the Alt+x or Alt+A. But not using the KeyBinding that I have put in. Thanks for your help.
I have a button with an Image as its content in a toolbar. I would like this button to open a menu beneath it when clicked. How?
<Toolbar>
<Button>
<Button.Content>
<Image Source="../Resources/help.png"></Image>
</Button.Content>
</Button>
</Toolbar>
Thanks!!
Instead of using a subclassed Button, you can use Attached Properties or a Behavior to implement the drop down button functionality, for a more WPF-like approach and so you don't impact the button style:
using System.Windows.Interactivity;
public class DropDownButtonBehavior : Behavior<Button>
{
private bool isContextMenuOpen;
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.AddHandler(Button.ClickEvent, new RoutedEventHandler(AssociatedObject_Click), true);
}
void AssociatedObject_Click(object sender, System.Windows.RoutedEventArgs e)
{
Button source = sender as Button;
if (source != null && source.ContextMenu != null)
{
if (!isContextMenuOpen)
{
// Add handler to detect when the ContextMenu closes
source.ContextMenu.AddHandler(ContextMenu.ClosedEvent, new RoutedEventHandler(ContextMenu_Closed), true);
// If there is a drop-down assigned to this button, then position and display it
source.ContextMenu.PlacementTarget = source;
source.ContextMenu.Placement = PlacementMode.Bottom;
source.ContextMenu.IsOpen = true;
isContextMenuOpen = true;
}
}
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.RemoveHandler(Button.ClickEvent, new RoutedEventHandler(AssociatedObject_Click));
}
void ContextMenu_Closed(object sender, RoutedEventArgs e)
{
isContextMenuOpen = false;
var contextMenu = sender as ContextMenu;
if (contextMenu != null)
{
contextMenu.RemoveHandler(ContextMenu.ClosedEvent, new RoutedEventHandler(ContextMenu_Closed));
}
}
}
Usage:
<!-- NOTE: xmlns:i="schemas.microsoft.com/expression/2010/interactivity" -->
<Button>
<i:Interaction.Behaviors>
<local:DropDownButtonBehavior/>
</i:Interaction.Behaviors>
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Source="/DropDownButtonExample;component/Assets/add.png" SnapsToDevicePixels="True" Height="16" Width="16" />
<TextBlock Text="Add"/>
<Separator Margin="2,0">
<Separator.LayoutTransform>
<TransformGroup>
<TransformGroup.Children>
<TransformCollection>
<RotateTransform Angle="90"/>
</TransformCollection>
</TransformGroup.Children>
</TransformGroup>
</Separator.LayoutTransform>
</Separator>
<Path Margin="2" VerticalAlignment="Center" Width="6" Fill="#FF527DB5" Stretch="Uniform" HorizontalAlignment="Right" Data="F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z "/>
</StackPanel>
</Button.Content>
<Button.ContextMenu>
<ContextMenu>
<MenuItem Header="Attribute"/>
<MenuItem Header="Setting"/>
<Separator/>
<MenuItem Header="Property"/>
</ContextMenu>
</Button.ContextMenu>
</Button>
Current gist source and example here.
If you have the luxury of targeting .NET 4 or newer, the new Ribbon library has a RibbonMenuButton that can do this. In 4.5 it is as easy as referencing System.Windows.Controls.Ribbon in your project:
<RibbonMenuButton x:Name="ExampleMenu" SmallImageSource="/Images/Example.png">
<RibbonMenuItem x:Name="ExampleMenuItem" Header="Save" />
</RibbonMenuButton>
i found this two solutions after searching for it:
1) Split Button in WPF
2) DropDownButtons in WPF
the second solution is my favorit (source taken from the website by Andrew Wilkinson)
public class DropDownButton : ToggleButton
{
// *** Dependency Properties ***
public static readonly DependencyProperty DropDownProperty =
DependencyProperty.Register("DropDown",
typeof(ContextMenu),
typeof(DropDownButton),
new UIPropertyMetadata(null));
// *** Constructors ***
public DropDownButton() {
// Bind the ToogleButton.IsChecked property to the drop-down's IsOpen property
Binding binding = new Binding("DropDown.IsOpen");
binding.Source = this;
this.SetBinding(IsCheckedProperty, binding);
}
// *** Properties ***
public ContextMenu DropDown {
get { return (ContextMenu)this.GetValue(DropDownProperty); }
set { this.SetValue(DropDownProperty, value); }
}
// *** Overridden Methods ***
protected override void OnClick() {
if (this.DropDown != null) {
// If there is a drop-down assigned to this button, then position and display it
this.DropDown.PlacementTarget = this;
this.DropDown.Placement = PlacementMode.Bottom;
this.DropDown.IsOpen = true;
}
}
}
usage
<ctrl:DropDownButton Content="Drop-Down">
<ctrl:DropDownButton.DropDown>
<ContextMenu>
<MenuItem Header="Item 1" />
<MenuItem Header="Item 2" />
<MenuItem Header="Item 3" />
</ContextMenu>
</ctrl:DropDownButton.DropDown>
</ctrl:DropDownButton>
hope that helps you...
There are lots of ways to get this done and you might consider this approach...
<ToolBar DockPanel.Dock="Top">
<MenuItem IsSubmenuOpen="{Binding SomeProperty}">
<MenuItem.Header>
<Button Height="28">
<Button.Content>
<Image Source="---your image---"></Image>
</Button.Content>
</Button>
</MenuItem.Header>
<Menu>
<MenuItem Header="Do this" />
<MenuItem Header="Do that"/>
</Menu>
</MenuItem>
</ToolBar>
This wraps your button into a MenuItem that has a submenu. As shown here, the MenuItem property called IsSubMenuOpen is bound to a notifying property of type bool in your ViewModel called SomeProperty.
You would have to have your ViewModel toggle this property depending upon what you are actually trying to do. You may want to consider making your button a toggle button so as to facilitate closing the submenu, otherwise you'll have to wire up additional behaviour in your ViewModel.
I am trying to pass Selected MenuItem's Text/Header string as the MethodParameter to my ObjectDataProvider. I have seen examples like these on the internet but haven't been able to adapt it to the Menu Control specifically. I am new to WPF and need some help accomplish this. Any help would be greatly appreciated.
Below is the code snippet, XAML for the ObjectDataProvider
<Window.Resources>
<ObjectDataProvider x:Key="NMInfo" ObjectType="{x:Type local:NMInfoProvider}" MethodName="GetDcmsInfomation" IsAsynchronous="True">
<ObjectDataProvider.MethodParameters>
<x:Static Member="system:String.Empty" />
</ObjectDataProvider.MethodParameters>
</ObjectDataProvider>
</Window.Resources>
XAML for the Menu control
<Menu Name="nmMenu" Height="25" HorizontalAlignment="Stretch" VerticalAlignment="Top" FontSize="12" DockPanel.Dock="Top">
<Menu.BitmapEffect>
<DropShadowBitmapEffect/>
</Menu.BitmapEffect>
<MenuItem Header="File">
<MenuItem Header="SNYC12P10650" IsCheckable="True" ToolTip="Production" Click="MenuItem_Clicked">
<MenuItem.IsChecked>
<Binding Source="{StaticResource NMInfo}" Path="MethodParameters[0]" BindsDirectlyToSource="True" Mode="OneWayToSource"/>
</MenuItem.IsChecked>
</MenuItem>
<MenuItem Header="GPRI12D10217" IsCheckable="True" ToolTip="QA" Click="MenuItem_Clicked">
<MenuItem.IsChecked>
<Binding Source="{StaticResource NMInfo}" Path="MethodParameters[0]" BindsDirectlyToSource="True" Mode="OneWayToSource"/>
</MenuItem.IsChecked>
</MenuItem>
<MenuItem Header="GPRI12D10219" IsCheckable="True" ToolTip="Dev" Click="MenuItem_Clicked">
<MenuItem.IsChecked>
<Binding Source="{StaticResource NMInfo}" Path="MethodParameters[0]" BindsDirectlyToSource="True" Mode="OneWayToSource"/>
</MenuItem.IsChecked>
</MenuItem>
<Separator/>
<MenuItem Header="Close"/>
</MenuItem>
</Menu>
What you need to do is Bind the Header property, not IsChecked. I'm assuming you only want to do this when the item is checked though. While this would be feasible by using a Style for the MenuItem, I would advocate doing this sort of work in a ViewModel.
Instead of having an ObjectDataProvider, your VM would expose boolean properties for each of the checkable menu items. When any of these properties changed, it could call that method itself, and expose the object as a read-only property. Just set the DataContext of the whole control to an instance of your VM, and the bindings would work.
Something like so:
public class NMInfoViewModel : INotifyPropertyChanged
{
private bool isSNYC12P10650 = false;
public bool IsSNYC12P10650
{
get { return isSNYC12P10650; }
set
{
if (value == isSNYC12P10650) return;
isSNYC12P10650 = value;
OnPropertyChanged("IsSNYC12P10650");
if (value)
NMInfo = NMInfoProvider.GetDcmsInfomation("SNYC12P10650");
}
}
...
private NMInfo nMInfo;
public NMInfo NMInfo
{
get { return nMInfo; }
private set
{
if (value == nMInfo) return;
nMInfo = value;
OnPropertyChanged("NMInfo");
}
}
}
And your MenuItems would look like this:
<MenuItem Header="SNYC12P10650" IsCheckable="True"
ToolTip="Production" IsChecked="{Binding IsSNYC12P10650}" />
I am binding a ToolBar to a collection of command view model objects. The objects in the collection have a property IsSeparator that when true I would like represented with a <Separator/> in the ToolBar.
My basic markup looks like this:
<ToolBar Grid.Row="1" ItemsSource="{Binding Path=ToolBarCommands}">
<ToolBar.ItemTemplate>
<DataTemplate>
<Button ToolTip="{Binding Path=ToolTip}" Command="{Binding Path=Command}">
<Button.Content>
<Image Width="16" Height="16" Source="{Binding Path=IconStream}"/>
</Button.Content>
</Button>
</DataTemplate>
</ToolBar.ItemTemplate>
</ToolBar>
I've played around with ItemContainerStyle much like this example for MenuItems but to no avail.
Any help is appreciated.
I followed Josh's suggestion about using a DataTemplateSelector and I'm just going to post code to help others.
public class ToolBarItemTemplateSelector : DataTemplateSelector
{
public DataTemplate ButtonTemplate { get; set; }
public DataTemplate SeparatorTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var toolBarItem = (ToolBarItemViewModel) item;
Debug.Assert(toolBarItem != null);
if (!toolBarItem.IsSeparator)
{
return ButtonTemplate;
}
return SeparatorTemplate;
}
}
<DataTemplate x:Key="buttonTemplate" DataType="{x:Type infrastructure:ToolBarItemViewModel}">
<Button Command="{Binding Command}" ToolTip="{Binding ToolTip}" Style="{DynamicResource ResourceKey={x:Static ToolBar.ButtonStyleKey}}">
<Image Source="{Binding ImageSource}" Width="16" Height="16" />
</Button>
</DataTemplate>
<DataTemplate x:Key="separatorTemplate">
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
</DataTemplate>
<local:ToolBarItemTemplateSelector ButtonTemplate="{StaticResource buttonTemplate}" SeparatorTemplate="{StaticResource separatorTemplate}" x:Key="toolBarItemTemplateSelector" />
<ToolBar AutomationProperties.AutomationId="toolBar" ItemsSource="{Binding ToolBarItems}" x:Name="toolBar" Band="1" BandIndex="1" ItemTemplateSelector="{StaticResource toolBarItemTemplateSelector}"/>
Rather than inserting Separator objects, you could just grab Separator's ControlTemplate (in Blend, right click a separator -> Edit Template -> Edit a Copy) and incorporate it directly into your button template. You can use a DataTrigger to control its visibility, perhaps binding to a "BeginGroup" property on your object.
If you want to have a dedicated Separator object in your collection you could use a DataTemplateSelector.