I'm trying to get my head around RoutedCommands in WPF. I like how they decrease coupling between different UI elements and the models but I can't seem to make the bindings work for custom controls that are children to the window. I guess this will be some easy creds for any of you WPF wizards out there! :-)
Here's some example code that can be tried out:
The routed command:
using System.Windows.Input;
namespace Wpf.RoutedCommands
{
public static class Commands
{
public static readonly RoutedCommand SayHi = new RoutedCommand();
}
}
Main window XAML:
<Window x:Class="Wpf.RoutedCommands.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:Wpf.RoutedCommands"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<!-- uncommenting the below makes it work but introduces coupling -->
<!--<Window.CommandBindings>
<CommandBinding Command="{x:Static local:Commands.SayHi}" Executed="cmdSayHi" CanExecute="cmdCanSayHi"></CommandBinding>
</Window.CommandBindings>-->
<DockPanel LastChildFill="True">
<Button DockPanel.Dock="Bottom" Content="Say Hi!" Command="{x:Static local:Commands.SayHi}" />
<local:Greeter x:Name="Greeter" />
</DockPanel>
Main window code:
using System.Windows.Input;
namespace Wpf.RoutedCommands
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
// these two will be used if you uncomment the command bindings in XAML
private void cmdCanSayHi(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = true;
private void cmdSayHi(object sender, ExecutedRoutedEventArgs e) => Greeter.SayHi();
}
}
Custom control ("Greeter") XAML:
<UserControl x:Class="Wpf.RoutedCommands.Greeter"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Wpf.RoutedCommands"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.CommandBindings>
<CommandBinding Command="{x:Static local:Commands.SayHi}" Executed="cmdSayHi" CanExecute="cmdCanSayHi"/>
</UserControl.CommandBindings>
<Grid>
<Label x:Name="Label" />
</Grid>
Greeter code:
using System.Windows.Input;
namespace Wpf.RoutedCommands
{
public partial class Greeter
{
public Greeter()
{
InitializeComponent();
}
private void cmdSayHi(object sender, ExecutedRoutedEventArgs e) => SayHi();
private void cmdCanSayHi(object sender, CanExecuteRoutedEventArgs e) => e.CanExecute = true;
public void SayHi() => Label.Content = "Hi!";
}
}
If you create a WPF project and use the above you will see that the button in main window is disabled. Debugging it shows that the Greeter.cmdCanSayHi method never gets called.
If you uncomment the dormant XAML in main window everything works. So: Why can I bind to commands from the window but not from its child controls? Is it to do with rendering timing or something?
A RoutedCommand searches the visual tree from the focused element and up for an element that has a matching CommandBinding and then executes the Execute delegate for this particular CommandBinding.
Your UserControl is located below the Button in the element tree.
Related
I'm trying to execute a method when the user presses ctrl + Tab. If there is more than one window (App.Current.Windows >1), then CanExecute should be true, otherwise false.
All posts I read so far suggest I need to write a subclass for ICommand and a ViewModel which is basically the "link" between the UI and tge custom command. I've read some examples
of how to create bindings
and because I didn't get this I tried to
learn more about MVVM
but I'm afraid I'm still feeling clueless.
What would an example checking for more than one window open and executing a method SomeMethod if CanExecute was true look like? Where would I place what? I'm sorry, but I searched and tried all day - and still feel clueless.
Any examples or pointers to good explanations?
Here is how you can do it
in Xaml:
<Window x:Class="Test.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<RoutedUICommand x:Key="ExecuteCommand" Text="ExecuteCommand" />
</Window.Resources>
<Window.InputBindings>
<KeyBinding Key="Tab" Modifiers="Ctrl" Command="{StaticResource ExecuteCommand}" />
</Window.InputBindings>
<Window.CommandBindings>
<CommandBinding Command="{StaticResource ExecuteCommand}" CanExecute="ExecuteCommand_CanExecute" Executed="ExecuteCommand_Executed" />
</Window.CommandBindings>
<Grid>
</Grid>
</Window>
In Code Behind:
private void ExecuteCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = App.Current.Windows.Count > 1;
}
private void ExecuteCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Hello");
}
Hope it helps.
I am getting the feeling that you are new to WPF and MVVM, Although it's recommended, you don't have to use MVVM with WPF. You can do the same thing by event handlers.
Here is something you might want to try:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
KeyUp += MainWindow_KeyUp;
}
private void MainWindow_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Tab && (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)))
{
MessageBox.Show(App.Current.Windows.Count.ToString());
}
}
}
I have a button in a UserControl. The code I supply here is simple, just to illustrate you the situation. That's why I included a second button to allow significative user interaction. The UserControl xaml code is as follows:
<UserControl x:Class="WpfControlLibrary1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfControlLibrary1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" x:Name="MyUserControl">
<Grid>
<Button Name="btSelectColor" ToolTip="Select color" Width="23" Height="23" BorderBrush="#FF070707" Background="{Binding Mode=OneWay, ElementName=MyUserControl, Path=CurrentColorBrush}"></Button>
<Button Name="btChangeColor" ToolTip="For change color for testing" Width="120" Height="23" Margin="90,166,90,110" Click="btChangeColor_Click">Test color changing</Button>
</Grid>
</UserControl>
The ElementName property value is MyUserControl, which is the same value of the UserControl x:Name attribute. The Path value is CurrentColorBrush which is the wrap of a dependency property defined in the code behind as follows:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace WpfControlLibrary1
{
/// <summary>
/// Interaction logic for UserControl1.xaml
/// </summary>
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
CurrentColorBrush = new SolidColorBrush(Color.FromArgb(1, 150, 250, 150));
}
public static readonly DependencyProperty currentColorBrushProperty = DependencyProperty.Register("CurrentColorBrush", typeof(SolidColorBrush), typeof(UserControl1));
public SolidColorBrush CurrentColorBrush
{
get
{
return (SolidColorBrush)GetValue(currentColorBrushProperty);
}
set
{
SetValue(currentColorBrushProperty, value);
}
}
private void btChangeColor_Click(object sender, RoutedEventArgs e)
{
CurrentColorBrush = new SolidColorBrush(Color.FromArgb(1, System.Convert.ToByte(CurrentColorBrush.Color.R - 20), 15, 15));
}
}
}
I set a default value to this property in the constructor of the UserControl using the following statement:
CurrentColorBrush = new SolidColorBrush(Color.FromArgb(1, 150, 250, 150));
The problem is that, when the window containing the Usercontrol is shown, the button background is not of the default color defined in the UserControl’s constructor. Even if the second button is clicked, the background of the first button remains the same. Weirder, if the Visual Tree is inspected in runtime you can see that the expected values are in place but you never see the change graphically . The UserControl is in a separate WPF User Control Library project. I'm using Visual Studio 2017.
EDITED*****************************
The solution proposed by Aybe gave me a clue. The code works and is similar to mine. So, I started to look for differences and I realized that the initial value for CurrentBrush in my code is a SolidColorBrush defined from a Color. This Color was defined from RGBA values picked under no special criteria. The fact is that, when I use (as Aybe did) standard values of brushes like Brushes.DarkCyan everything works well. Maybe I was providing RGB values that create a color which is not valid for Background property. I don't know if there are any limitations of this kind but the behavior of my code could point to something like this. I googled it but I couldn't find anything about this matter. Do you have any idea?
Here is the smallest possible example that works, for every case you mentioned:
Window:
<Window
x:Class="WpfApp1.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:local="clr-namespace:WpfApp1"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<StackPanel>
<local:UserControl1 x:Name="uc1" />
<Button
Background="{Binding ElementName=uc1, Path=CurrentBrush}"
Click="Button_Click"
Content="Button1" />
</StackPanel>
</Window>
Window:
using System.Windows;
using System.Windows.Media;
namespace WpfApp1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
// this will work but beware that button focus hides it
// move the mouse away from button to see that it worked
uc1.CurrentBrush = Brushes.PaleVioletRed;
}
}
}
Control:
<UserControl
x:Class="WpfApp1.UserControl1"
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"
mc:Ignorable="d">
<Grid>
<TextBlock Text="Hello, world !" />
</Grid>
</UserControl>
Control:
using System.Windows;
using System.Windows.Media;
namespace WpfApp1
{
public partial class UserControl1
{
// the default value is specified in the metadata, below
// I changed it from default(Brush) to Aqua
public static readonly DependencyProperty CurrentBrushProperty = DependencyProperty.Register(
"CurrentBrush", typeof(Brush), typeof(UserControl1), new PropertyMetadata(Brushes.Aqua));
public UserControl1()
{
InitializeComponent();
// this will override the default value above
CurrentBrush = Brushes.DarkCyan;
}
public Brush CurrentBrush
{
get => (Brush) GetValue(CurrentBrushProperty);
set => SetValue(CurrentBrushProperty, value);
}
}
}
Note: beware of the XAML designer of Visual Studio, sometimes changes are not reflected, i.e. prefer the XAML designer of Expression Blend
Try to use a specific overload of your dependency property creation that allow you to specify a default value
here is an article about the dp's default value
I have a WPF dialog, that hosts a windowsFormHost control, with something like this
<Window x:Class="WPFSort.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:WPFSort"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<WindowsFormsHost HorizontalAlignment="Left" Height="Auto"
Margin="87,43,0,0" VerticalAlignment="Top" Width="Auto">
<local:SimpleWinControl />
</WindowsFormsHost>
</Grid>
</Window>
And for the SimpleWinControl , it is a WinForm control. When button 1 is clicked, I want
The WPF dialog to be closed
And the data importantdata to be "pass out" to the WPF form that calls the WPF dialog?
public partial class SimpleWinControl : UserControl
{
public SimpleWinControl()
{
InitializeComponent();
}
public object importantdata;
private void button1_Click(object sender, EventArgs e)
{
//how should I write the close and pass the importantdata out
}
}
You could for example add a property to your WinForms control that exposes the Button control:
public partial class SimpleWinControl : UserControl
{
public SimpleWinControl()
{
InitializeComponent();
}
public Button TheButton { get { return button1; } }
...
}
Give the WinForms control an x:Name in your XAML markup:
<WindowsFormsHost HorizontalAlignment="Left" Height="Auto" Margin="87,43,0,0" VerticalAlignment="Top" Width="Auto">
<local:SimpleWinControl x:Name="winFormsControl" />
</WindowsFormsHost>
...and hook up to the Click event of the Button in the code-behind of your WPF dialog window:
public partial class Dialog : Window
{
public Dialog()
{
InitializeComponent();
winFormsControl.TheButton.Click += (s, e) => this.Close();
}
}
The window that opens the dialog could then access the importantdata field once the ShowDialog method returns:
private void ShowDialog_Click(object sender, RoutedEventArgs e)
{
Dialog d = new Dialog();
d.ShowDialog();
object importantData = d.winFormsControl.importantdata;
}
Another option may be to raise an event from the WinForms control: https://msdn.microsoft.com/en-us/library/5z57dxz2(v=vs.90).aspx
I have a WPF project with two windows, the first window firing events and have it's own event handlers, the other window will fire the same events, any idea how to use handlers in first window to handle events in second window ?
thanks to #Cody Gray , #keyboardP and to this useful article http://geekswithblogs.net/lbugnion/archive/2007/03/02/107747.aspx
Here's a code snippet to demonstrate the answer:
first step add a new subclass :
using System.Windows;
namespace WpfApplication
{
public class Subclass : Window
{
//Event handlers,functions or any potential reused code
}
}
Second step: go to window1.xaml.cs
namespace WpfApplication
{
public partial class Window1 : Subclass
{
public Window1()
{
InitializeComponent();
}
}
}
Third step: change the Xaml code for window1 as below:
<src:Subclass
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:WpfApplication"
Title="Window1" Height="350" Width="525">
</src:Subclass>
Finally do 2nd & 3rd steps for window2
A simple sample of what i was proposing in first place:
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<Window Loaded="Window_Loaded"/>
<Window Loaded="Window_Loaded"/>
</Grid>
</Window>
Look how both windows share the event handler:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//Whatever
}
}
At this point you should ask yourself if you really needed two windows, or any other container control could be enough.
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.