How to make an invisible button to run a command - wpf

I have a form with TextBox and two Buttons. One button has IsDefault property set to true, and IsCancel set to true for other button. TextBox is CommandTarget for both buttons. When I'm pressing Enter or ESC keys on TextBox, it works as I'm pressing on corresponding button.
I want to remove buttons from the form. They should not be visible, but the textbox should react on Enter or ESC as before. I cannot just set button's Visible property to collapsed - in this case they does not work at all. And I prefer to avoid of tracking keyboard events.
Is it possible?

While Skeets' and Abe's methods work, they are hacks. You can simply specify that a WPF command should also be invoked by a so called InputGesture, in this case a KeyGesture ("enter", or "escape"). You can set the scope of this KeyGestures by placing the CommandBinding for the command at the appropriate level in the Visual Tree. Like this:
<Window x:Class="CommandSpike.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CommandSpike"
Title="Window1" Height="300" Width="300">
<StackPanel>
<Grid>
<Grid.CommandBindings>
<CommandBinding x:Name="EnterBinding"
Command="{x:Static local:Commands.EnterCommand}"
CanExecute="CommandBinding_CanExecute"
Executed="EnterBinding_Executed"/>
<CommandBinding x:Name="CancelBinding"
Command="{x:Static local:Commands.CancelCommand}"
CanExecute="CommandBinding_CanExecute"
Executed="CancelBinding_Executed"/>
</Grid.CommandBindings>
<TextBox>
Press Enter or Cancel when I have focus...
</TextBox>
</Grid>
<TextBox Margin="0,4">
Pressing Enter or Cancel does nothing while I have focus!
</TextBox>
</StackPanel>
</Window>
using System.Windows;
using System.Windows.Input;
namespace CommandSpike
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
private void EnterBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Enter");
}
private void CancelBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Cancel");
}
}
}
using System.Windows.Input;
namespace CommandSpike
{
public static class Commands
{
public static RoutedUICommand EnterCommand { get;private set; }
public static RoutedUICommand CancelCommand { get; private set; }
static Commands()
{
EnterCommand=new RoutedUICommand("Enter",
"EnterCommand",
typeof(Commands));
EnterCommand.InputGestures.Add(
new KeyGesture(Key.Enter)
);
CancelCommand=new RoutedUICommand("Cancel",
"CancelCommand",
typeof(Commands));
CancelCommand.InputGestures.Add(
new KeyGesture(Key.Escape)
);
}
}
}

Have you tried other mechanisms to make the buttons invisible? Here are some suggestions:
Set Opacity to 0
Set Width/Height to 0
Set a RenderTransform that moves the buttons off-screen

I would give them an empty ControlTemplate:
<ControlTemplate x:Key="blankButton" TargetType="{x:Type Button}" />
...
<Button IsDefault="True" ... Template="{StaticResource blankButton}" />
<Button IsCancel="True" ... Template="{StaticResource blankButton}" />

Related

WPF weird window switching on double-click

