WPF image's gotfocus/lostfocus event does not fired - c#

Could You please help me, why GotFocus and LostFocusa event doesn't fired when I Click to image and then to textbox?
My XAML:
<Window x:Class="imageclick.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>
<StackPanel>
<Image Source="Untitled.png" GotFocus="GF" LostFocus="LF" Focusable="True"></Image>
<TextBox ></TextBox>
</StackPanel>
</Grid>
</Window>
I could not understand why GotFocus/LostFocus event never fired
Thanks in advance
Update: When I set the tabindex, when the tab reached the image event fired, but I could not reach with mouse click

Image isn't a Control. Only Controls can get focus.Instead of GotFocus and LostFocus use MouseEnter and MouseLeave events,
<StackPanel>
<Image Stretch="Uniform" Source="Untitled.png" Height="410" MouseEnter="Image_MouseEnter" MouseLeave="Image_MouseLeave"></Image>
<TextBox Height="65"></TextBox>
</StackPanel>

According to MSDN, UIElement.GotFocus event occurs when this element gets logical focus.
And logical focus differs from keyboard focus, it is raised when the value of the IsFocused property of an element in the route is changed from false to true.
So, in order to achieve it through mouse clicks, need to handle the respective mouse button events or simply handle MouseDown and set the focus to the sender.
private void Image_MouseDown(object sender, MouseButtonEventArgs e)
{
if (sender is Image)
{
(sender as Image).Focus();
}
}
This will set the IsFocused property of the Image to true.

Related

Popup with "StaysOpen=false" steals LeftMouseButtonDown event

