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";
}
Related
I have a menu (with menuitems) in WPF. Unfortunately when I click on the menu heading it opens the menu to the right. The problem is that there is stuff on the right that I don't want it to overlap. How do I tell WPF to open the menu to the left? Do I need to do a control template? (control templates seem so heavy handed for such basic style changes).
Thanks!
KSG
While you can create a ControlTemplate to do this like they do here, I agree that it is a cumbersome method just to modify one value on a part of the MenuItems. Instead, I think that this is a great place to use an AttachedProperty. We can create something just like the ContextMenuService, but for Popups (In fact, I'm somewhat surprised that it isn't built in).
To change where the popup is opening, we're going to want to set the Popup's PlacementMode. We can use the propa shortcut to generate our AttachedProperty(or properties if you want to implement the rest). We need to add a callback to our PropertyMetadata, but if the AttachedProperty is set inline on the control in XAML then the callback will fire before the whole control is fully constructed. To ensure the MenuItem's template is applied, and the Popup exists before we try and set it's value, we can just attach to the Loaded event if it isn't already loaded.
Once it is loaded, we want to retrieve the Popup from the template, and if we look at the MenuItem class we can see that it has a TemplatePartAttribute defining the Popup's name as "PART_Popup". Once we have that, we can set the PlacementMode on the MenuItem's Popup.
public static PlacementMode GetMenuPlacement(DependencyObject obj)
{
return (PlacementMode)obj.GetValue(MenuPlacementProperty);
}
public static void SetMenuPlacement(DependencyObject obj, PlacementMode value)
{
obj.SetValue(MenuPlacementProperty, value);
}
// Using a DependencyProperty as the backing store for MenuPlacement. This enables animation, styling, binding, etc...
public static readonly DependencyProperty MenuPlacementProperty =
DependencyProperty.RegisterAttached("MenuPlacement",
typeof(PlacementMode),
typeof(Window1),
new FrameworkPropertyMetadata(PlacementMode.Bottom, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnMenuPlacementChanged)));
private static void OnMenuPlacementChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
var menuItem = o as MenuItem;
if (menuItem != null)
{
if (menuItem.IsLoaded)
{
SetPopupPlacement(menuItem, (PlacementMode)e.NewValue);
}
else
{
menuItem.Loaded += new RoutedEventHandler((m, v) => SetPopupPlacement(menuItem, (PlacementMode)e.NewValue));
}
}
}
private static void SetPopupPlacement(MenuItem menuItem, PlacementMode placementMode)
{
Popup popup = menuItem.Template.FindName("PART_Popup", menuItem) as Popup;
if (popup != null)
{
popup.Placement = placementMode;
}
}
Now that we have our AttachedProperty, it's easy to change the Popup placement in the UI.
<Menu>
<MenuItem Header="Item 1"
local:Window1.MenuPlacement="Right">
<MenuItem Header="SubItem 1" />
<MenuItem Header="SubItem 2" />
<MenuItem Header="SubItem 3" />
<MenuItem Header="SubItem 4" />
</MenuItem>
<MenuItem Header="Item 2"
local:Window1.MenuPlacement="Left">
<MenuItem Header="SubItem 5" />
<MenuItem Header="SubItem 6" />
<MenuItem Header="SubItem 7" />
<MenuItem Header="SubItem 8" />
</MenuItem>
<MenuItem Header="Item 3"
local:Window1.MenuPlacement="Mouse">
<MenuItem Header="SubItem 9" />
<MenuItem Header="SubItem 10" />
<MenuItem Header="SubItem 11" />
<MenuItem Header="SubItem 12" />
</MenuItem>
</Menu>
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
I have a WPF code like this:
<ListBox SelectedItem="{Binding SelectedItem}">
<ListBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete" Click="MenuItem_Delete_Click" />
<MenuItem Header="Replace" Click="MenuItem_Replace_Click">
<ListBox SelectionMode="Single" SelectedItem="{Binding ReplaceItem}" />
</MenuItem>
<MenuItem Header="Insert" Click="MenuItem_Insert_Click">
<ListBox SelectionMode="Single" SelectedItem="{Binding InsertItem}}" />
</MenuItem>
</ListBox>
But this goes like follows:
When Mouse leave
When Mouse On
So how should I fix this?Thanks in advance!
Instead of using a listbox on the MenuItem, why don't you try to add menuitems to the existing menuitem (for your case is "Insert" and "Replace")
Let"s see my example.
MenuItem mi = new MenuItem();
mi.Header = "PX1-20T-D-B";
NameOfYourMenuItem.Items.Add(mi);
mi.Click += new RoutedEventHandler(MenuItemClick);
You can also add the eventHandler for each one of them.
Instead of using a ListBox in the MenuItem or adding sub menu items in code behind, try using DataTemplates. I assume that you have a collection containing your list box items.
<MenuItem Header="Replace" Click="MenuItem_Replace_Click" ItemsSource="{Binding ReplaceItemsCollection}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding}" Click="replaceSubMenuItem_Clicked"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
<MenuItem Header="Replace" Click="MenuItem_Replace_Click" ItemsSource="{Binding InsertItemsCollection}">
<MenuItem.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding}" Click="insertSubMenuItem_Clicked"/>
</DataTemplate>
</MenuItem.ItemTemplate>
</MenuItem>
In your code behind add:
private void replaceSubMenuItem_Clicked(object sender, EventArgs e)
{
// sender is the MenuItem. Just parse it.
}
private void insertSubMenuItem_Clicked(object sender, EventArgs e)
{
// ...
}
I have successfully created a drop-down list in the XAML using a
.XAML
<Grid>
<DockPanel>
<Menu>
<MenuItem Header="Menu">
<MenuItem Header="{Binding Path=ddlXferData1}" Command="{Binding Path=XferData}" Click="MenuItem_Click" IsCheckable="True"/>
<MenuItem Header="{Binding Path=ddlXferData2}" Command="{Binding Path=XferData}" Click="MenuItem_Click" IsCheckable="True"/>
<MenuItem Header="{Binding Path=ddlXferData3}" Command="{Binding Path=XferData}" Click="MenuItem_Click" IsCheckable="True"/>
<Separator />
<MenuItem Header="{Binding Path=ddlSkillData1}" Command="{Binding Path=SkillData}" Click="MenuItem_Click" IsCheckable="True"/>
<MenuItem Header="{Binding Path=ddlSkillData2}" Command="{Binding Path=SkillData}" Click="MenuItem_Click" IsCheckable="True"/>
<MenuItem Header="{Binding Path=ddlSkillData3}" Command="{Binding Path=SkillData}" Click="MenuItem_Click" IsCheckable="True"/>
</MenuItem>
</Menu>
</DockPanel>
</Grid>
.xaml.cs
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
MenuItem mnItem = (MenuItem) e.OriginalSource;
this.Model.menuItemSelected = (string) mnItem.Header;
}
From here the Commands XferData and SkillData are used to reference the event handler method that basically does something depending on the this.Model.menuItemSelected
All of this is working properly and the way I need it to work.
However, now I need the Menu (drop-down list) options to be build or created dynamically.
I am not 100% sure what is the best course of action to design and implement a dynamic drop-down list:
Should I put a in the .xaml
<Button Name="MenuButton" Content="{Binding Path=btnMenu}" Command="{Binding Path=Menu}" Click="button1_Click" .../>
and in the .xaml.cs build the Menu with options
private void button1_Click(object sender, RoutedEventArgs e)
{
// Make the main menu.
Menu mainMenu = new Menu();
DockPanel dockPanel = new DockPanel();
dockPanel.Children.Add(mainMenu);
mainMenu.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
mainMenu.VerticalAlignment = System.Windows.VerticalAlignment.Top;
// Make the menu items.
MenuItem menuItem = new MenuItem();
.....
}
or
Should I dynamically build the XAML?
it's not recommended but you can do it like this :
//assuming you have Menu named MainMenu and you want to add Menu Item to it and Sub menu item to the item which you have just added
MenuItem MainItem = new MenuItem();
MainItem .Header = "Main Item";
this.MainMenu.Items.Add(MainItem);
MenuItem SubItem= new MenuItem();
SubItem.Header = "Sub Item";
SubItem.Click += new RoutedEventHandler(SubItem_Click);
MainItem.Items.Add(SubItem);
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);
}
}