Same click event for both Button and Grid - wpf

I read about Routed events today and tried to provide same click event handler for both a normal button and a custom grid button. Stackpanel is handling the routed button click event, and to invoke same handler I am firing a click event from grid's mousedown event. Code is without error but not working as expected.
Button click brings the messagebox but clicking the grid with mouse does nothing.
<Window x:Class="RoutedEventPr.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="550" Width="525">
<StackPanel Background="Transparent" Button.Click="Button_Click_1">
<Grid Width="200" Height="100" Background="Aqua" MouseDown="Grid_MouseDown_1">
<Ellipse StrokeThickness="4" Width="200" Height="100" Fill="Beige"/>
<TextBlock Text="Press" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
<Button x:Name="SureBtn" Content="Pause !" Width="200" Margin="0 10 0 0"/>
<Image HorizontalAlignment="Center" VerticalAlignment="Center" Width="200" Height="200" Source="I://fancybtn.jpg"/>
<Label Content="Start the game"/>
</StackPanel>
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
RaiseEvent(new RoutedEventArgs(Button.ClickEvent, this));
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
MessageBox.Show("Are you sure !");
}

Button.Click event is routed event with bubbling strategy set to Bubble.
That means it will bubble up to visual parent till root until it was handled. In your case, you raise an event from Window, so it will bubble up from Window to its parent which is null.
You have to raise the event from child control of StackPanel so that it can bubble up to StackPanel.
XAML
<Grid x:Name="grid" Width="200" Height="100" Background="Aqua"
MouseDown="Grid_MouseDown_1">
<Ellipse StrokeThickness="4" Width="200" Height="100" Fill="Beige"/>
<TextBlock Text="Press" HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
Code behind
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
grid.RaiseEvent(new RoutedEventArgs(Button.ClickEvent, this));
}

I did below changes and it works now.
private void Grid_MouseDown_1(object sender, MouseButtonEventArgs e)
{
SureBtn.RaiseEvent(new RoutedEventArgs(Button.ClickEvent, this));
}

Related

Programatically attach an event handler to a custom child element routed event

I have a Window that has a Button and a Popup. When the button is clicked, the event handler in code behind opens the Popup. On the Popup I have Button when clicked, the event handler in code behind closes the Popup. Simple. Crude.
I also have a UserControl that has a custom routed event and a Button that raises that event.
That UserControl has been placed on the Popup.
I have added an event handler in XAML on the Popup element for the UserControl custom event. In code behind I show a message box.
This all works fine and dandy.
This is an extremely boiled down example. My ultimate question is, how do I programmatically attach an event handler to the custom routed event at the Popup level?
The UserControl XAML:
<Grid>
<Button
Width="100"
Height="100"
Click="Button_Click"
Content="Event!" />
</Grid>
The UserControl code behind:
public static readonly RoutedEvent CustomEventEvent = EventManager.RegisterRoutedEvent(
nameof(CustomEvent), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(UserControl1));
public event RoutedEventHandler CustomEvent
{
add => this.AddHandler(CustomEventEvent, value);
remove => this.RemoveHandler(CustomEventEvent, value);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
RaiseEvent(new RoutedEventArgs(CustomEventEvent));
}
Main window XAML:
<Grid>
<Button
Width="200"
Height="100"
Click="Button_Click"
Content="Popup" />
<Popup
x:Name="MyPopup"
local:UserControl1.CustomEvent="MyPopup_CustomEvent"
AllowsTransparency="True"
Loaded="MyPopup_Loaded"
Placement="Right">
<Border
Background="Azure"
BorderBrush="Gray"
BorderThickness="2"
CornerRadius="3">
<Grid Width="500" Height="300">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button
Grid.Row="0"
Width="100"
Height="100"
Click="Button_Click_1"
Content="Close" />
<local:UserControl1
Grid.Row="1"
Width="100"
Height="100" />
</Grid>
</Border>
</Popup>
</Grid>
The main window code behind (minus the boring popup open/close button clicks):
private void MyPopup_CustomEvent(object sender, RoutedEventArgs e)
{
MessageBox.Show("YAY");
}
private void MyPopup_Loaded(object sender, RoutedEventArgs e)
{
if (sender is Popup popup)
{
// Here is where the wheels fall off.
// How do I find/attach to the routed event after it has bubbled up to the popup?
// popup.CustomEvent += LocalMyPopup_CustomEvent;
}
}
private void LocalMyPopup_CustomEvent(object sender, RoutedEventArgs e)
{
}
Ultimately, there will be several of the UserControls in many popups and I'd rather not have to attach event handlers to each and every one of them. Especially considering I can get them all at the Popup level using MyPopup_CustomEvent. I just want to replicate that behavior.
Turns out the solution is as trivial as it is (in hindsight) obvious. The AddHandler has to be called directly on the Popup and passing in the static Routed Event reference.
private void MyPopup_Loaded(object sender, RoutedEventArgs e)
{
if (sender is Popup popup)
{
popup.AddHandler(UserControl1.CustomEventEvent, new RoutedEventHandler(LocalMyPopup_CustomEvent), true);
// popup.CustomEvent += LocalMyPopup_CustomEvent;
}
}

