Dynamically add radiobuttons in submenu in C# WPF - c#

I would like to implement a dynamical menu in my application, written in C# and using WPF.
MenuSubSerialPortSelect.Items.Clear();
foreach (string element in GlobalVars.ActualSerialPorts)
{
MenuItem item = new MenuItem();
item.Header = element;
MenuSubSerialPortSelect.Items.Add(item);
}
The sub-menu adding is functional, but how can I add some radio buttons in this style?
I've read some websites that described that there must be used another "template", but I can't find a matched solution for me.
The attribute item.IsCheckable = true; is not a solution for me -- the entries must be blocked against each other.
It would be great if somebody could give me a tip on how to do that.

Try this
<Window x:Class="Stackoverflow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Stackoverflow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<RadioButton x:Key="RadioButton" HorizontalAlignment="Center"
GroupName="MyGroup" IsHitTestVisible="False"/>
<Style TargetType="MenuItem">
<Setter Property="Icon" Value="{StaticResource RadioButton}"/>
<EventSetter Event="Click" Handler="MenuItem_Click" />
</Style>
</Window.Resources>
<Grid Background="Red">
<Grid.ContextMenu>
<ContextMenu>
<MenuItem Header="Print"/>
<MenuItem Header="Save"/>
<MenuItem Header="Open"/>
<MenuItem Header="Delete"/>
<MenuItem Header="Update"/>
</ContextMenu>
</Grid.ContextMenu>
</Grid>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
private void MenuItem_Click(object sender, System.Windows.RoutedEventArgs e)
{
MenuItem menuItem = sender as MenuItem;
if (menuItem != null)
{
RadioButton rb = menuItem.Icon as RadioButton;
if (rb != null)
{
rb.IsChecked = true;
}
}
}
}
Here i just have static menuitems.

Related

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

ContextMenuOpening when opening a ContextMenu from a ListViewItem

I have a ListView with some data and an ItemContainerStyle to bind the ContextMenu to every item (so it only appears when clicking on an item in the ListView)
I've got the ContextMenu defined in ListView.Resources. How do I properly fire the ContextMenuOpening event and then access the ContextMenu in the ContextMenuEventArgs in said event?
ListView.Resources>
<ContextMenu x:Key="ItemContextMenu" FontFamily="Verdana" FontSize="14">
//Rest of the ContextMenu
<MenuItem Header="Custom Actions">
</MenuItem>
</ContextMenu>
</ListView.Resources>
Right now, I tried to have an Event Setter in the style,
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="ContextMenu" Value="{StaticResource ItemContextMenu}" />
<EventSetter Event="ContextMenuOpening" Handler="ContextMenu_ContextMenuOpening" />
</ListView.ItemContainerStyle>
but this triggers a NullPointerException here in the C#-Code (which is basically copied from https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/how-to-handle-the-contextmenuopening-event)
private void ContextMenu_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
FrameworkElement fe = e.Source as FrameworkElement;
ContextMenu cm = fe.ContextMenu;
List<MenuItem> customActions = new List<MenuItem>();
customActions.Add(new MenuItem {Header = "First Thing"});
customActions.Add(new MenuItem {Header = "Second Thing"});
foreach (MenuItem mi in cm.Items) // here is the NullPointerException
{
if((String)mi.Header == "Custom Actions")
{
foreach(MenuItem cmi in customActions)
{
mi.Items.Add(cmi);
}
}
}
}
How can I access the ContextMenu and modify it before opening?

Open Buttons ContextMenu on Left Click

