Make a wpf element hide when it loses focous - c#

I have a WPF Menu bar which is collapsed when the program starts, and can be shown by pressing F10 (like Firefox)
<Menu x:Name="mainMenuBar" Height="20" Width="130" HorizontalAlignment="Left" DockPanel.Dock="Top" FocusManager.LostFocus="mainMenuBar_LostFocus">
I have code which I though would make it collapse again if it loses focus (i.e. the user clicks somewhere other than the menu bar and the sub menus) using the FocusManager.LostFocus property.
private void showMenuBar(object sender, KeyEventArgs e)
{
if(e.SystemKey == Key.F10)
{
mainMenuBar.Visibility = Visibility.Visible;
mainMenuBar.Focus();
}
}
private void mainMenuBar_LostFocus(object sender, RoutedEventArgs e)
{
mainMenuBar.Visibility = Visibility.Collapsed;
}
When I click away from the menu bar it does not collapse, but it does when I click on the menu bar. This behaviour is the opposite of what I expected, does anyone know why this is happening?
Edit: XAML Code
<Menu x:Name="mainMenuBar" Height="20" Width="130" HorizontalAlignment="Left" DockPanel.Dock="Top">
<MenuItem Header="File">
<Separator />
<MenuItem Header="Exit" Click="menuFileExit_Click" ToolTip="Close Program" />
</MenuItem>
<MenuItem Header="Options">
<MenuItem Header="Settings" Click="menuOptionsSettings_Click" />
</MenuItem>
<MenuItem Header="Help">
<MenuItem Header="Read Me" Click="menuHelpReadMe_Click" />
<MenuItem Header="Version History" Click="menuHelpVersionHistory_Click" />
<MenuItem Header="About" Click="menuHelpAbout_Click" />
</MenuItem>
</Menu>

I found the way to get wished behaviour.
Show menu as you do but even without setting focus.
protected override void OnKeyDown(KeyEventArgs e)
{
if(e.SystemKey == Key.F10)
{
mainMenuBar.Visibility = Visibility.Visible;
}
base.OnKeyDown(e);
}
Override OnPreviewMouseDown for window where your menu is.
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
if (mainMenuBar.IsVisible && e.Source != mainMenuBar&& !IsMenuChildMouseDown(e.Source as FrameworkElement))
{
mainMenuBar.Visibility = Visibility.Collapsed;
}
base.OnPreviewMouseDown(e);
}
And create method IsMenuChildMouseDown(FrameworkElement elem) for checking if mouse was pressed on child of menu.
private bool IsMenuChildMouseDown(FrameworkElement elem)
{
if (elem == null)
return false;
DependencyObject parent = elem.Parent;
if (parent == null)
return false;
else
{
if (parent == mainMenuBar)
return true;
return IsMenuChildMouseDown(parent as FrameworkElement);
}
}

Related

How to stop canvas shifting when right click is pressed to show contextmenu?

I'm working on drawing map using shapes and add then to canvas, I've to show ContextMenu when MouseRightButtonDown, but there is shifting happened on canvas when I press the right button of mouse, So how can i stop this shifting?!
private void MainCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton == MouseButton.Right)
{
Canvas c = sender as Canvas;
ContextMenu contextMenu = c.ContextMenu;
contextMenu.PlacementTarget = c;
contextMenu.IsOpen = true;
e.Handled = true;
}
}
<Canvas x:Name="MainCanvas" Uid="parentCanvas" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MouseDown="MainCanvas_MouseDown">
<Canvas.ContextMenu >
<ContextMenu>
<MenuItem Name="CzechRepublic"
Header="Czech Republic"/>
<MenuItem Name="MiddleEurope"
Header="Middle Europe"/>
<MenuItem Name="Europe"
Header="Europe"/>
<MenuItem Name="WholeMap"
Header="Whole Map"
Click="WholeMap_Clicked"/>
<Separator />
<MenuItem Name="Settings"
Header="Settings"
Click="Settings_Clicked"/>
</ContextMenu>
</Canvas.ContextMenu>

WPF TextBox with popup