Access keys not working inside popup (WPF)

I have a simple popup in my WPF application.
There is a button with access-key inside this popup.
The problem I have is that this button doesn't respond to Alt+access-key combination.
Moreover pressing Alt doesn't make access key visible like it happens in ordinary window.
Is there any way to make controls inside popup respond to Alt+access-key combination?
P.S. I have no problem with navigation using Tab through this popup.
Sapmle code that I'm using
<Grid>
<Button Click="ButtonBase_OnClick" Content="_Open File"></Button>
<Popup x:Name="Popup" StaysOpen="False">
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button Content="_Open File"/>
<Button Grid.Row="1" Content="O_pen File"/>
<CheckBox Grid.Row="2" Content="_Go"></CheckBox>
</Grid>
</Popup>
</Grid>
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Popup.IsOpen = true;
}
I have also tried adding this as the first answer suggests
private void Popup_OnOpened(object sender, EventArgs e)
{
var popup = sender as Popup;
popup.Child.Focusable = true;
Keyboard.Focus(popup.Child);
}
I have also tried the idea from the first comment
private void Popup_OnOpened(object sender, EventArgs e)
{
var popup = sender as Popup;
FocusManager.SetIsFocusScope(popup, true);
}
or instead of standart focus scope the one from suggested link codeproject.com/Articles/38507/Using-the-WPF-FocusScope
EnhancedFocusScope.SetFocusOnActiveElementInScope(popup);
Setting focus scope helped a little bit, but I didn't manage to make it work exactly as I would like.
Setting focus scope to true did help to use alt+key combination for checkboxes and label+textbox, but not for buttons. Although I could use Alt+access key combinations, I couldn't actually see them, because underscores didn't appear when I pressed Alt
Popup is not part of the visual tree. This means it has its own isolated focus scope. When a Popup is opened, the Popup.Child is hosted in a dedicated Window with its own detached visual tree. The Popup.Child therefore needs to explicitly receive keyboard focus before the access keys are available in the detached focus scope.
You can handle the Popup.Opened event, either in code-behind or using an attached behavior.
It's essential that the Popup.Child is focusable in order to receive keyboard focus.
Some classes like Panel and its subclasses have UIElement.Focusable set to false by default.
<StackPanel>
<ToggleButton x:Name="ToggleButton" Content="Show Popup" />
<Popup x:Name="Popup"
AllowsTransparency="True"
PlacementTarget="{Binding ElementName=ToggleButton}"
IsOpen="{Binding ElementName=ToggleButton, Path=IsChecked}"
Opened="Popup_OnOpened">
<StackPanel>
<Button Grid.Row="1" Content="O_pen File" />
<CheckBox Grid.Row="2" Content="_Go" />
</StackPanel>
</Popup>
</StackPanel>
private void Popup_OnOpened(object sender, EventArgs e)
{
var popup = sender as Popup;
popup.Child.Focusable = true;
Keyboard.Focus(popup.Child);
}
Once a button or a checkbox inside the popup is focused, the alt-shortcuts works.
<Button Click="ButtonBase_OnClick" Content="_Open File" />
<Popup x:Name="Popup">
<StackPanel Background="White">
<CheckBox x:Name="FirstCeckbox" Content="_Foo" />
<CheckBox Content="_Bar" />
</StackPanel>
</Popup>
Code behind:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Popup.IsOpen = !Popup.IsOpen;
if (Popup.IsOpen)
FirstCeckbox.Focus(); // Make sure to focus a Button or a Checkbox, not the Stackpanel or Grid etc.
}