Does anybody know how to implement a double-click event handler that opens a new window in a way the new window becomes the front most window? (Just the behavior that is normally expected).
In WPF there is a strange behavior of windows when opening a second window in the double-click event handler. The second window opens but the first window, where the double-click-event was fired, becomes activated again immediately.
Opening a window in a click event handler, works as expected. The second window opens and remains the front window.
For demonstration purposes I created the following application. Two window classes with just a button control. To distinguish between click and double-click on the button control, the click-event works only if the left shift key is pressed.
After double-click
http://blog.mutter.ch/wp-content/uploads/2014/05/wpf_window1.png
After click (this is also the expected behavior for double-click)
http://blog.mutter.ch/wp-content/uploads/2014/05/wpf_window2.png
Main Window
<Window x:Class="WpfWindowSwitching.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="200" Width="600">
<Grid>
<Button Margin="40"
HorizontalAlignment="Center"
VerticalAlignment="Center"
MouseDoubleClick="doubleClick"
Click="click">
<TextBlock FontWeight="Bold"
FontSize="22">
I am the first Window, double click this button...
</TextBlock>
</Button>
</Grid>
</Window>
The code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void doubleClick(object sender, MouseButtonEventArgs e)
{
openNewWindow();
}
private static void openNewWindow()
{
var window = new SecondWindow();
window.Show();
}
private void click(object sender, RoutedEventArgs e)
{
if (!Keyboard.IsKeyDown(Key.LeftShift)) return;
openNewWindow();
}
}
Second Window
<Window x:Class="WpfWindowSwitching.SecondWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SecondWindow" Height="200" Width="600">
<Grid>
<Button Margin="40"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Click="click">
<TextBlock FontWeight="Bold"
FontSize="22">
I am the second Window
</TextBlock>
</Button>
</Grid>
</Window>
The code behind:
public partial class SecondWindow : Window
{
public SecondWindow()
{
InitializeComponent();
}
private void click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
After MouseDoubleClick event, MouseUp event is raised which gets handled on MainWindow. Hence secondary window gets activated momentarily and with subsequent event bubbling, main window gets activated.
In case you don't want that, you can explicitly stop event bubbling by setting e.Handled to True after mouse double click event. This way secondary window will remain activated.
private void doubleClick(object sender, MouseButtonEventArgs e)
{
openNewWindow();
e.Handled = true;
}

Implement short cut keys in Wpf application

I am new to wpf application and I am working on application and i have created a menu Now i want to function menu items event on short key ctrl+o, ctrl+n etc. How can i do it.please give me in details.
You can so it in the following way....
In Xaml file
<Window x:Class="FocusDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:FocusDemo"
Title="Window1" Height="300" Width="300">
<Window.CommandBindings>
<CommandBinding
Command=
"{x:Static
local:Window1.CustomRoutedCommand}"
Executed="ExecutedCustomCommand"
CanExecute="CanExecuteCustomCommand" >
</CommandBinding>
</Window.CommandBindings>
<Window.InputBindings>
<KeyBinding
Command=
"{x:Static
local:Window1.CustomRoutedCommand}"
Key="S"
Modifiers="Control"/>
</Window.InputBindings>
<Grid>
<!--Your Controls-->
</Grid>
</Window>
In the Code behind file
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public static RoutedCommand CustomRoutedCommand = new RoutedCommand();
public Window1()
{
InitializeComponent();
}
#region
public void ExecutedCustomCommand(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Ctrl+S");
}
public void CanExecuteCustomCommand(object sender,
CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
}
#endregion
}
Source : Click here
Please dont forget to mark the answer if its correct
I know it is not exact answer to the question, but probably anybody like me was searching for the way to put ANY keyboard shortcuts to menu items (command buttons) like Alt+O, Alt+N. In this case, you can just put undescore character (_) before the shortcut character in the item name.

WPF: using Commands bound in a UserControl