In my application, when a user attempts to click a slider which is on the main window, while a popup control is open, the popup control steals the mouse down event.
This results in the slider not responding to the mouse down event correctly.
(it seems to get focus and move to an incorrect location)
I found that the that the "OnPreviewMouseLeftButtonDown" in the slider does not fire when popup's "StaysOpen" property is false (and the popup is open),
and does fire when its true (or when the popup is closed).
I was wondering if someone has found a solution for this issue.
I encountered these type of issues in other controls in my application in various contexts, So I would prefer a more general solution rather than just solving this for the slider.
Sample code:
<Window x:Class="SampleApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="Root"
Title="MainWindow" Height="350" Width="525">
<Grid Height="130" Width="300">
<Button Width="40" Height="40" Click="ButtonBase_OnClick" HorizontalAlignment="Left" VerticalAlignment="Top"></Button>
<Popup StaysOpen="False" IsOpen="{Binding ElementName=Root, Path=IsOpen}" Width="100" Height="100"
HorizontalAlignment="Center" VerticalAlignment="Center" Placement="Center">
<Grid Background="Black">
<TextBlock Text="hello"></TextBlock>
</Grid>
</Popup>
<Slider Width="200" IsMoveToPointEnabled="True" VerticalAlignment="Bottom"></Slider>
</Grid>
Thanks ahead,
Yotam
This happens because PreviewMouseDown (and it's derivates) (from the base class UIElement) has a default RoutingStrategy.Direct.
Direct - The routed event does not route through an element tree, but does support other routed event capabilities such as class handling, EventTrigger or EventSetter.
This is the source code of the event taken from ReferenceSource.
public static readonly RoutedEvent PreviewMouseLeftButtonDownEvent =
EventManager.RegisterRoutedEvent(
"PreviewMouseLeftButtonDown",
RoutingStrategy.Direct,
typeof(MouseButtonEventHandler),
_typeofThis);
And here is what happens in the Popup:
private void OnPreviewMouseButton(MouseButtonEventArgs e)
{
// We should only react to mouse buttons if we are in an auto close mode (where we have capture)
if (_cacheValid[(int)CacheBits.CaptureEngaged] && !StaysOpen)
{
Debug.Assert( Mouse.Captured == _popupRoot.Value, "_cacheValid[(int)CacheBits.CaptureEngaged] == true but Mouse.Captured != _popupRoot");
// If we got a mouse press/release and the mouse isn't on the popup (popup root), dismiss.
// When captured to subtree, source will be the captured element for events outside the popup.
if (_popupRoot.Value != null && e.OriginalSource == _popupRoot.Value)
{
// When we have capture we will get all mouse button up/down messages.
// We should close if the press was outside. The MouseButtonEventArgs don't tell whether we get this
// message because we have capture or if it was legit, so we have to do a hit test.
if (_popupRoot.Value.InputHitTest(e.GetPosition(_popupRoot.Value)) == null)
{
// The hit test didn't find any element; that means the click happened outside the popup.
SetCurrentValueInternal(IsOpenProperty, BooleanBoxes.FalseBox);
}
}
}
}
So it was designed to work this way, and you should likely not use OnPreviewMouseDown for whatever you are trying to accomplish here.
In my application, when a user attempts to click a slider which is on the main window, while a popup control is open, the popup control steals the mouse down event
While your description is not completely correct, that is the normal behaviour of any Popup control. The reason that this occurs is because the Popup control has focus and so it is listening out for the Click event even if it occurs outside the bounds of the Popup. Think about this logically now... if it didn't do this, how would it know when to close? You will find the same behaviour from the Popup control used in a ComboBox.
There is a workaround to achieve the behavior you require, Set 'IsHitTestVisible = True' for the Slider control you are use.
PS:
Set IsHitTestVisible = True, only when the Popup is Open - False otherwise.

Close DropDown of RibbonComboBox on Button Click

I am having a simple WPF Ribbon Window using RibbonControlsLibrary.
In this Window I am having a RibbonComboBox which contains an Button Element. My intent is to close RibbonComboBox' dropdown once the Button is clicked. I do not want manipulate the IsOpen property of the RibbonComboBox, since the Button should be reused for different purposes. My idea was to handle the MouseDown event of the button and then set e.Handled to false
The XAML looks like this
<ribbon:RibbonWindow x:Class="WpfRibbonApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ribbon="clr-namespace:Microsoft.Windows.Controls.Ribbon;assembly=RibbonControlsLibrary"
xmlns:my="clr-namespace:WpfRibbonApplication3"
Title="MainWindow"
x:Name="RibbonWindow"
Width="640" Height="480">
<ribbon:Ribbon>
<ribbon:RibbonTab Header="Home">
<ribbon:RibbonGroup Header="Group1">
<ribbon:RibbonComboBox>
<ribbon:RibbonGallery>
<ribbon:RibbonGalleryCategory>
<ribbon:RibbonGalleryItem>
<my:CustomButton Content="Test" />
</ribbon:RibbonGalleryItem>
</ribbon:RibbonGalleryCategory>
</ribbon:RibbonGallery>
</ribbon:RibbonComboBox>
</ribbon:RibbonGroup>
</ribbon:RibbonTab>
</ribbon:Ribbon>
</ribbon:RibbonWindow>
The code behind looks as follows:
public CustomButton()
{
// Insert code required on object creation below this point.
AddHandler(MouseDownEvent,(RoutedEventHandler)Button_MouseDown,true);
}
private void Button_MouseDown(object sender, RoutedEventArgs e)
{
Console.WriteLine(string.Format("MouseDownEvent, e.Handled = {0}", e.Handled));
e.Handled = false;
}
The event gets fired but the Ribbon dropdown closes only when pressing the button the second time. Interestingly, the second time I press the button the MouseDown event handler does not get invoked. Can someone give me some hints on how to achieve closing of the ribbon on the first click in a clean manner?
You can give your RibbonComboBox a name, let's say x:Name="comboBox", then in the code behind in the mouse down event handler, you can simply do: comboBox.IsDropDownOpen = false;

Dynamic event handling - not firing

I'm having issues with dynamically handling the MouseUp event. Test code with issue:
WPF:
<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>
<Button x:Name="Button1" Content="Button1" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click_1"/>
<Button x:Name="Button2" Content="Button2" HorizontalAlignment="Left" Margin="10,37,0,0" VerticalAlignment="Top" Width="75"/>
</Grid>
</Window>
C# behind:
using System.Windows;
using System.Windows.Input;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
Button2.MouseLeftButtonUp += something;
}
private void something(object sender, MouseEventArgs e)
{
MessageBox.Show("TEST");
Button2.MouseLeftButtonUp -= something;
}
}
}
Now, I want the MessageBox with the text "TEST" to show only the first time after I click Button2, after I've clicked Button1. It doesn't show up. The same code works with the Click event instead, but I need MouseUp to get the mouse position. I've confirmed that the first function fires, but the second doesn't no matter what I try. Help?
See the Remarks section in ButtonBase.Click:
The ButtonBase marks the MouseLeftButtonDown event as handled in the
OnMouseLeftButtonDown method and raises the Click event. Hence, the
OnMouseLeftButtonDown event will never occur for a control that
inherits from ButtonBase. Instead, attach an event handler to the
PreviewMouseLeftButtonDown event, or call AddHandler(RoutedEvent,
Delegate, Boolean) with handledEventsToo set to true.
Since MouseLeftButtonDown is handled internally, you will also get no
MouseLeftButtonUp event. You could however use the PreviewMouseLeftButtonUp event instead.