Seemingly strange WPF behaviour: textbox lostfocus event and button click event

The following behaviour is occurring with the .net 4.0 code set out further below:
Click on textbox so it gains focus, and then click button:
As code stands, lostfocus handler is called, but not buttonclick handler
Comment out MessageBox.Show("handlelostfocus") and then click handler is called
Set breakpoint in handlelostfocus and breakpoint is hit, but click handler is not called
Are these bugs or behaviour by design - if later, is there any further explanation?
<Window x:Class="WpfApplication4.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="216,194,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="197,108,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
</Grid>
</Window>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
textBox1.LostFocus += new RoutedEventHandler(handlelostfocus);
}
private void handlelostfocus(object sender, RoutedEventArgs e)
{
MessageBox.Show("handlelostfocus");
}
private void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("click");
}
}
Change ClickMode property of Button to "Press"
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="216,194,0,0" ClickMode="Press" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click" MouseUp="button1_MouseUp" MouseLeftButtonUp="button1_MouseLeftButtonUp" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="197,108,0,0" Name="textBox1" VerticalAlignment="Top" Width="120" />
The 'click' in this case never occurs because as H.B. indicated you are interrupting the UI/event logic by showing the modal messagebox so there is never a mouse down event on the button.
Try replacing the messagebox with a non-modal wndow such as this:
new Window() { Width = 300, Height = 100, Title = "handlelostfocus" }.Show();
and you will see that the events still occur because you are not drawing the focus away from the main window in the middle of the event logic.
You interrupt the click logic, to get a click both mouse-down and its mouse-up need to occur consecutively on the Button; thus the observed behavior seems fine to me.

Any way for a ToolTip to trigger a MouseEnter event?

I have a control with a tooltip. What i want is that when I hover over the control and the tooltip opens: if I then enter the tooltip with the mouse - this will trigger a mouseEnter event in order to trigger some other action. The closest I am to finding a solution to this is adding a ToolTipClosing event on the control with the trigger ... but this will fire as soon as I leave the control - even if my mouse doesn't enter the tooltip.
(Triggering a MouseEnter event on the tooltip itself doesn't seem to get fired at all)
Here's an example: (where I want to change the background of the border if I enter the tooltip)
XAML
<Border Height="300" Name="dummyBorder"
Width="200"
Background="Red" />
<Label ToolTipService.InitialShowDelay="3000"
Content="Hover over here"
ToolTipService.ShowDuration="4000"
ToolTipService.Placement="Right"
ToolTipClosing="Label_ToolTipClosing"
Width="100"
HorizontalAlignment="Center"
Margin="10">
<Label.ToolTip>
<ToolTip Name="tt" MouseEnter="ttBorder_MouseEnter">
<Border Background="Brown"
Name="ttBorder"
MouseEnter="ttBorder_MouseEnter"
Width="100"
Height="50">
<TextBlock Text="This is a tool tip." />
</Border>
</ToolTip>
</Label.ToolTip>
</Label>
CodeBehind: (neither of these work)
private void Label_ToolTipClosing(object sender, ToolTipEventArgs e)
{
if (tt.IsMouseDirectlyOver)
{
dummyBorder.Background = Brushes.Aqua;
}
}
private void ttBorder_MouseEnter(object sender, MouseEventArgs e)
{
dummyBorder.Background = Brushes.Aqua;
}
I specifically want to use a tooltip and not a popup. Is this possible?
Any help will be greatly appreciated!
you will want to try something like this:
<Window.CommandBindings>
<CommandBinding Command="ChangeColour"
CanExecute="ChangeCanExecute"
Executed="ChangeExecuted" />
</Window.CommandBindings>
inside your tooltip tag:
<MouseBinding Gesture="LeftClick" Command="{Binding ChangeColour}"/>
then in your codebehind:
private void ChangeCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = true;
e.Handled = true;
}
private void ChangeExecuted(object sender, ExecutedRoutedEventArgs e)
{
dummyBorder.Background = Brushes.Aqua;
e.Handled = true;
}
Well (after 9 months and no answer) I guess that there is no way then. (Unless proven otherwise)

