Access keys not working inside popup (WPF) - 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.
}

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;
}
}

Dynamically Change the Style of Control in WPF

I have styles defined for various controls viz. textboxes,combobox,datepickers,etc.. in my application. The resources needed in these styles are present in other styles and all styles are there in ResourceDictionary.
I want to change the background color of these controls to yellow on basis of some configuration settings.
I am thinking to use attached properties and make use of them with styles and set the background color. But not sure how to implement it.
Please suggest if any other approach.
Thanks,
Abdi
You could try this solution:
In your view:
Call your window like 'myForm' or whatever:
I use in this case some radiobuttons. Each of them with an associated color You would adapt this to your requirements.
<UniformGrid Columns="0"
x:Name="_skinChanger"
Grid.Column="0" Grid.Row="1" Width="200">
<RadioButton x:Name="BlueSkin"
Content="Blue Skin" HorizontalAlignment="Left" Width="131" />
<RadioButton x:Name="None"
Content="No Skin"
IsChecked="True" HorizontalAlignment="Left" Width="87" />
<RadioButton x:Name="RedSkin"
Content="Red Skin" Margin="0,0,0,4" HorizontalAlignment="Left" Width="109" />
<RadioButton x:Name="GreenSkin"
Content="Green Skin" HorizontalAlignment="Left" Width="109" />
</UniformGrid>
The elements you want to change the style should use dynamic resource:
...
Style="{DynamicResource LabelStyle}"
...
Inside the constructor of your window for example...
Loaded += RuntimeSkinning_Loaded;
Then define the event in your class.
void RuntimeSkinning_Loaded(object sender, RoutedEventArgs e)
{
// this is my uniformgrid where the radiobuttons are placed. I add a handled for
// the checkedevent.
_skinChanger.AddHandler(RadioButton.CheckedEvent, new RoutedEventHandler(OnSkinChanged));
}
private void OnSkinChanged(object sender, RoutedEventArgs e)
{
string name = (e.OriginalSource as RadioButton).Name;
_registerForm.Resources.Clear();
_registerForm.Resources.MergedDictionaries.Clear();
if (name == "None") return;
ResourceDictionary skin =
Application.LoadComponent(new Uri("/myproject;component/Skins/" + name + ".xaml", UriKind.Relative)) as ResourceDictionary;
_registerForm.Resources.MergedDictionaries.Add(skin);
e.Handled = true;
}

How to change button text from OK/CANCEL to YES/NO for MessageBox in SL4?

SL 4 provides a dialog box by MessageBox, but MessageBoxButton only provide option for button as OK, Cancel. How to change it to YES, NO button?
This MessageBox built into silverlight can't be changed beyond the capabilities that are exposed.
Your only solution would be to make a custom ChildWindow class which provides the functionality you want. There are many examples of this.
This has the advantage of acting more like other silverlight popup windows, and can be themed and skinned however you'd like, with whatever buttons and functionality you chose to implement.
This has the disadvantage that you are forced then to use a callback model rather than an a more usual imperative flow control.
Your best bet is to use the System.Windows.Controls.Primitives.Popup
<Grid x:Name="LayoutRoot" Background="White">
<Button x:Name="showPopup" Click="showPopup_Click" Height="100" Width="100" Content="Show Popup"/>
<Popup x:Name="myPopup" IsOpen="False" VerticalAlignment="Top" HorizontalAlignment="Center" >
<Canvas Height="200" Width="300" Background="Azure">
<Button x:Name="closePopup" Click="closePopup_Click" Height="50" Width="100" Content="Close Popup"/>
</Canvas>
</Popup>
<Canvas x:Name="myCanvas" Visibility="Collapsed" Background="Black" Opacity=".4"></Canvas>
</Grid>
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
}
private void closePopup_Click(object sender, RoutedEventArgs e)
{
myPopup.IsOpen = false;
myCanvas.Visibility = Visibility.Collapsed;
}
private void showPopup_Click(object sender, RoutedEventArgs e)
{
myPopup.IsOpen = true;
myCanvas.Visibility = Visibility.Visible;
}
}
If you don't want to create your own popup, there probably are 3rd party messageboxes, but with this solution, you have eveything in your own hands.

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)

Closing a Popup when its parent gets collapsed

I've been struggling with this for quite some time and I can't seem to find a proper solution. This is the scenario stripped down. Imagine you have the following XAML:
<Grid x:Name="LayoutRoot" Background="White">
<Grid x:Name="Host" Width="200" Height="200">
<Popup IsOpen="True">
<Button Content="Some Button" Click="Button_Click" />
</Popup>
</Grid>
</Grid>
In the Button_Click event handler all I do is collapse the grid with name Host.
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Host.Visibility = System.Windows.Visibility.Collapsed;
}
What I expected was that the Popup would close therefore hiding the Button. I understand that Popups are not in the same VisualTree and I understand why this might not be working the way I expect it to, but there should be some kind of mechanism for this to happen automatically. The only workaround that comes to my mind is on LayoutUpdated to traverse the visual tree up and ask each Button's parent if it is visible and if I meet a collapsed parent -> close the Popup. However, imagine the performance hit having a HUGE visual tree. It's insane to traverse the visual tree on every layout pass.
I'm open to any sort of suggestions.
EDIT: It seems that I did not explain fully my scenario. The case is to collapse the Popup if ANY of its parent gets collapsed (not just the immediate one). In WPF there is a useful property called IsVisible which is different than Visibility. For example, Visibility might still be Visible, but IsVisible will be false in this scenario.
Best Regards,
K
I think you found a bug, or at least a "weirdness" in the popup control - check this out:
My initial thought was to simply Bind the Visibility of the Popup to the Host. This SHOULD work.
<Grid x:Name="LayoutRoot" Background="White">
<Grid x:Name="Host" Width="200" Height="200" Background="Aqua">
<Popup IsOpen="True" Visibility="{Binding ElementName=Host, Path=Visibility}" Height="100" Width="100">
<Button Content="Some Button" Click="Button_Click"/>
</Popup>
</Grid>
</Grid>
But it does not work. The "Host" grid disappears, but I still see the button. This confused me, so I fired up Silverlight Spy, and check this out - setting the Visibility of the Popup does NOT hide the button!
See Demo Video
Anyway, in order to make this work, you just need to massage things a little bit in order tie the Host Visibility to the IsOpen property. I used a converter here:
<UserControl.Resources>
<Junk:VisibilityToBoolConverter x:Key="VisibilityToBoolConverter"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="White">
<Grid x:Name="Host" Width="200" Height="200" Background="Aqua">
<Popup IsOpen="{Binding ElementName=Host, Path=Visibility, Converter={StaticResource VisibilityToBoolConverter}}" Height="100" Width="100">
<Button Content="Some Button" Click="Button_Click"/>
</Popup>
</Grid>
</Grid>
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Host.Visibility = System.Windows.Visibility.Collapsed;
}
public class VisibilityToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((Visibility) value) == Visibility.Visible;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
That's working for me.
If you just want to close the popup, why don't you set the IsOpen at the popup to false.
<Grid x:Name="LayoutRoot" Background="White">
<Grid x:Name="Host" Width="200" Height="200">
<Popup x:Name="HostPopup" IsOpen="True">
<Button Content="Some Button" Click="Button_Click" />
</Popup>
</Grid>
</Grid>
private void Button_Click(object sender, RoutedEventArgs e)
{
this.HostPopup.IsOpen = false;
}
This closes the popup.

Resources