I have a button with a context menu, my requirements are to show the context menu on left click
The problem is that the <ContextMenu ItemsSource="{Binding LineItems}" isn't being updated/refresh when the context menu opens. However If i right click first, items are loaded fine
XAML
<Button x:Name="BtnMessageChannel" Click="BtnMessageChannel_Click" Grid.Row="0" Grid.Column="2" Height="23" Width="23" ToolTip="Message Channel" >
<Button.ContextMenu>
<ContextMenu ItemsSource="{Binding LineItems}" x:Name="CtxMessageChannel">
<ContextMenu.Resources>
<Image x:Key="img" Source="{Binding Icon}" x:Shared="false"/>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Header" Value="{Binding DisplayName}"/>
<Setter Property="Icon" Value="{StaticResource img}">
</Setter>
</Style>
</ContextMenu.Resources>
</ContextMenu>
</Button.ContextMenu>
<Image Source="Images/mail_send.png" HorizontalAlignment="Left" Width="16" />
</Button>
Code Behind
private void BtnMessageChannel_Click(object sender, RoutedEventArgs e)
{
BtnMessageChannel.ContextMenu.GetBindingExpression(ContextMenu.ItemsSourceProperty)
.UpdateTarget();
BtnMessageChannel.ContextMenu.Visibility = Visibility.Visible;
BtnMessageChannel.ContextMenu.IsOpen = true;
}
Is there any easy solutions to this problem?
An easy solution is to update your button event handler to simulate a right click if the context menu is not currently open.
private void BtnMessageChannel_Click(object sender, RoutedEventArgs e)
{
if (!BtnMessageChannel.ContextMenu.IsOpen)
{
e.Handled = true;
var mouseRightClickEvent = new MouseButtonEventArgs(Mouse.PrimaryDevice, Environment.TickCount, MouseButton.Right)
{
RoutedEvent = Mouse.MouseUpEvent,
Source = sender,
};
InputManager.Current.ProcessInput(mouseRightClickEvent);
}
}

How to change context menu item's style if command is disabled

I have a context menu and at certain point i disable some of the commands bound to context menu items.
what works:
when the command gets disabled , clicking on the menu item does not call the execute method on the command as i wanted.
What does not work:
Both commands that can exucute and those which cant look identical!!
my users cant tell the difference.
Problem:
How can I change the style to display this change. Change in background color, border color, foreground color , a tooltip... anything will be acceptable..
I experimented with xaml and was unable to solve this.
(I am a noob with xaml so my attempts are not even worth pasting here :) )
xaml :
<Border BorderThickness="1" MinWidth="100" Background="Transparent" ContextMenu="{Binding Path=ContextMenu}" BorderBrush="{Binding Path=BorderColor}">
Binding:
public override ContextMenu ContextMenu
{
get
{
return new ContextMenu
{
ItemsSource = new ObservableCollection<MenuItem>
{
new MenuItem
{
Header = IsSharedFieldView? "Delete Shared Field" :"Delete Field" ,
Command = DeleteFieldCommand
}
}
};
}
}
The trick here, is that ContextMenu actually inherits from ItemsControl, this works on my machine:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.DataContext = this;
InitializeComponent();
MenuItems = new ObservableCollection<KeyValuePair<string, ICommand>>();
MenuItems.Add(new KeyValuePair<string, ICommand>("One", OneCommand));
MenuItems.Add(new KeyValuePair<string, ICommand>("Two", null));
}
public ObservableCollection<KeyValuePair<String, ICommand>> MenuItems { get; set; }
#region OneCommand
DelegateCommand _OneCommand;
public DelegateCommand OneCommand
{
get { return _OneCommand ?? (_OneCommand = new DelegateCommand(One, CanOne)); }
}
public bool CanOne()
{
return false;
}
public void One()
{
}
#endregion
}
And the XAML:
<Window x:Class="DynamicContextMenuTest.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>
<Path Data="M 10,2 L 17.5,2 L 17.5,0 L 23,4.5 L 17.5,9 L 17.5,7.3 L 10,7.3 L 10,2" Fill="Green">
<Path.ContextMenu>
<ContextMenu ItemsSource="{Binding MenuItems}" DisplayMemberPath="Key">
<ContextMenu.Resources>
<Style TargetType="MenuItem">
<Setter Property="Command" Value="{Binding Value}" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</ContextMenu.Resources>
</ContextMenu>
</Path.ContextMenu>
</Path>
</Grid>
</Window>
Notice a few things:
You no longer bind to a ContextMenu (viewmodels shouldn't be aware of controls), instead you bind to a collection of string(Header)/ICommand(Command)
When an item's command's CanExecute returns false, the bound control's IsEnabled property becomes false. You can have a trigger on that property to modify the appearance of the bound control.
<Window.Resources>
<Style TargetType="{x:Type MenuItem}">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<TextBox >
<TextBox.ContextMenu>
<ContextMenu>
<Menu>
<MenuItem Header="Add" IsEnabled="False"/>
<MenuItem Header="Delete"/>
</Menu>
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</Grid>
Its just demo and you can have idea from this . and you can bind IsEnabled to your VM Command.CanExecute . I hope this will help.
Solution that worked :
Initially i was binding a ContextMenu from my view model and the isEnabled was not working.
Instead of that I created the context menu in xaml and bound the itemsSource from the viewModel.
Now the menu item gets disabled and the triggers are working.
Not sure what i was doing wrong but this fixed it :)