Drag and Drop WPF ComboBox, Buttons, Radio Buttons, etc

I have a problem, i've already read tutorials, blogs, etc about drag and drop on WPF (i'm using VS10).
The problem is I need to have a toolbox with buttons,combobox, radio button,etc sothe user can drag it and drop it(copy) on a work space (canvas or whatever).
I managed to do drag and drop from textbox and images but that doesn't work for me, when i tried on buttons or combobox it just doesnt work, i assume it is cause of the click event by default, i don't know what the problem is tho. Here is what i've tried with a button.
<Window x:Class="WpfApplication1.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>
<TextBox Height="22" HorizontalAlignment="Left" Margin="84,36,0,0" Name="textBox1" VerticalAlignment="Top" Width="103" Text="Drag" />
<TextBox Height="40" HorizontalAlignment="Left" Margin="225,136,0,0" Name="textBox3" VerticalAlignment="Top" Width="124" Text="Drop" />
<Label Content="DragLabel" Height="26" HorizontalAlignment="Left" Margin="284,36,0,0" Name="label1" VerticalAlignment="Top" Width="80" MouseDown="label1_MouseDown" />
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="84,122,0,0" Name="button1" VerticalAlignment="Top" Width="75" MouseDown="button1_MouseDown" AllowDrop="True" IsEnabled="True" Click="button1_Click" />
<Rectangle Height="100" HorizontalAlignment="Left" Margin="149,199,0,0" Name="rectangle1" Stroke="Black" VerticalAlignment="Top" Width="200" AllowDrop="True" Fill="#FFDCA1A1" />
</Grid>
My Code Behind ...
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void label1_MouseDown(object sender, MouseButtonEventArgs e)
{
Label lbl = (Label)sender;
DragDrop.DoDragDrop(lbl, lbl.Content, DragDropEffects.Copy);
}
private void button1_MouseDown(object sender, MouseButtonEventArgs e)
{
var dependencyObject = (Button)sender;
DragDrop.DoDragDrop(dependencyObject, dependencyObject, DragDropEffects.Move);
}
private void button1_Click(object sender, RoutedEventArgs e)
{
return;
}
}
Thank You in advance guys. Btw sry about my english :s...
Thx again!
Luis
Have you tried using the PreviewMouseDown event instead of MouseDown? Your code will get called before the Button can capture the click.
WPF elements normally use RoutedEvents which often have a corresponding "Preview" event that uses the Tunneling Routing Strategy, which will be sent to all parents before the element that actually raised the event. This allows you to perform your operation in response to the MouseDown before the Button gets a chance to try to execute a click action.
private void button1_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var dependencyObject = (Button)sender;
DragDrop.DoDragDrop(dependencyObject, dependencyObject, DragDropEffects.Move);
}
will work as mentioned by Abe

Resources