I am trying to learn keybinding in WPF by a simple example.
here is my XAML file:
<Window.Resources>
<RoutedUICommand x:Key="myNewCommand"></RoutedUICommand>
</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command="{StaticResource myNewCommand}" Executed="Button_Click"></CommandBinding>
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Command="{Binding myNewCommand}" Key="B" />
</Window.InputBindings>
<Grid>
<Button Command="{Binding myNewCommand}" Click="Button_Click" Content="Click Here"/>
</Grid>
And this is code behind for Button_Click :
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("hello");
}
I get the "hello" message when I click on button, but no response when I press "B" on keyboard.
I want to have this binding without changing the Button_click, can I do it just in XAML? how?
Your command bindings aren't correct. You should replace {Binding myNewCommand} with {Binding Source={StaticResource myNewCommand}}. It is also not necessary to have a Click handler on the button when you have already bound a command.
<Window.Resources>
<RoutedUICommand x:Key="myNewCommand"/>
</Window.Resources>
<Window.CommandBindings>
<CommandBinding Command="{StaticResource myNewCommand}"
Executed="MyCommandExecuted"/>
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Command="{Binding Source={StaticResource myNewCommand}}" Key="B" />
</Window.InputBindings>
<Grid>
<Button Command="{Binding Source={StaticResource myNewCommand}}"
Content="Click Here"/>
</Grid>
The Executed handler:
private void MyCommandExecuted(object sender, RoutedEventArgs e)
{
MessageBox.Show("hello");
}
Related
Is there a way to create a CommandBinding with a parameter?
You can normally add bindings to commands for controls doing something like this:
<Control.CommandBindings>
<CommandBinding Command="ApplicationCommands.XXX" CanExecute="XXX_CanExecute"
Executed="XXX_Executed">
</CommandBinding>
</Control.CommandBindings>
I was wondering if there was also a way to pass a paramater?
No, you dont have a CommandParameter on the CommandBinding but you can use the InputBindings from UIElement to add for example a MouseBinding, which has a Command and CommandParameter attribute.
<Control>
<Control.InputBindings>
<MouseBinding MouseAction="LeftClick"
Command="{StaticResource MyCommand}"
CommandParameter="{x:Static system:Boolean.TrueString}"/>
</Control.InputBindings>
</Control>
You have to assign the parameter value to the ICommandSource.CommandParameter property of the element that invokes the command (for example Button.CommandParameter). This property also accepts a Binding.
You can retrieve the parameter from the ExecutedRoutedEventArgs.Parameter and CanExecuteRoutedEventArgs.Parameter property.
<Window>
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Delete"
CanExecute="DeleteCommand_CanExecute"
Executed="DeleteCommand_Executed" />
</Control.CommandBindings>
</Window.CommandBindings>
<Button Content="X"
Command="{x:Static ApplicationCommands.Delete}"
CommandParameter="abc" />
</Window>
private void DeleteCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
var commandParameter = e.Parameter as string; // Returns "abc"
}
private void DeleteCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
var commandParameter = e.Parameter as string; // Returns "abc"
}
When clicking the Button or an ICommandSource in general, it sends the routed command along with the command parameter up the logical tree. The CommandBinding can catch this event and handle it using the registered handlers.
The point is that the command source must define the command parameter and not the CommandBinding. This way, the same CommandBinding can handle different command sources that send different command parameters.
Have you tried the CommandParameter property?
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="MyHeader"
Command="{Binding MyCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ContextMenu}}, Path=PlacementTarget.SelectedItem}" />
</DataGrid.ContextMenu>
I have a button in WPF window. I want to display a message by both pressing left mouseclick and by clicking Ctrl+F. I want most of the code in XAML. Code is as given below.
My problem is that mouse click is working for me but not the key press. Can anybody please help me. Thanks in advance.
MyWindow.xaml
<Window x:Class="Commands_Xaml.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="164,88,0,0" Name="button1" VerticalAlignment="Top" Width="75">
<Button.CommandBindings>
<CommandBinding Command="ApplicationCommands.Find" Executed="CommandBinding_Executed"/>
</Button.CommandBindings>
<Button.InputBindings>
<KeyBinding Key="F" Modifiers="Control" Command="ApplicationCommands.Find"/>
<MouseBinding MouseAction="LeftClick" Command="ApplicationCommands.Find"/>
</Button.InputBindings>
</Button>
</Grid>
</Window>
MainWindow.xaml.cs
namespace Commands_Xaml
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//ApplicationCommands.Find.InputGestures.Add(new KeyGesture(Key.F,ModifierKeys.Control));
//ApplicationCommands.Find.InputGestures.Add(new MouseGesture(MouseAction.LeftClick));
//CommandBinding bindingObject = new CommandBinding();
//bindingObject.Command = ApplicationCommands.Find;
//bindingObject.Executed += new ExecutedRoutedEventHandler(CommandBinding_Executed);
//this.CommandBindings.Add(bindingObject);
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Button clicked");
}
}
}
Move the command binding / input binding to the window level and use the Command property of the button instead of trying to use a MouseBinding.
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Find" Executed="CommandBinding_Executed" />
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding Command="ApplicationCommands.Find" Key="F" Modifiers="Control" />
</Window.InputBindings>
<Grid>
<Button Command="ApplicationCommands.Find" Content="Button" HorizontalAlignment="Left" Margin="206,152,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
</Window>
UPDATE
To have different behavior based on different button clicks, this can be accomplished a few ways. Here are two:
Use a different command for each button:
<Button Command="ApplicationCommands.Find" Content="Button1" />
<Button Command="ApplicationCommands.Print" Content="Button2" />
Use CommandParameter:
<Button Command="ApplicationCommands.Find" CommandParameter="Find1" Content="Button1" />
<Button Command="ApplicationCommands.Find" CommandParameter="Find2" Content="Button2" />
And for the KeyBinding:
<KeyBinding Command="ApplicationCommands.Find" CommandParameter="Find3" />
You can access this property in the CommandBinding.Executed handler:
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
if (e.Parameter as string == "Find1")
{
//Find1
}
else if (e.Parameter as string == "Find2")
{
//Find2
}
else if (e.Parameter as string == "Find3")
{
//Find3
}
}
Out of the box if you crank up Blend put a button on a grid and set a couple of ChangeProeprtyActions on it ... will fail with an error message.
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
x:Class="SL4_Button_States.MainPage"
Width="640" Height="480">
<Grid x:Name="LayoutRoot" Background="White">
<Button x:Name="button" Content="Button" Height="39" Margin="68,42,299,0" VerticalAlignment="Top">
<i:Interaction.Triggers>
<i:EventTrigger>
<ei:ChangePropertyAction PropertyName="Content" Value="Button is now loaded!" TargetObject="{Binding ElementName=button}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction PropertyName="Content" Value="temporary"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</Grid>
The error message is this:
Webpage error details
Message: Unhandled Error in Silverlight Application The type 'Object' was not found. [Line: 2 Position: 10] at MS.Internal.XcpImports.CreateFromXaml(String xamlString, Boolean createNamescope, Boolean requireDefaultNamespace, Boolean allowEventHandlers, Boolean expandTemplatesDuringParse)
at MS.Internal.XcpImports.CreateFromXaml(String xamlString, Boolean createNamescope, Boolean requireDefaultNamespace, Boolean allowEventHandlers)
at Microsoft.Expression.Interactivity.TypeConverterHelper.ExtendedStringConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
at Microsoft.Expression.Interactivity.Core.ChangePropertyAction.Invoke(Object parameter)
at System.Windows.Interactivity.TriggerBase.InvokeActions(Object parameter)
at System.Windows.Interactivity.EventTriggerBase.OnEvent(EventArgs eventArgs)
at System.Windows.Interactivity.EventTriggerBase.OnEventImpl(Object sender, EventArgs eventArgs)
at MS.Internal.CoreInvokeHandler.InvokeEventHandler(UInt32 typeIndex, Delegate handlerDelegate, Object sender, Object args)
at MS.Internal.JoltHelper.FireEvent(IntPtr unmanagedObj, IntPtr unmanagedObjArgs, Int32 argsTypeIndex, Int32 actualArgsTypeIndex, String eventName, UInt32 flags)
Line: 1
Char: 1
Code: 0
URI: http://localhost:62193/Silverlight.js
If you add a ChangePropertyAction on a TextBlock ... works fine:
<TextBlock Height="20" Margin="207,132,138,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<ei:ChangePropertyAction PropertyName="Text" Value="Mouse Left BUtton Down"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
If you have any idea why this happens please enlighten me ... if there is another/better way of doing this, other than states please let me know.
Thanks.
It is failing because the Content property on a Button is not just a string but an object. Even though Blend will let you set it I'm not sure that it is possible. To change the text content of the button you would need to do something like this:
<Button x:Name="button" HorizontalAlignment="Left" Height="23" Margin="154,98,0,0" VerticalAlignment="Top" Width="113">
<Button.Content>
<TextBlock x:Name="btnText" Text="Orginal" />
</Button.Content>
<i:Interaction.Triggers>
<i:EventTrigger>
<ei:ChangePropertyAction PropertyName="Text" Value="Loaded" TargetName="btnText"/>
</i:EventTrigger>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction PropertyName="Text" Value="Clicked" TargetName="btnText"/>
</i:EventTrigger>
</i:Interaction.Triggers>
How to change Button's Content via ChangePropertyAction in Blend?
All following steps are in Express Blend 4.
Add a Button in ArtBoard. Drag "ChangePropertyAction" from Assets to
the button.
In Data panel, create a new ContentControl data source (click "Create Object Data Source", search "ContentControl" and click OK. The datasource will be named as ContentControlDataSource).
In Object and Timeline, focus the ChangePropertyAction of button. In Property->Common Properties->PropertyName, select "Content", and create a data binding to the ContentControlDataSource with Value. (while setting Value of Content, click the small rectangle at the end of value. Choose 'Data Binding...", in the "Create Data Binding" dialog, select ContentControlDataSource in Data Field)
In Object and Timeline panel, unfocus then focus again the ChangePropertyAction of button. In Property->Common Properties->Value, you can see a yellow rectangle around the text like "(ContentControl) New". Click the small yellow rectangle at the end and select "Reset". After the yellow rectangle disappear, click the New button after Value property. When the "Select Object" dialog pops up, search "TextBlock" and add it. Then set the TextBlock's Text as you want. Build and test it now. Click the button and you can see the text is changed to what you set.
The XAML looks like:
<Button Content="Button" HorizontalAlignment="Left" Margin="125,51,0,0" VerticalAlignment="Top" Width="75">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction PropertyName="Content">
<ei:ChangePropertyAction.Value>
<TextBlock Text="HelloText"/>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Button x:Name="button" Content="Button" Height="39" Margin="68,42,299,0" Click="button_Click" MouseRightButtonDown="button_MouseRightButtonDown" VerticalAlignment="Top"
private void button_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
((Button)sender).Content = "Right Click";
}
private void button_Click(object sender, RoutedEventArgs e)
{
((Button)sender).Content = "Button Click";
}
This menuItem because it's linked to a command does the magic behind the scenes for me:
<MenuItem Name="mnuOpen" Command="Open"/>
where I have
<Window.CommandBindings>
<CommandBinding Command="Open"
Executed="CommandBinding_Open_Executed"
CanExecute="CommandBinding_ProjectSelected"/>
</Window.CommandBindings>
but every binding I've tried has failed to do anything.
<MenuItem Name="mnuExplorer" Click="mnuExplorer_Click" Header="Open Containing Folder" IsEnabled="{Binding ElementName=mnuOpen, Path=IsEnabled}" />
It works fine maybe you forgot to set CanExecute flag or have other dependency
full code
<Window x:Class="MenuBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding Command="Open"
Executed="CommandBinding_Executed"
CanExecute="CommandBinding_CanExecute"/>
</Window.CommandBindings>
<Grid>
<Menu>
<MenuItem Name="mnuOpen" Command="Open" IsEnabled="False" />
<MenuItem Name="mnuExplorer" Header="Open Containing Folder" IsEnabled="{Binding ElementName=mnuOpen, Path=IsEnabled}" />
</Menu>
</Grid>
and class
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Magic");
}
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true; //define if command can be executed
}
}
I intended to disable and enable the Buttons outside the TabControl, just like those inside the TabItem when the current tab is changed. But the CommandBindings of the TabItem do not seem to impact "up" the visual tree. What is the right way to do this?
With this XAML:
<Window x:Class="WpfApplication10.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication10"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Button Content="MyCommand1" Command="local:MainWindow.MyCommand1" />
<Button Content="MyCommand2" Command="local:MainWindow.MyCommand2" />
<TabControl>
<TabItem Header="tabItem1" Name="tabItem1">
<TabItem.CommandBindings>
<CommandBinding Command="local:MainWindow.MyCommand1"
Executed="ExecuteMyCommand" />
</TabItem.CommandBindings>
<StackPanel>
<Button Content="MyCommand1" Command="local:MainWindow.MyCommand1" />
<Button Content="MyCommand2" Command="local:MainWindow.MyCommand2" />
</StackPanel>
</TabItem>
<TabItem Header="tabItem2" Name="tabItem2">
<TabItem.CommandBindings>
<CommandBinding Command="local:MainWindow.MyCommand2"
Executed="ExecuteMyCommand"/>
</TabItem.CommandBindings>
<StackPanel>
<Button Content="MyCommand1" Command="local:MainWindow.MyCommand1" />
<Button Content="MyCommand2" Command="local:MainWindow.MyCommand2" />
</StackPanel>
</TabItem>
</TabControl>
</StackPanel>
</Window>
With this Code Behind:
public static readonly RoutedUICommand MyCommand1 = new RoutedUICommand();
public static readonly RoutedUICommand MyCommand2 = new RoutedUICommand();
public MainWindow()
{
InitializeComponent();
}
private void ExecuteMyCommand(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Hello");
}
MSFT gave me the correct answer in their WPF forum, here (http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/bb3d1eb1-96fa-4fbc-beda-799613acb9f7)
<StackPanel>
<StackPanel FocusManager.IsFocusScope="True">
<Button Content="MyCommand1" Command="local:Window8.MyCommand1" />
<Button Content="MyCommand2" Command="local:Window8.MyCommand2" />
</StackPanel>
<TabControl>
<TabItem Header="tabItem1" Name="tabItem1">
<TabItem.CommandBindings>
<CommandBinding Command="local:Window8.MyCommand1" Executed="ExecuteMyCommand" />
</TabItem.CommandBindings>
<StackPanel>
<Button Content="MyCommand1" Command="local:Window8.MyCommand1" />
<Button Content="MyCommand2" Command="local:Window8.MyCommand2" />
</StackPanel>
</TabItem>
<TabItem Header="tabItem2" Name="tabItem2">
<TabItem.CommandBindings>
<CommandBinding Command="local:Window8.MyCommand2" Executed="ExecuteMyCommand"/>
</TabItem.CommandBindings>
<StackPanel>
<Button Content="MyCommand1" Command="local:Window8.MyCommand1" />
<Button Content="MyCommand2" Command="local:Window8.MyCommand2" />
</StackPanel>
</TabItem>
</TabControl>
</StackPanel>
Your don't have any code that could disable buttons. You can do it in several ways:
1. Define CanExecute event handler.
<CommandBinding Command="local:MainWindow.MyCommand1"
Executed="ExecuteMyCommand"
CanExecute="MyCommand_CanExecute"/>
Code behind:
private void MyCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = tabItem1.IsSelected;
}
2. Bind button IsEnabled property to tab item IsSelected property
<Button IsEnabled="{Binding ElementName=tabItem1, Path=IsSelected}"
Content="MyCommand1" Command="local:MainWindow.MyCommand1" />
Is this all the code?
You have a special CanExecute defined, that disables the MyCommandsX ?
Or you have a binding on the Enabled Property of the bound buttons, and you implement INotifyPropertyChanged or something?
Or why should they be enabled/disabled in your code sample?
I you ask me, I wouldn't expect the code to disable the buttons..
Update 1:
You could enable the buttons the same way you did it, by adding the command bindings in the surrounding stackpanel for example.
<StackPanel>
<StackPanel.CommandBindings>
<CommandBinding Command="local:MainWindow.MyCommand1"
Executed="ExecuteMyCommand" />
</StackPanel.CommandBindings>
<Button Content="MyCommand1" Command="local:MainWindow.MyCommand1" />
<Button Content="MyCommand2" Command="local:MainWindow.MyCommand2" />
<TabControl>
You can use the CanExecute part of the command binding to verify the conditions under which the bound buttons enabled.
Instead you should handle the command itself I think.