Letting users customize content property of user control at run-time

This is the required behaviour:
I have various controls present on the canvas e.g. Callouts (from Expression Blend .dll), or simple Labels. When the user 'double clicks' (or any other event I decide to tie in) the control should change its appearance to allow the user to edit the control's Content property. Clicking off the control should then turn it back to 'read-only' method.
Any suggestions on how this would be best achieved? Ideally I want to do this all in c# to add this behaviour to the control at runtime (as this control is added dynamically to the canvas)- and avoid XAML altogether.
I reckon I've got to do something with adorners to display a textbox bound to the control's content property on the required event, but some code samples or links elsewhere would be appreciated? :) - I haven't been able to find anything on an existing search, but I reckon it should be fairly simple.
Unfortunately, style triggers do not do anything with IsReadOnly and IsEnabled. You would have to do that from an event.
Here is my sample:
WPF:
<Window x:Class="StateChangingTextbox.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">
<Window.Resources>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#eee" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBox Width="300" Height="200" TextWrapping="Wrap" IsReadOnly="True"
MouseEnter="TextBox_MouseEnter"
MouseLeave="TextBox_MouseLeave"/>
</Grid>
</Window>
Code-behind:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void TextBox_MouseEnter(object sender, MouseEventArgs e)
{
var textbox = sender as TextBox;
if (textbox != null)
{
textbox.IsReadOnly = false;
}
}
private void TextBox_MouseLeave(object sender, MouseEventArgs e)
{
var textbox = sender as TextBox;
if (textbox != null)
{
textbox.IsReadOnly = true;
}
}
}
XAML:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions"
mc:Ignorable="d"
x:Class="ComicWPF.Bubble"
x:Name="UserControl" Height="100" Width="200">
<Canvas LostFocus="this_LostFocus">
<ed:Callout x:Name="callout" Content=""
AnchorPoint="0,1" FontSize="14" Height="100" Width="200"
Fill="Blue"
PreviewMouseDoubleClick="Callout_DoubleClick"
Canvas.Left="0" Canvas.Top="0" />
<TextBox x:Name="textbox"
FontSize="14"
Canvas.Left="30" Height="55" Width="80" Canvas.Top="30"
Visibility="Visible"/>
</Canvas>
</UserControl>
C# Code:
private void Callout_DoubleClick(object sender, MouseButtonEventArgs e)
{
Activate();
}
public void Activate()
{
//set bool activated to true
//make textbox visible and set focus and select all text
}
private void Callout_DeSelect()
{
//set content of callout to the textbox.Text
//Hide textbox
//set bool activated to false
}
private void this_LostFocus(object sender, RoutedEventArgs e)
{
Callout_DeSelect();
}
}

Categories