I'm working with WPF Popup. A popup contains some keyboard.
I want to open a popup when a user clicks on a text box and do not hide a popup while textbox has the focus.
Also I need to hide a popup when a user clicks somewhere away from a popup.
Here's the xaml code:
<Grid>
<StackPanel>
<TextBox x:Name="textBox" GotKeyboardFocus="textBox_GotFocus" MouseDown="textBox_MouseDown" />
<Popup x:Name="popup" Width="100" Height="100" PlacementTarget="{Binding ElementName=textBox}" Placement="Bottom"
StaysOpen="{Binding ElementName=text,Path=IsKeyboardFocused}">
<Grid Background="Blue">
</Grid>
</Popup>
</StackPanel>
</Grid>
Here's the c# code:
private void textBox_GotFocus(object sender, KeyboardFocusChangedEventArgs e)
{
popup.IsOpen = true;
}
private void textBox_MouseDown(object sender, MouseButtonEventArgs e)
{
popup.IsOpen = true;
}
I found that the binding can help:
StaysOpen="{Binding ElementName=,Path=IsKeyboardFocused}"
But the TextBox never hides. Also if I set StaysOpen="False" the TextBox never shows
You could handle the LostKeyboardFocus event and check whether the new focused element is the TextBox itself or if it's a child element of the Popup:
private void textBox_GotFocus(object sender, KeyboardFocusChangedEventArgs e)
{
popup.IsOpen = true;
}
private void textBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
var newFocusedElement = e.NewFocus as DependencyObject;
//if the focused element is a child of the window, it can't be the child of the popup
if (newFocusedElement == null || FindParent<Window>(newFocusedElement) != null)
popup.IsOpen = false;
}
private void popup_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
popup.IsOpen = e.NewFocus == textBox;
}
private static T FindParent<T>(DependencyObject dependencyObject) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(dependencyObject);
if (parent == null) return null;
var parentT = parent as T;
return parentT ?? FindParent<T>(parent);
}
Sample XAML:
<StackPanel>
<TextBox x:Name="textBox" GotKeyboardFocus="textBox_GotFocus" LostKeyboardFocus="textBox_LostKeyboardFocus" />
<Popup x:Name="popup" Width="100" Height="100" PlacementTarget="{Binding ElementName=textBox}"
LostKeyboardFocus="popup_LostKeyboardFocus"
Placement="Bottom">
<Grid x:Name="popupRoot" Background="Blue">
<TextBox Margin="10" />
</Grid>
</Popup>
<TextBox />
</StackPanel>

C# WPF Context menu item click event returns null

I'm using WPF with C#. I have a grid of buttons and I've assigned a context menu to each button if it's right-clicked. Right-clicking the buttons works fine and the context menu shows up but clicking the menu items gives a null sender. What could be wrong? Here is the relevant code embedded into the Window XAML code:
<Window.Resources>
<ContextMenu x:Key="cmButton">
<MenuItem Header="Copy" Click="Copy_Click" />
<MenuItem Header="Cut" />
<Separator />
<MenuItem Header="Paste" Click="Paste_Click" />
</ContextMenu>
</Window.Resources>
And here is the relevant C# code:
public void WarpHeadCell_RightClick(DraftWindow w, Button b)
{
ContextMenu cm = w.FindResource("cmButton") as ContextMenu;
cm.PlacementTarget = b;
cm.IsOpen = true;
}
private void Copy_Click(object sender, RoutedEventArgs e)
{
MenuItem mi = e.OriginalSource as System.Windows.Controls.MenuItem;
ContextMenu cm = mi.ContextMenu;
Button b = (Button)cm.PlacementTarget;
}
mi is always null, does anybody have a clue?
I don't see any reason why mi would be null, but you haven't included everything, so I'm going out on a limb here and guessing that mi.ContextMenu is where you are running into a problem. The menu item itself doesn't have a ContextMenu, but it does have a Parent property, which is the ContextMenu it belongs to and is probably what you are looking for.
private void Copy_Click(object sender, RoutedEventArgs e)
{
MenuItem mi = sender as MenuItem;
ContextMenu cm = mi.Parent as ContextMenu;
Button b = cm.PlacementTarget as Button;
}
This is my XAML:
<Window.Resources>
<ContextMenu x:Key="cmButton">
<MenuItem Click="Copy_Click" Header="Copy" />
<MenuItem Header="Cut" />
<Separator />
<MenuItem Click="Paste_Click" Header="Paste" />
</ContextMenu>
</Window.Resources>
<Grid>
<Button Content="SS" ContextMenu="{StaticResource cmButton}" />
</Grid>
This is my code:
private void Paste_Click(object sender, RoutedEventArgs e)
{
if (sender is MenuItem menuItem)
{
Debug.WriteLine("Ok");
}
if (e.OriginalSource is MenuItem menuItem2)
{
Debug.WriteLine("Ok");
}
}
It works, menuItem and menuItem2 is not null
You can download my rar here:
https://1drv.ms/u/s!AthRwq2eHeRWiOkw6MHXelG-ntjaDQ

Change the header/content in class system.windows.controls.menuitem?

I have a c#, WPF application with the notification in taskbar. And the design like this:
I want the app could tell the notification icon is clicked. For example, if you clicked on the Change status, it will be like this:
The code in main window design is like this:
<ContextMenu>
<MenuItem
Header="LeftClicked"
Click="NodifierClick"/>
<Separator/>
<MenuItem Header="Reset" Click="NotifyIconClickReset" />
<MenuItem Header="Show" Click="NotifyIconClickShow" />
<MenuItem Header="Hide" Click="NotifyIconClickHide" />
<MenuItem Header="Close" Click="NotifyIconClickClose" />
<Separator/>
<MenuItem Header="Exit" Click="NotifyIconClickExit" />
</ContextMenu>
I have no idea that how to change the Header in the class System.Windows.Control.MenuItem, somebody has the solution?
This is the quick and dirty way:
private void NotifyIconClickReset(object sender, RoutedEventArgs e)
{
((MenuItem)sender).Header = "LeftClicked";
}
But if you want to have a consistent reference to the menu item and make sure nothing else has its header changed by accident, you could give the item a name and reference it instead:
<MenuItem x:Name="_resetMenu" Header="Reset" Click="NotifyIconClickReset" />
private void NotifyIconClickReset(object sender, RoutedEventArgs e)
{
_resetMenu.Header = "LeftClicked";
}