I'm doing a sample with MVVM and have a problem with commands. I have an Article class (with ID, Name, Price, etc.), an ArticleViewModel that represents the view model, and a user control (ArticleControl) that allows to input the data for the article, with bindings to the properties of the ArticleViewModel. This user control has a biding for a save command.
<UserControl.CommandBindings>
<CommandBinding x:Name="saveCmd"
Command="local:Commands.Save"
CanExecute="CommandBinding_CanExecute"
Executed="CommandBinding_Executed"/>
</UserControl.CommandBindings>
This is how the command is defined:
public class Commands
{
private static RoutedUICommand _save;
public static RoutedUICommand Save
{
get { return _save; }
}
static Commands()
{
InputGestureCollection saveInputs = new InputGestureCollection();
saveInputs.Add(new KeyGesture(Key.S, ModifierKeys.Control, "Ctrl+S"));
_save = new RoutedUICommand(
"Save",
"Save",
typeof(Commands),
saveInputs);
}
}
And the command binding handlers:
private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
double baseprice = 0;
double.TryParse(ArticleBasePrice.Text, out baseprice);
e.CanExecute =
!string.IsNullOrEmpty(ArticleID.Text) &&
!string.IsNullOrEmpty(ArticleName.Text) &&
!string.IsNullOrEmpty(ArticleDescription.Text) &&
baseprice > 0;
}
private void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
ArticleViewModel avm = (ArticleViewModel)DataContext;
if (avm != null && avm.Save())
{
ArticleID.Text = String.Empty;
ArticleName.Text = String.Empty;
ArticleDescription.Text = String.Empty;
ArticleBasePrice.Text = String.Empty;
}
}
Now, I put this user control on a window. When I hit Ctrl+S the command is executed. However, I also put a Save button on that window, next to this user control. When I click it I want to execute the same command (and I don't want to do another command binding in the window where the user control is hosted).
<StackPanel>
<local:ArticleControl x:Name="articleControl" />
<Button Name="btnSave"
Content="Save" Width="100"
HorizontalAlignment="Left"
Command="{???}"/> <!-- what should I put here? -->
</StackPanel>
But I do not know how to refer that saveCmd defined in the user control. I tried different things, some are completely wrong (they throw exception when running the app), some don't have any effect.
Command="{StaticResource saveCmd}"
Command="{StaticResource local:ArticleControl.saveCmd}"
Command="{x:Static local:Commands.Save}"
Any help is appreciated. Thank you.
The reason why the Save button will not cause the commandbindings of your other control to execute is because the Save button is outside the user control and therefore the command system will not look for a commandbinding in that control. The Command execution strategy is a bit like a bubbling event and will start from the focused item (the Button) and go up the visual tree until it finds the CommandBindings.
You can either implement the command binding in the parent control or set the CommandTarget property of the Save button to the user control.
Another approach is to set the FocusManager.IsFocusScope=True on the button or the container of the button. If you do this I suggest you read up on what IsFocusScope does but in a nutshell it will leave the input focus on whatever control has the focus when you press the button, instead of making the button the new input focus. This is generally used for toolbars or menu like structures.
Based on Patrick's suggestions, this is what I did:
Put the command binding in the user control and implemented the handlers in the code-behind as shown in the original message.
Used Command, CommandTarget and FocusManager properties on the button to point to the binding from the user control (ArticleUserControl is the x:Name of the user control).
This is how the XAML for the window looks:
<Window x:Class="MVVMModel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMModel"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<local:ArticleControl x:Name="articleControl" />
<Button Name="btnSave" Content="Save" Width="100" HorizontalAlignment="Left"
Command="local:Commands.Save"
CommandTarget="{Binding ElementName=ArticleUserControl}"
FocusManager.IsFocusScope="True" />
</StackPanel>
</Window>
I think you just have to move your CommandBinding to a Resource Dictionary, so that it's available outside your UserControl!
Here is what I did to work, though I'm not particularly happy with the solution. If anyone knows a better approach, please do let me know.
I moved the logic for the commands handler in a separate, static class:
static class CommandsCore
{
public static bool Save_CanExecute(ArticleControl ac)
{
double baseprice = 0;
double.TryParse(ac.ArticleBasePrice.Text, out baseprice);
return
!string.IsNullOrEmpty(ac.ArticleID.Text) &&
!string.IsNullOrEmpty(ac.ArticleName.Text) &&
!string.IsNullOrEmpty(ac.ArticleDescription.Text) &&
baseprice > 0;
}
public static void Save_Executed(ArticleControl ac)
{
ArticleViewModel avm = (ArticleViewModel)ac.DataContext;
if (avm != null && avm.Save())
{
ac.ArticleID.Text = String.Empty;
ac.ArticleName.Text = String.Empty;
ac.ArticleDescription.Text = String.Empty;
ac.ArticleBasePrice.Text = String.Empty;
}
}
}
I kept the command binding in the user control as it was
<UserControl.CommandBindings>
<CommandBinding x:Name="saveCmd"
Command="local:Commands.Save"
CanExecute="CommandBinding_CanExecute"
Executed="CommandBinding_Executed"/>
</UserControl.CommandBindings>
But in the handlers I called the two methods I just defined above.
public void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = CommandsCore.Save_CanExecute(this);
}
public void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
CommandsCore.Save_Executed(this);
}
And then I did the same from the window where the control is used.
<Window x:Class="MVVMModel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MVVMModel"
Title="MainWindow" Height="350" Width="525">
<Window.CommandBindings>
<CommandBinding x:Name="saveCmd"
Command="local:Commands.Save"
CanExecute="CommandBinding_CanExecute"
Executed="CommandBinding_Executed"/>
</Window.CommandBindings>
<StackPanel>
<local:ArticleControl x:Name="articleControl" />
<Button Name="btnSave" Content="Save" Width="100" HorizontalAlignment="Left"
Command="local:Commands.Save"/>
</StackPanel>
</Window>
and the handlers
public void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = CommandsCore.Save_CanExecute(articleControl);
}
public void CommandBinding_Executed(object sender, ExecutedRoutedEventArgs e)
{
CommandsCore.Save_Executed(articleControl);
}
And this works, the Save button is enabled only when the fields are filled in appropriately and the command is executed correctly when clicking the button.

