I am creating a command which will have a Textbox control as target.
Code to create the command:
public class Commands
{
public static RoutedCommand Appender;
static Commands()
{
Appender = new RoutedCommand();
}
public static void AppenderExecuted(object target, ExecutedRoutedEventArgs e)
{
System.Windows.Controls.TextBox targetTbox = target as System.Windows.Controls.TextBox;
if (targetTbox != null)
{
targetTbox.Text += "AppendedText";
}
}
}
XAML:
<StackPanel Name="span" FocusManager.IsFocusScope="True">
<Menu IsMainMenu="True">
<MenuItem Header="Tools">
<MenuItem Header="_Append" Name="menuAppend" />
</MenuItem>
</Menu>
<TextBox Height="100" Name="txtEdit"></TextBox>
</StackPanel>
CS: Window constructor:
//create bindings
CommandBinding bindingTM = new CommandBinding(Commands.Appender, Commands.AppenderExecuted);
//[THIS DOESN'T WORK]
this.CommandBindings.Add(bindingTM);
//[THIS WORKS]
txtEdit.CommandBindings.Add(bindingTM);
//associate command
menuAppend.Command = Commands.Appender;
I would like to be able to use the Appender command on any TextBox on the Window, without the need to add the command binding to each TextBox.
-> Why doesn't adding the command binding to Window doesn't work?
-> Any solutions?
Try:
public static void AppenderExecuted(object target, ExecutedRoutedEventArgs e) {
System.Windows.Controls.TextBox targetTbox = e.OriginalSource as System.Windows.Controls.TextBox;
if (targetTbox != null) {
targetTbox.Text += "AppendedText";
}
}
Related
I have a problem with my XAML. I have a Menu Component, and I would like that it would work in Shortcut key too. I have XAML code, which doesn't work:
<MenuItem Header="_New" Name="New" Click="New_Click" InputGestureText="Ctrl+N">
<MenuItem.InputBindings>
<KeyBinding Key="N" Modifiers="control"/>
</MenuItem.InputBindings>
</MenuItem>
What is the solution? New_Click event works, but Shortcut key doesn't...
Using InputGestureText is only going to add text to the menu item per the documentation. You need to specify what needs to happen when the shortcut is actually performed. To do that you need to create an ICommand in your ViewModel, preferably, then bind that command to the MenuItem.Command
So your resulting code should look like this:
<MenuItem Header="_New" Name="New" Command="{Binding NewCommand}" InputGestureText="Ctrl+N">
assuming you have a public ICommand NewCommand {...} in your view model.
EDIT
Doing this requires a command because that's how WPF works. WPF != WinForms, where in WinForms you would use events and in WPF you want to try to use ICommand bindings. This is proven, and answers your question on why Command is required: it is because InputBinding implements the Command design pattern, so you're not going to really get a way to work around it.
So there isn't really a way to work around using the Click event handler instead of a Command with input gestures. If you're not in the position to use a Command as they are intended to be use (like in MVVM), then you will have to add an ICommand in code-behind, then programmatically set up the binding.
private RelayCommand qatRemoveItemCommand;
public ICommand RemoveItemCommand
{
get
{
if (this.RemoveItemCommand == null)
{
this.RemoveItemCommand = new RelayCommand(param => this.RemoveItem(), param => CanRemoveItem);
}
return this.RemoveItemCommand;
}
}
private void RemoveItem()
{
this.DeleteItem();
}
private bool CanRemoveItem
{
get
{
return true;
}
}
KeyBinding RemoveItemCmdKeyBinding = new KeyBinding(
this.RemoveItemCommand,
Key.N,
ModifierKeys.Control);
New.InputBindings.Add(OpenCmdKeyBinding);
<MenuItem Header="_New" Name="New" InputGestureText="Ctrl+N">
Note it may be required that you Remove or clear the InputBindings when the control is unloaded, but I think this will be as close as you can get, not to mention my original answer answer's your question; your request for additional information is a separate question in itself.
Also, do some research on the classes that implement inherit from InputBinding, KeyBinding and MouseBinding
It is possible to apply shortcut on click.We need to use command binding to achieve this functionality through XAML.
First of all,you need to bind command to the MenuItem and then bind the same command to keybinding.
Following is the working code for that:
<MenuItem Header="_New" Name="New" Command="{Binding NewCommand, Mode=TwoWay}" InputGestureText="Ctrl+N">
<MenuItem.InputBindings>
<KeyBinding Key="N" Modifiers="control" Command="{Binding NewCommand, Mode=TwoWay}"/>
</MenuItem.InputBindings>
</MenuItem>
NewCommand will be of Icommand type in your ViewModel.
I am providing C# code for your understanding of command binding:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyData();
}
}
public class MyData
{
public MyData()
{
this.NewCommand = new DelegateCommand(ExecuteShowMessage);
}
private void ExecuteShowMessage(object obj)
{
MessageBox.Show("Test");
}
public ICommand NewCommand { get; set; }
}
Above code will work as per your requirement.Please Let me know if you have any queries regarding this.
Thank You
Here's my utility class for that:
using System;
using System.Windows;
using System.Windows.Input;
namespace NNN
{
/// <summary>This utility class translates ICommand calls to RoutedEventHandler calls</summary>
class c2e : ICommand
{
readonly RoutedEventHandler eh;
public c2e( RoutedEventHandler eh )
{
this.eh = eh;
}
public event EventHandler CanExecuteChanged;
bool ICommand.CanExecute( object parameter )
{
return true;
}
void ICommand.Execute( object parameter )
{
var a = new RoutedEventArgs();
this.eh( this, a );
}
}
static class Hotkey
{
/// <summary>Register event handler for hotkey</summary>
public static void registerHotkey( this Window wnd, Key key, ModifierKeys modifier, RoutedEventHandler handler )
{
ICommand cmd = new c2e( handler );
InputBinding ib = new InputBinding( cmd,new KeyGesture( key, modifier ) );
wnd.InputBindings.Add( ib );
}
}
}
Usage example:
public MainWindow()
{
InitializeComponent();
this.registerHotkey( Key.O, ModifierKeys.Control, menuOpen );
}
As non of the above solutions worked for me, I would like to suggest another solution here, by using RoutedCommand.
XAML
<Window x:Class="CH02.ContextMenuDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CH02.ContextMenuDemo"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.CommandBindings>
<CommandBinding Command="{x:Static local:MainWindow.MenuItemClickCommand}"
CanExecute="CanExecute"
Executed="OnMenuItemClicked"/>
</Window.CommandBindings>
<Grid>
<TextBlock Text="Right Click here to open Context Menu!"
VerticalAlignment="Center"
HorizontalAlignment="Center"
TextWrapping="Wrap">
<TextBlock.ContextMenu>
<ContextMenu>
<MenuItem Header="My Menu Item"
Name="MyMenuItem"
Command="{x:Static local:MainWindow.MenuItemClickCommand}">
</MenuItem>
<Separator />
<MenuItem Header="Another Menu Item"
IsCheckable="True"
IsChecked="True"/>
</ContextMenu>
</TextBlock.ContextMenu>
</TextBlock>
</Grid>
</Window>
CS
public partial class MainWindow : Window
{
private static ICommand _clickCommand;
public static ICommand
MenuItemClickCommand => _clickCommand ??
(_clickCommand = new RoutedUICommand(
text: "Options",
name: "MenuItemClickCommand",
ownerType: typeof(MainWindow),
inputGestures: new InputGestureCollection(
inputGestures: new InputGesture[] {
new KeyGesture(Key.N, ModifierKeys.Control)
})));
public MainWindow()
{
InitializeComponent();
Focus();
}
private void OnMenuItemClicked(object sender, RoutedEventArgs e)
{
MessageBox.Show("Context menu item clicked!");
}
private void CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true; // or other logic
}
}
I have to update a ListView item by clicking on a button. How do I find and update one at the runtime?
update: I mean I have to find the certain ListView item and update the text of this item only.
When ListViewItems were added to the ListView manually, you can look them up by their content and replace with new content like this (using System.Linq):
object contentToReplace = ...;
object newContent = ...;
ListViewItem item = listView.Items.Cast<ListViewItem>().FirstOrDefault(
lvi => lvi.Content == contentToReplace);
if (item != null)
{
item.Content = newContent;
}
You may use commands. For example:
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public static readonly ICommand ItemClickCommand = new RoutedCommand("ItemClick", typeof(MainWindow));
public MainWindow()
{
InitializeComponent();
this.CommandBindings.Add(
new CommandBinding(
MainWindow.ItemClickCommand,
this.ExecuteItemClickCommand,
this.CanExecuteItemClickCommand));
}
private void CanExecuteItemClickCommand(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = e.Parameter is ListBoxItem;
}
private void ExecuteItemClickCommand(object sender, ExecutedRoutedEventArgs e)
{
// Here you can access ListBoxItem that holds a clicked button.
ListBoxItem listBoxItem = (ListBoxItem)e.Parameter;
listBoxItem.Content = "...";
}
}
}
Now, the only thing you need is to assign ItemClickCommand to a button and bind CommandParameter to corresponding ListBoxItem.
XAML example:
<Window ...
xmlns:local="clr-namespace:WpfApplication1">
<ListBox>
<ListBoxItem>
<ListBoxItem.Content>
<Button Command="{x:Static local:MainWindow.ItemClickCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}}"
Content="Click Me"/>
</ListBoxItem.Content>
<...>
I'm using MVVM.
<ItemsControl ItemsSource="{Binding AllIcons}" Tag="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label HorizontalAlignment="Right">x</Label>
<Image Source="{Binding Source}" Height="100" Width="100" />
<Label HorizontalAlignment="Center" Content="{Binding Title}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
That looks fine. If I put a button in the stack panel using this command:
<Button Command="{Binding Path=DataContext.InvasionCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}}" CommandParameter="{Binding}"/>
I'm able to capture the command. However, I want to execute the command binding when the mouse enters the stack panel, not when I click a button.
Any idea?
My wrong, input bindings does not solve the problem. You may use attached properties for this:
public static class MouseEnterCommandBinding
{
public static readonly DependencyProperty MouseEnterCommandProperty = DependencyProperty.RegisterAttached(
"MouseEnterCommand",
typeof(ICommand),
typeof(MouseEnterCommandBinding),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender)
);
public static void SetMouseEnterCommand(UIElement element, ICommand value)
{
element.SetValue(MouseEnterCommandProperty, value);
element.MouseEnter += (s,e) =>
{
var uiElement = s as UIElement;
var command = GetMouseEnterCommand(uiElement);
if (command != null && command.CanExecute(uiElement.CommandParameter))
command.Execute(uiElement.CommandParameter);
}
}
public static ICommand GetMouseEnterCommand(UIElement element)
{
return element.GetValue(MouseEnterCommandProperty) as ICommand;
}
}
First you need to declare a behavior for mouse enter. This basically translates the event into a command in your ViewModel.
public static class MouseEnterBehavior
{
public static readonly DependencyProperty MouseEnterProperty =
DependencyProperty.RegisterAttached("MouseEnter",
typeof(ICommand),
typeof(MouseEnterBehavior),
new PropertyMetadata(null, MouseEnterChanged));
public static ICommand GetMouseEnter(DependencyObject obj)
{
return (ICommand)obj.GetValue(MouseEnterProperty);
}
public static void SetMouseEnter(DependencyObject obj, ICommand value)
{
obj.SetValue(MouseEnterProperty, value);
}
private static void MouseEnterChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
UIElement uiElement = obj as UIElement;
if (uiElement != null)
uiElement.MouseEnter += new MouseEventHandler(uiElement_MouseEnter);
}
static void uiElement_MouseEnter(object sender, MouseEventArgs e)
{
UIElement uiElement = sender as UIElement;
if (uiElement != null)
{
ICommand command = GetMouseEnter(uiElement);
command.Execute(uiElement);
}
}
}
Then you just need to create that command in your view model and reference it in the view. The behaviors: namespace should just point to wherever you created that behavior. I use this pattern every time I need to translate an event into a command in a view model.
<Grid>
<StackPanel behaviors:MouseEnterBehavior.MouseEnter="{Binding MouseEnteredCommand}"
Height="150"
Width="150"
Background="Red">
</StackPanel>
</Grid>
You probably need to use InputBindings: http://msdn.microsoft.com/en-us/library/system.windows.input.inputbinding.aspx
I want to create a Style for a WPF ListBox that includes a Button in the ControlTemplate that the user can click on and it clears the ListBox selection.
I dont want to use codebehind so that this Style can be applied to any ListBox.
I have tried using EventTriggers and Storyboards and it has proved problematic as it only works first time and stopping the Storyboard sets the previous selection back.
I know I could use a user control but I want to know if it is possible to achieve this using only a Style.
It is not possible to achieve this using XAML and only the classes provided by the .NET framework. However you can still produce a reusable solution by defining a new command (call it ClearSelectionCommand) and a new attached property (call it ClearSelectionOnCommand).
Then you can incorporate those elements into your style.
Example:
public class SelectorBehavior
{
public static RoutedCommand
ClearSelectionCommand =
new RoutedCommand(
"ClearSelectionCommand",
typeof(SelectorBehavior));
public static bool GetClearSelectionOnCommand(DependencyObject obj)
{
return (bool)obj.GetValue(ClearSelectionOnCommandProperty);
}
public static void SetClearSelectionOnCommand(
DependencyObject obj,
bool value)
{
obj.SetValue(ClearSelectionOnCommandProperty, value);
}
public static readonly DependencyProperty ClearSelectionOnCommandProperty =
DependencyProperty.RegisterAttached(
"ClearSelectionOnCommand",
typeof(bool),
typeof(SelectorBehavior),
new UIPropertyMetadata(false, OnClearSelectionOnCommandChanged));
public static void OnClearSelectionOnCommandChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
Selector selector = d as Selector;
if (selector == null) return;
bool nv = (bool)e.NewValue, ov = (bool)e.OldValue;
if (nv == ov) return;
if (nv)
{
selector.CommandBindings.Add(
new CommandBinding(
ClearSelectionCommand,
ClearSelectionCommand_Executed,
ClearSelectionCommand_CanExecute));
}
else
{
var cmd = selector
.CommandBindings
.Cast<CommandBinding>()
.SingleOrDefault(x =>
x.Command == ClearSelectionCommand);
if (cmd != null)
selector.CommandBindings.Remove(cmd);
}
}
public static void ClearSelectionCommand_Executed(
object sender,
ExecutedRoutedEventArgs e)
{
Selector selector = (Selector)sender;
selector.SelectedIndex = -1;
}
public static void ClearSelectionCommand_CanExecute(
object sender,
CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
}
Example usage - XAML:
<Window x:Class="ClearSelectionBehaviorLibrary.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClearSelectionBehaviorLibrary"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Style x:Key="MyStyle" TargetType="Selector">
<Setter
Property="local:SelectorBehavior.ClearSelectionOnCommand"
Value="True"/>
</Style>
</Window.Resources>
<Grid>
<DockPanel>
<Button
DockPanel.Dock="Bottom"
Content="Clear"
Command="{x:Static local:SelectorBehavior.ClearSelectionCommand}"
CommandTarget="{Binding ElementName=TheListBox}"/>
<ListBox
Name="TheListBox"
ItemsSource="{Binding MyData}"
Style="{StaticResource MyStyle}"/>
</DockPanel>
</Grid>
</Window>
Example usage - Code Behind:
public partial class Window1 : Window
{
public List<string> MyData { get; set; }
public Window1()
{
MyData = new List<string>
{
"aa","bb","cc","dd","ee"
};
InitializeComponent();
DataContext = this;
}
}
I have a RoutedUICommand called Comment Selection. I need to add an input gesture for this command as it is in VIsual Studio, ie. (Ctrl+K, Ctrl+C).
How can I do this? Plz help me. (Keep VS functionality in mind).
Regards, Jawahar
This code is made for "Ctrl+W, Ctrl+E" and/or "Ctrl+W, E" combinations, however you can parametrize it for any key combinations:
XAML:
<MenuItem Header="Header" InputGestureText="Ctrl+W, E" Command="ShowCommand"/>
C#:
public static readonly RoutedUICommand ShowCommand = new RoutedUICommand(
"Show command text",
"Show command desc",
typeof(ThisWindow),
new InputGestureCollection(new[] { new ShowCommandGesture (Key.E) }));
public class ShowCommandGesture : InputGesture
{
private readonly Key _key;
private bool _gotFirstGesture;
private readonly InputGesture _ctrlWGesture = new KeyGesture(Key.W, ModifierKeys.Control);
public ShowCommandGesture(Key key)
{
_key = key;
}
public override bool Matches(object obj, InputEventArgs inputEventArgs)
{
KeyEventArgs keyArgs = inputEventArgs as KeyEventArgs;
if (keyArgs == null || keyArgs.IsRepeat)
return false;
if (_gotFirstGesture)
{
_gotFirstGesture = false;
if (keyArgs.Key == _key)
{
inputEventArgs.Handled = true;
}
return keyArgs.Key == _key;
}
else
{
_gotFirstGesture = _ctrlWGesture.Matches(null, inputEventArgs);
if (_gotFirstGesture)
{
inputEventArgs.Handled = true;
}
return false;
}
}
}
I've found this blog post which I think could be of help
http://kent-boogaart.com/blog/multikeygesture
Basically, WPF has no built in support for it, but subclassing InputGesture or KeyGesture seems like a possible way to achieve this without too much hassle.
Here's how I cobbled together something that actually works. I just wish I could credit the person or persons who paved the way to my Path of Enlightenment.
Let's say your application is called Heckler. Add a namespace tag for your application to the Window object:
<Window ...
xmlns:w="clr-namespace:Heckler"
...>
Now add a CommandBindings property tag and start your collection of CommandBinding objects. Here we add custom command Comment Selection:
<Window.CommandBindings>
<CommandBinding
Command="w:CustomCommands.CommentSelection"
CanExecute="CommentSelectionCanExecute"
Executed="CommentSelectionExecuted" />
</Window.CommandBindings>
Add a MenuItem to a main Menu's MenuItem:
<Menu
IsMainMenu="True">
<MenuItem
Header="_File">
<MenuItem
Command="w:CustomCommands.CommentSelection">
</MenuItem>
</MenuItem>
</Menu>
...
</Window>
In the Window code-behind, add your CustomCommands class and custom command:
public static class CustomCommands
{
// Ctrl+Shift+C to avoid collision with Ctrl+C.
public static readonly RoutedUICommand CommentSelection =
new RoutedUICommand("_Comment Selection",
"CommentSelection", typeof(MainWindow),
new InputGestureCollection()
{ new KeyGesture(Key.C, (ModifierKeys.Control | ModifierKeys.Shift)) });
}
Now wire up your event handlers:
private void CommentSelectionCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
// Determines status of command.
e.CanExecute = true;
}
private void CommentSelectionExecuted(object sender, ExecutedRoutedEventArgs e)
{
// TO-DO: Insert magic here.
}
You should be good to go. I hope this helps and I didn't miss anything!
<KeyBinding Command="{Binding ExitCommand}"
Key="{Binding ExitCommand.GestureKey}"
Modifiers="{Binding ExitCommand.GestureModifier}"/>
get
{
if (exitCommand == null)
{
exitCommand = new DelegateCommand(Exit);
exitCommand.GestureKey = Key.X;
exitCommand.GestureModifier = ModifierKeys.Control;
exitCommand.MouseGesture = MouseAction.LeftDoubleClick;
}
return exitCommand;
}
}
private void Exit()
{
Application.Current.Shutdown();
}