Child Expanders raising Parent Expander's Expanded and Collapsed Events?

For some reason, child Expanders (placed in a StackPanel inside of another Expander), when collapsed or expanded, cause the parent Expander to raise its Expanded or Collapsed events.
Anyone know why this is or how I can change it? I'm only interested in the parent's events.
Here is some test XAML:
<Expander Header="Outer" Expanded="Expander_Expanded" Collapsed="Expander_Collapsed">
<StackPanel>
<Expander Header="Inner1">
<Canvas Height="100" Width="100" Background="Blue" />
</Expander>
<Expander Header="Inner2">
<Canvas Height="100" Width="100" Background="Red" />
</Expander>
</StackPanel>
</Expander>
and here is the code-behind:
private void Expander_Expanded(object sender, RoutedEventArgs e)
{
MessageBox.Show("expanded");
}
private void Expander_Collapsed(object sender, RoutedEventArgs e)
{
MessageBox.Show("collapsed");
}
When you run this, if you expand the parent, you get an "expanded" messagebox, as you'd expect. But when you then expand one of the children, you get the messagebox again.
The documentation for the Expanded event says:
The Expanded event occurs when the IsExpanded property changes from false to true
But clearly the IsExpanded property isn't changing on the parent Expander.
Why is this happening, any ideas?
Those events are routed and bubble up in the tree, if you want to prevent the parents from handling the event and thus reacting to it, set e.Handled to true in the child expander's event handler.
Edit: Instead of preventing the event from being raised you also could just restrict the code-execution in the handler to the case when the actual expander where the handler is attached raised the event. You can do this by wrapping everything in an if-block which executes if sender == e.OriginalSource.
(Woo, 10k...)

WPF Maintain Keyboard Focus

I'm creating a UserControl consisting of a TextBox and a ListView. I want keyboard focus to remain with the TextBox as long as the control has keyboard focus (selection changes in the ListView shouldn't remove keyboard focus from the TextBox).
I've tried catching GotKeyboardFocus in the ListView and passing keyboard focus back to the TextBox using Keyboard.Focus(), but this seems to cancel any selection operation in the ListView. The below code shows the problem. Does anyone know how to achieve this functionality?
<Window x:Class="WpfApplication5.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">
<StackPanel>
<TextBox x:Name="TextBox1" />
<ListView x:Name="ListBox1" Keyboard.GotKeyboardFocus="ListBox1_GotKeyboardFocus">
<ListViewItem Content="Able" />
<ListViewItem Content="Baker" />
<ListViewItem Content="Charlie" />
</ListView>
</StackPanel>
</Window>
using System.Windows;
using System.Windows.Input;
namespace WpfApplication5
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void ListBox1_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
Keyboard.Focus(TextBox1);
}
}
}
Instead, have you considered just capturing keystrokes and putting those keystrokes into your TextBox?
<Window PreviewKeyDown="Window_PreviewKeyDown" >
<Grid>
<TextBox x:Name="TextBox1" />
<ListBox />
</Grid>
</Window>
Then in your window's code-behind:
private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
{
TextBox1.Text += e.Key.ToString();
}
You'll have to do extra work for anything like special characters (backspace, etc), and obviously a Key handler for your "Enter" or "Post" operation, but it gives you the ability to just free-form type while the Window has focus and to properly handle the keystrokes as necessary.
It looks like it's possible to change focus in the MouseUp event. I think if you do it too early, like in the GotKeyboardFocus event, you'll steal focus before the listview can handle the event and select the chosen item.
<StackPanel>
<TextBox x:Name="TextBox1" />
<ListView x:Name="ListBox1" MouseUp="ListBox1_MouseUp">
<ListViewItem Content="Able" />
<ListViewItem Content="Baker" />
<ListViewItem Content="Charlie" />
</ListView>
</StackPanel>
private void ListBox1_MouseUp(object sender, MouseButtonEventArgs e)
{
TextBox1.Focus();
}
If you are calling your WPF window from a WinForm you must use this:
System.Windows.Forms.Integration.ElementHost.EnableModelessKeyboardInterop(wpfWindow);
wpfWindow.show();
from the MSDN documentation.
Thats how I solved my keyboard problem.
IceX
This is a hack, but what if instead of listening to the GotKeyboardFocus event, you listen to the SelectionChanged event on the ListBox?
Put Focusable=false on your ListView.
Ok, this was driving me crazy. Even though set focus to UserControl every time lost focus, still couldn't get my command hot keys to work. All I had to do was to set the property Focusable to true, and voilĂ , it's working!

Categories

Resources