How to add an option to selectively remove items from ComboBox?

I have a ComboBox that displays strings. How can I add an option to remove some items from the ComboBox list? I tried:
<ComboBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Click="MenuItem_OnClick"></MenuItem>
</ContextMenu>
</ComboBox.ContextMenu>
But I don't know how to locate the item the user chose:
private void MenuItem_OnClick(object sender, RoutedEventArgs e) {
/* ... ??? ... */
}
I don't mind putting some icon next to each item, that removes its related item when clicked, but don't know how to do it..
Summary:
This is how I solved it, finally (The credit belongs to Nawed Nabi Zada, who provided the main idea of "climbing" using the VisualTreeHelper.GetParent(...) to get the ComboBoxItem, in the accepted answer, below)
<ComboBox IsEditable="True" Name="RemotePathComboBox" VerticalAlignment="Center"
SelectionChanged="RemotePathComboBoxOnSelectionChanged"
Grid.Column="1" Margin="0,6" KeyUp="HostNameOrIPAddress_OnKeyUp">
<ComboBox.ItemTemplate>
<DataTemplate>
<DockPanel>
<Button Click="RemoveRemotePathItem_Click" Margin="5" DockPanel.Dock="Left">
<Image Source="{Binding Converter={StaticResource iconExtractor}, ConverterParameter=%WinDir%\\System32\\shell32.dll|131}"/>
</Button>
<TextBlock Name="ItemTextBlock" VerticalAlignment="Center" Text="{Binding Path=Path}"></TextBlock>
</DockPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Code-behind:
private void RemoveRemotePathItem_Click(object sender, RoutedEventArgs e) {
var depObj = sender as DependencyObject;
while (!(depObj is ComboBoxItem)) {
if (depObj == null) return;
depObj = VisualTreeHelper.GetParent(depObj);
}
var comboBoxItem = depObj as ComboBoxItem;
var item = comboBoxItem.Content as RemotePathItem;
_remotePathsList.Remove(item);
RemotePathComboBox_SelectIndexWithoutChangingList(0);
}
(The "Icon Extractor" that fetches the icon from the system's DLL is from an old post of mine)
You can also do it this way:
<Window x:Class="RemoveItemsFromComboBox.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>
<ComboBox x:Name="CbxItems" VerticalAlignment="Top" HorizontalAlignment="Left" Width="250">
<ComboBox.ContextMenu>
<ContextMenu>
<MenuItem x:Name="MenuItem" Header="Delete" Click="MenuItem_OnClick"></MenuItem>
</ContextMenu>
</ComboBox.ContextMenu>
<TextBlock Text="Item 1"/>
<TextBlock Text="Item 2"/>
<TextBlock Text="Item 3"/>
<TextBlock Text="Item 4"/>
</ComboBox>
</Grid>
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
CbxItems.PreviewMouseRightButtonDown += OnPreviewMouseRightButtonDown;
}
private void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var comboBoxItem = VisualUpwardSearch(e.OriginalSource as DependencyObject);
if (comboBoxItem == null) return;
comboBoxItem.IsSelected = true;
e.Handled = true;
}
private ComboBoxItem VisualUpwardSearch(DependencyObject source)
{
while (source != null && !(source is ComboBoxItem))
source = VisualTreeHelper.GetParent(source);
return source as ComboBoxItem;
}
private void MenuItem_OnClick(object sender, RoutedEventArgs e)
{
CbxItems.Items.Remove(CbxItems.SelectedItem);
}
}
Put that ContextMenu for each ComboBoxItem instead of the ComboBox itself :
<ComboBoxItem.ContextMenu>
<ContextMenu>
<MenuItem Header="Remove" Click="MenuItem_OnClick"></MenuItem>
</ContextMenu>
</ComboBoxItem.ContextMenu>
You can also put that in DataTemplate or generate it from code behind, depending on how you populate the ComboBox. Then in menu item's click event handler you can do as follow to get user chosen ComboBoxItem :
private void MenuItem_OnClick(object sender, RoutedEventArgs e)
{
var menuItem = (MenuItem)sender;
var ctxMenu = (ContextMenu)menuItem.Parent;
var comboBoxItem = (ComboBoxItem) ctxMenu.PlacementTarget;
}
For locating the combobox items, you can use checkbox in the item template of the combobox so that user can check the items which he/she wants to delete.
If your combobox is data bound, then you will have to filter the datasource of your combobox i.e. on context menu click you will have to delete the items checked by user from the datasource of your combobox and then re-bind the combobox with datasource.
If you don't have a data bound combobox, then on context menu click simply loop through the combobox items and delete the items which are checked by user.

Categories