Since there is not a samilar answer, I post my solution here:
public class RadioMenuItem : MenuItem
{
public string GroupName { get; set; }
protected override void OnClick()
{
var ic = Parent as ItemsControl;
if (null != ic)
{
var rmi = ic.Items.OfType<RadioMenuItem>().FirstOrDefault(i =>
i.GroupName == GroupName && i.IsChecked);
if (null != rmi) rmi.IsChecked = false;
IsChecked = true;
}
base.OnClick();
}
}
In XAML just use it as an usual MenuItem:
<MenuItem Header="OOO">
<local:RadioMenuItem Header="111" GroupName="G1"/>
<local:RadioMenuItem Header="222" GroupName="G1"/>
<local:RadioMenuItem Header="333" GroupName="G1"/>
<local:RadioMenuItem Header="444" GroupName="G1"/>
<local:RadioMenuItem Header="555" GroupName="G1"/>
<local:RadioMenuItem Header="666" GroupName="G1"/>
<Separator/>
<local:RadioMenuItem Header="111" GroupName="G2"/>
<local:RadioMenuItem Header="222" GroupName="G2"/>
<local:RadioMenuItem Header="333" GroupName="G2"/>
<local:RadioMenuItem Header="444" GroupName="G2"/>
<local:RadioMenuItem Header="555" GroupName="G2"/>
<local:RadioMenuItem Header="666" GroupName="G2"/>
</MenuItem>
Quite simple and clean. And of course you can make the GroupName
a dependency property by some additional codes, that's all the same as others.
BTW, if you do not like the check mark, you can change it to whatever you like:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var p = GetTemplateChild("Glyph") as Path;
if (null == p) return;
var x = p.Width/2;
var y = p.Height/2;
var r = Math.Min(x, y) - 1;
var e = new EllipseGeometry(new Point(x,y), r, r);
// this is just a flattened dot, of course you can draw
// something else, e.g. a star? ;)
p.Data = e.GetFlattenedPathGeometry();
}
If you used plenty of this RadioMenuItem
in your program, there is another more efficient version shown below. The literal data is aquired from e.GetFlattenedPathGeometry().ToString()
in previous code snippet.
private static readonly Geometry RadioDot = Geometry.Parse("M9,5.5L8.7,7.1 7.8,8.3 6.6,9.2L5,9.5L3.4,9.2 2.2,8.3 1.3,7.1L1,5.5L1.3,3.9 2.2,2.7 3.4,1.8L5,1.5L6.6,1.8 7.8,2.7 8.7,3.9L9,5.5z");
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var p = GetTemplateChild("Glyph") as Path;
if (null == p) return;
p.Data = RadioDot;
}
And at last, if you plan to wrap it for use in your project, you should hide IsCheckable
property from the base class, since the auto check machenism of MenuItem
class will lead the radio check state mark a wrong behavior.
private new bool IsCheckable { get; }
Thus VS will give an error if a newbie try to compile XAML like this:
// note that this is a wrong usage!
<local:RadioMenuItem Header="111" GroupName="G1" IsCheckable="True"/>
// note that this is a wrong usage!