The framework actually tracks that value in the PopupControlService.Owner property, but it's an internal class. If you're willing to use undocumented features, you could iterate over the properties on the ContextMenu and pull it out:
private static object GetOwner(ContextMenu menu)
{
var prop = menu.GetLocalValueEnumerator();
while (prop.MoveNext())
{
if (prop.Current.Property.Name == "Owner" &&
prop.Current.Property.OwnerType.Name == "PopupControlService")
{
return prop.Current.Value;
}
}
return null;
}
The other approach is that ContextMenuService.ContextMenuOpeningEvent will be raised by the Hyperlink, so you could take the sender parameter and put it in your own attached property. Something like this:
public class ContextMenuOwnerTracker
{
private static bool isInitialized;
public static void Initialize()
{
if (!isInitialized)
{
isInitialized = true;
EventManager.RegisterClassHandler(typeof(ContentElement),
ContextMenuService.ContextMenuOpeningEvent,
new ContextMenuEventHandler(OnContextMenuOpening));
EventManager.RegisterClassHandler(typeof(ContentElement),
ContextMenuService.ContextMenuClosingEvent,
new ContextMenuEventHandler(OnContextMenuClosing));
}
}
private static void OnContextMenuOpening
(object sender, ContextMenuEventArgs args)
{
var menu = ContextMenuService.GetContextMenu((DependencyObject)sender);
if (menu != null)
{
SetOwner(menu, sender);
}
}
private static void OnContextMenuClosing
(object sender, ContextMenuEventArgs args)
{
var menu = ContextMenuService.GetContextMenu((DependencyObject)sender);
if (menu != null)
{
ClearOwner(menu);
}
}
private static readonly DependencyPropertyKey OwnerKey =
DependencyProperty.RegisterAttachedReadOnly(
"Owner",
typeof(object),
typeof(ContextMenuOwnerTracker),
new PropertyMetadata(null));
public static readonly DependencyProperty OwnerProperty =
OwnerKey.DependencyProperty;
public static object GetOwner(ContextMenu element)
{
return element.GetValue(OwnerProperty);
}
private static void SetOwner(ContextMenu element, object value)
{
element.SetValue(OwnerKey, value);
}
private static void ClearOwner(ContextMenu element)
{
element.ClearValue(OwnerKey);
}
}
Note that there may be multiple ContentElements with ContextMenu properties and this will actually set Owner on all of them, even though it only matters for the one that is actually displayed.
To get the value for a specific MenuItem, you will have to walk up the tree or use a binding with {RelativeSource AncestorType=ContextMenu}
. You could also mark the Owner property inherited to have it propagate automatically to the MenuItems.
If you attach the context menu at a higher level than the Hyperlink, you can use OriginalSource instead of sender, but that will normally give you a Run so you'll have to walk up the tree to find a Hyperlink.