WPF: selected ListView items after showing a dialog window

I have an action Edit in my WPF application, which is bound to items in a ListView control, i.e. it is executed when an item is double clicked or the Edit button in the toolbar is clicked. This action in turn displays a modal window with the editing stuff.
Now when I select multiple items in the list, click Edit, the items stay selected in the background, also, when I close the dialog, they are still selected in the sence that their background is blue. However, they seem to be not selected in the sence that the Edit button is disabled in the toolbar (the Edit action's CanExecute method simply checks FileList.SelectedIndex != -1. What's more, the "selected" items won't get deselected when I click some other list item - they only get deselected when I explicitly click on them one by one - it's as if the blue background is stuck on them.
My code does not use any fancy ListView styles or what not, so what could be causing this ?
I can post my code upon request, but it is pretty much standard.
EDIT:
After cutting down my code I finally found what's causing this issue. After showing the dialog, I edit the items in the data bound collection, so that the ListView would get updated (i.e. replace the bound objects to new objects). The question is, why is this causing a problem and how should I resolve it ?
Something in your code must be causing this issues. Below is a sample which behaves as expected.
XAML:
<Window x:Class="TestDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<RoutedUICommand x:Key="EditItemsCommand" Text="Edit Items" />
</Window.Resources>
<Window.CommandBindings>
<CommandBinding
Command="{StaticResource EditItemsCommand}"
CanExecute="EditItems_CanExecute"
Executed="EditItems_Executed" />
</Window.CommandBindings>
<StackPanel>
<Button Name="_editButton" Content="Edit" Command="{StaticResource EditItemsCommand}" />
<Button Content="Unselect all" Click="OnUnselectAll" />
<ListView
Name="_listView"
ItemsSource="{Binding Path=Items}"
SelectionMode="Extended"
MouseDoubleClick="OnListViewMouseDoubleClick">
</ListView>
</StackPanel>
</Window>
Code behind:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
namespace TestDemo
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
public IEnumerable<string> Items
{
get
{
for (int i = 0; i < 10; i++) { yield return i.ToString(); }
}
}
private void EditItems_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = _listView != null && _listView.SelectedItems.Count > 0;
}
private void EditItems_Executed(object sender, ExecutedRoutedEventArgs e)
{
EditWindow editWindow = new EditWindow();
editWindow.EditItems = _listView.SelectedItems.Cast<string>();
editWindow.ShowDialog();
}
private void OnListViewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
_editButton.Command.Execute(null);
}
private void OnUnselectAll(object sender, RoutedEventArgs e)
{
_listView.SelectedItem = null;
}
}
}
XAML:
<Window x:Class="TestDemo.EditWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="EditWindow">
<ListBox ItemsSource="{Binding Path=EditItems}" />
</Window>
Code behind:
using System;
using System.Collections.Generic;
using System.Windows;
namespace TestDemo
{
public partial class EditWindow : Window
{
public EditWindow()
{
InitializeComponent();
DataContext = this;
}
public IEnumerable<string> EditItems { get; set; }
}
}
What do you have ListView.SelectionMode set to? It sounds like it is set to Multiple (clicking an item extends the selection), while you might want to set it to Extended (selection is extended when clicking an item and pressing Control or Shift) instead.
I'm not sure what to say about the Edit command problem, though. Maybe there is an odd behavior with SelectedIndex and multiple selection - possibly check the count of the objects in the ListView.SelectedItems collection instead?

Is faking a WPF mouseover possible?

I have a data template with a textbox and a button with some styles on it. I would like to have the button show the mouse over state when focus is on the textbox beside it. Is this possible?
I figure it would involve something like this. I can get the textbox through use of FindVisualChild and FindName. Then I can set the GotFocus event on the textbox to do something.
_myTextBox.GotFocus += new RoutedEventHandler(TB_GotFocus);
Here in TB_GotFocus I'm stuck. I can get the button I want to show the mouse over state of, but I don't know what event to send to it. MouseEnterEvent isn't allowed.
void TB_GotFocus(object sender, RoutedEventArgs e)
{
ContentPresenter myContentPresenter = FindVisualChild<ContentPresenter>(this.DataTemplateInstance);
DataTemplate template = myContentPresenter.ContentTemplate;
Button _button= template.FindName("TemplateButton", myContentPresenter) as Button;
_button.RaiseEvent(new RoutedEventArgs(Button.MouseEnterEvent));
}
I don't think it's possible to fake the event but you can force the button to render itself as if it had MouseOver.
private void tb_GotFocus(object sender, RoutedEventArgs e)
{
// ButtonChrome is the first child of button
DependencyObject chrome = VisualTreeHelper.GetChild(button, 0);
chrome.SetValue(Microsoft.Windows.Themes.ButtonChrome.RenderMouseOverProperty, true);
}
private void tb_LostFocus(object sender, RoutedEventArgs e)
{
// ButtonChrome is the first child of button
DependencyObject chrome = VisualTreeHelper.GetChild(button, 0);
chrome.ClearValue(Microsoft.Windows.Themes.ButtonChrome.RenderMouseOverProperty);
}
you need to reference PresentationFramework.Aero.dlll for this to work and then it will only work on Vista for the Aero theme.
If you want it to work for other themes you should make a custom controltemplate for each of the theme you want to support.
See http://blogs.msdn.com/llobo/archive/2006/07/12/663653.aspx for tips
As a follow up to jesperll's comment, I think you can get around making a custom template for each theme by dynamically setting the style to the one you want / null.
Here is my window, with the style defined (but not set to anything).
<Window x:Class="WpfApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<Style TargetType="{x:Type Button}" x:Key="MouseOverStyle">
<Setter Property="Background">
<Setter.Value>Green</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid Height="30">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="MyTextBox" Grid.Column="0" Text="Some Text" Margin="2" GotFocus="TextBox_GotFocus" LostFocus="MyTextBox_LostFocus"/>
<Button x:Name="MyButton" Grid.Column="1" Content="Button" Margin="2" MouseEnter="Button_MouseEnter" MouseLeave="Button_MouseLeave" />
</Grid>
Instead of setting the style via triggers in the template, you can use events in your .cs file like so:
...
public partial class Window1 : Window
{
Style mouseOverStyle;
public Window1()
{
InitializeComponent();
mouseOverStyle = (Style)FindResource("MouseOverStyle");
}
private void TextBox_GotFocus(object sender, RoutedEventArgs e) { MyButton.Style = mouseOverStyle; }
private void MyTextBox_LostFocus(object sender, RoutedEventArgs e) { MyButton.Style = null; }
private void Button_MouseEnter(object sender, MouseEventArgs e) { ((Button)sender).Style = mouseOverStyle; }
private void Button_MouseLeave(object sender, MouseEventArgs e) { ((Button)sender).Style = null; }
}
You get a reference to the style in the constructor and then dynamically set it / unset it. This way, you can define what you want your style to look like in Xaml, and you don't have to rely on any new dependencies.

Resources