Skip to content

Instantly share code, notes, and snippets.

@gekka
Created February 23, 2023 07:53
Show Gist options
  • Save gekka/e828782889e5c74553af54b1db1cf8dd to your computer and use it in GitHub Desktop.
Save gekka/e828782889e5c74553af54b1db1cf8dd to your computer and use it in GitHub Desktop.
<Window x:Class="WpfApp1.MainWindow"
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:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="200" Width="300">
<Window.Resources>
<!-- 添付プロパティでスタイルを指定していない場合のMenuItem -->
<Style TargetType="MenuItem">
<Setter Property="Background" Value="LightBlue" />
</Style>
</Window.Resources>
<StackPanel>
<!-- Popupを捕まえるイベントを用意して、TextBoxの標準のContextMenuをいじくる -->
<TextBox local:PopupTool.PopupLoaded="Popup_Loaded" Margin="5">
<!-- ContextMenuに存在するMenuItemのスタイルを添付プロパティで追加指定できるように -->
<local:PopupTool.ContextMenuItemStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Background" Value="LightPink" />
</Style>
</local:PopupTool.ContextMenuItemStyle>
</TextBox>
<!-- を添付プロパティで追加指定できるようにしない場合はリソースからMenuItem用のスタイルを適用 -->
<TextBox Margin="5"/>
<!-- RichTextBoxでも -->
<RichTextBox local:PopupTool.PopupLoaded="Popup_Loaded" Margin="5" />
</StackPanel>
</Window>
using System;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
namespace WpfApp1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Popup_Loaded(object sender, RoutedEventArgs e)
{
//TextBoxが標準で出すContextMenuにMenuItemを追加
//IMEの項目などが追加されて普通の方法では出来ないので
if (e.OriginalSource is System.Windows.Controls.Primitives.Popup popup)
{
if (popup.Child is ContextMenu cm)
{
cm.Items.Add(new Separator());
MenuItem mi = new MenuItem() { Header = "てすと" };
for (int i = 1; i <= 2; i++)
{
MenuItem subItem = new MenuItem() { Header = "さぶめにゅー" + i.ToString() };
mi.Items.Add(subItem);
}
cm.Items.Add(mi);
}
}
}
}
}
namespace WpfApp1
{
public class PopupTool
{
static PopupTool()
{
// app.xamlのリソースに以下のスタイルを追加することと同じ
// <Style TargetType="Popup">
// <Setter Property="local:PopupTool.Hook" Value="true" />
// </Style>
Style style = new Style(typeof(Popup));
style.Setters.Add(new Setter(PopupTool.HookProperty, true));
Application.Current.Resources.Add(style.TargetType, style);
}
#region Popupが表示されるときを検出させるための添付プロパティ
//public static void SetHook(Popup obj, bool value) { obj.SetValue(HookProperty, value); }
private static readonly DependencyProperty HookProperty
= DependencyProperty.RegisterAttached("Hook", typeof(bool), typeof(PopupTool), new PropertyMetadata(false, OnPopupLoading));
private static void OnPopupLoading(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
// 添付プロパティの値がSetterで設定されるタイミングを検出
var p = (Popup)s;
var re = new RoutedEventArgs(PopupLoadedEvent, p);
var placementTarget = p.PlacementTarget;
if (placementTarget != null)
{
var cm = p.Child as ContextMenu;
var styleContextMenu = GetContextMenuStyle(placementTarget);
if (styleContextMenu != null && styleContextMenu.TargetType.IsAssignableFrom(typeof(ContextMenu)))
{
p.Resources[styleContextMenu.TargetType] = styleContextMenu;
cm.Style = styleContextMenu;
}
var styleMenuItem = GetContextMenuItemStyle(placementTarget);
if (styleMenuItem == null && p.PlacementTarget is FrameworkElement fe)
{
//添付プロパティでMenuItemのスタイルが指定されていないならリソースからスタイルを探す
styleMenuItem = fe.TryFindResource(typeof(MenuItem)) as Style;
}
if (styleMenuItem != null && styleMenuItem.TargetType.IsAssignableFrom(typeof(MenuItem)))
{
p.Resources[styleMenuItem.TargetType] = styleMenuItem;
//MenuItemを継承していてスタイルが自動的に適用されないMemuItemにスタイルを適用する
foreach (var item in cm.Items.OfType<MenuItem>())
{
item.Style = styleMenuItem;
}
}
//Popupが表示される前にRoutedEventを発生させる
p.PlacementTarget.RaiseEvent(re);
}
}
#endregion
#region Popupの表示されるときを通知するイベント
public static readonly RoutedEvent PopupLoadedEvent
= EventManager.RegisterRoutedEvent
("PopupLoaded", RoutingStrategy.Bubble, typeof(RoutedEventHandler)
, typeof(System.Windows.DependencyObject));
public static void AddPopupLoadedHandler(DependencyObject element, RoutedEventHandler handler)
{
if (!(element is UIElement uiElement))
{
return;
}
uiElement.AddHandler(PopupLoadedEvent, handler);
}
public static void RemovePopupLoadedHandler(DependencyObject element, RoutedEventHandler handler)
{
if (!(element is UIElement uiElement))
{
return;
}
uiElement.RemoveHandler(PopupLoadedEvent, handler);
}
#endregion
#region Popupに表示されるContextMenuに適用するスタイルを指定するための添付プロパティ
public static Style GetContextMenuStyle(DependencyObject obj) { return (Style)obj.GetValue(ContextMenuStyleProperty); }
public static void SetContextMenuStyle(DependencyObject obj, Style value) { obj.SetValue(ContextMenuStyleProperty, value); }
public static readonly DependencyProperty ContextMenuStyleProperty
= DependencyProperty.RegisterAttached("ContextMenuStyle", typeof(Style), typeof(PopupTool), new PropertyMetadata(null));
public static Style GetContextMenuItemStyle(DependencyObject obj) { return (Style)obj.GetValue(ContextMenuItemStyleProperty); }
public static void SetContextMenuItemStyle(DependencyObject obj, Style value) { obj.SetValue(ContextMenuItemStyleProperty, value); }
public static readonly DependencyProperty ContextMenuItemStyleProperty
= DependencyProperty.RegisterAttached("ContextMenuItemStyle", typeof(Style), typeof(PopupTool), new PropertyMetadata(null));
#endregion
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment