[英]How to not close a xtk:SplitButton's custom Popup when a click is made on a button inside it?
I have a custom control with an override for OnApplyTemplate. 我有一个自定义控件,其中包含OnApplyTemplate的重写。 In it I tried to access children of children templates but they seem to not be loaded.
在其中,我尝试访问子模板的子模板,但似乎未加载它们。 What I wish: when the
PART_IncreaseButton
inside the Popup
of a xtk:SplitButton
is clicked, the Popup
will not close but just let the Button
react to the click. 我想什么:当该
PART_IncreaseButton
内侧Popup
一个的xtk:SplitButton
被点击, Popup
不会接近,但只是让Button
反应的点击。
CustomIntegerUpDown
and CustomSplitButton
are derived from the Xceed Extended WPF Toolkit. CustomIntegerUpDown
和CustomSplitButton
源自Xceed Extended WPF Toolkit。 CustomIntegerUpDown has no changed style or template or code-behind and currently its only purpose is to do what I said above but I am just at the beginning of it. CustomIntegerUpDown的样式,模板或代码均未更改,目前,其唯一目的是执行我在上面所说的内容,但我只是刚开始。 Below is all the relevant source.
以下是所有相关来源。
I tried this: 我尝试了这个:
IncrementButton = Utils.FindChild<RepeatButton>(PartPopup, "PART_IncreaseButton")
and IncrementButton is null after that, although in the Immediate Window: 之后,IncrementButton为null,尽管在立即窗口中:
Utils.FindChild<Popup>(this, "PART_Popup")
returns the Popup
as obtained from GetTemplateChild("PART_Popup")
. Utils.FindChild<Popup>(this, "PART_Popup")
返回从GetTemplateChild("PART_Popup")
获得的Popup
GetTemplateChild("PART_Popup")
。
then 然后
Utils.FindChild<ButtonSpinner>(PartPopup, "PART_Spinner")
returns null
. Utils.FindChild<ButtonSpinner>(PartPopup, "PART_Spinner")
返回null
。
Utils.FindChild<CustomIntegerUpDown>(PartPopup, "MyCustomIntegerUpDown")
return null
. Utils.FindChild<CustomIntegerUpDown>(PartPopup, "MyCustomIntegerUpDown")
返回null
。
VisualTreeHelper.GetChildrenCount(PartPopup)
returns 0
. VisualTreeHelper.GetChildrenCount(PartPopup)
返回0
。
PartPopup.ApplyTemplate()
returns false
. PartPopup.ApplyTemplate()
返回false
。
I've also seen this and I am not sure if it is worth trying that way. 我也看到了这一点 ,但不确定是否值得尝试这种方式。
FindChild
is this (took from here ): FindChild
是这个(从这里获取 ):
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
public static T FindChild<T>(System.Windows.DependencyObject parent, string childName)
where T : System.Windows.DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = System.Windows.Media.VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = System.Windows.Media.VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as System.Windows.FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
In CustomSplitButton.xaml.cs I have this: 在CustomSplitButton.xaml.cs中,我具有以下内容:
internal Popup PartPopup;
internal Button PartButtonWith1, PartButtonWith5, PartButtonWith10, PartButtonWithCustom;
internal RepeatButton IncrementButton;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
PartPopup = (Popup)GetTemplateChild("PART_Popup");
PartButtonWith1 = (Button)GetTemplateChild("PART_ButtonWith1");
PartButtonWith5 = (Button)GetTemplateChild("PART_ButtonWith5");
PartButtonWith10 = (Button)GetTemplateChild("PART_ButtonWith10");
PartButtonWithCustom = (Button)GetTemplateChild("PART_ButtonWithCustom");
PartPopup.ApplyTemplate();
IncrementButton = Utils.FindChild<RepeatButton>(PartPopup, "PART_IncreaseButton");
if (PartPopup != null)
{
PartPopup.PreviewMouseDown += PART_Popup_PreviewMouseUp;
PartPopup.PreviewMouseUp += PART_Popup_PreviewMouseUp;
}
if (PartButtonWith1 != null)
{
PartButtonWith1.Click += Btns_NewTimer_Click;
}
if (PartButtonWith5 != null)
{
PartButtonWith5.Click += Btns_NewTimer_Click;
}
if (PartButtonWith10 != null)
{
PartButtonWith10.Click += Btns_NewTimer_Click;
}
if (PartButtonWithCustom != null)
{
PartButtonWithCustom.Click += BtnCustom_Click;
}
}
The visual tree is this: 视觉树是这样的:
The style of CustomSplitButton is the following ( xmlns:xtkThemes="clr-namespace:Xceed.Wpf.Toolkit.Themes;assembly=Xceed.Wpf.Toolkit"
): CustomSplitButton的样式如下(
xmlns:xtkThemes="clr-namespace:Xceed.Wpf.Toolkit.Themes;assembly=Xceed.Wpf.Toolkit"
):
<Style x:Key="AddCountSplitButtonStyle" TargetType="{x:Type xtk:SplitButton}">
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="IsTabStop" Value="False"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Background" Value="{DynamicResource {ComponentResourceKey ResourceId=ButtonNormalBackgroundKey, TypeInTargetAssembly={x:Type xtkThemes:ResourceKeys}}}"/>
<Setter Property="BorderBrush" Value="{DynamicResource {ComponentResourceKey ResourceId=ButtonNormalOuterBorderKey, TypeInTargetAssembly={x:Type xtkThemes:ResourceKeys}}}"/>
<Setter Property="DropDownContentBackground">
<Setter.Value>
<LinearGradientBrush EndPoint="0,1" StartPoint="0,0">
<GradientStop Color="#FFF0F0F0" Offset="0"/>
<GradientStop Color="#FFE5E5E5" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Padding" Value="3"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type xtk:SplitButton}">
<Grid x:Name="MainGrid" SnapsToDevicePixels="True">
<xtk:ButtonChrome x:Name="ControlChrome" BorderThickness="0" Background="{TemplateBinding Background}" RenderEnabled="{TemplateBinding IsEnabled}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button x:Name="PART_ActionButton" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="0" Padding="{TemplateBinding Padding}" Style="{x:Null}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}">
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}"/>
</ControlTemplate>
</Button.Template>
<Grid>
<xtk:ButtonChrome x:Name="ActionButtonChrome" BorderBrush="{TemplateBinding BorderBrush}" Background="{TemplateBinding Background}" Foreground="{TemplateBinding Foreground}" RenderMouseOver="{Binding IsMouseOver, ElementName=PART_ActionButton}" RenderPressed="{Binding IsPressed, ElementName=PART_ActionButton}" RenderEnabled="{TemplateBinding IsEnabled}">
<xtk:ButtonChrome.BorderThickness>
<Binding ConverterParameter="2" Path="BorderThickness" RelativeSource="{RelativeSource TemplatedParent}">
<Binding.Converter>
<xtk:ThicknessSideRemovalConverter/>
</Binding.Converter>
</Binding>
</xtk:ButtonChrome.BorderThickness>
<ContentPresenter x:Name="ActionButtonContent" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</xtk:ButtonChrome>
</Grid>
</Button>
<ToggleButton x:Name="PART_ToggleButton" Grid.Column="1" IsChecked="{Binding IsOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}">
<ToggleButton.IsHitTestVisible>
<Binding Path="IsOpen" RelativeSource="{RelativeSource TemplatedParent}">
<Binding.Converter>
<xtk:InverseBoolConverter/>
</Binding.Converter>
</Binding>
</ToggleButton.IsHitTestVisible>
<ToggleButton.Template>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" ContentStringFormat="{TemplateBinding ContentStringFormat}"/>
</ControlTemplate>
</ToggleButton.Template>
<Grid>
<xtk:ButtonChrome x:Name="ToggleButtonChrome" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="1,0" RenderMouseOver="{Binding IsMouseOver, ElementName=PART_ToggleButton}" RenderPressed="{Binding IsPressed, ElementName=PART_ToggleButton}" RenderChecked="{TemplateBinding IsOpen}" RenderEnabled="{TemplateBinding IsEnabled}">
<Grid x:Name="arrowGlyph" IsHitTestVisible="False" Margin="4,3">
<Path x:Name="Arrow" Data="M0,0L3,0 4.5,1.5 6,0 9,0 4.5,4.5z" Fill="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" Height="5" Margin="0,1,0,0" Width="9"/>
</Grid>
</xtk:ButtonChrome>
</Grid>
</ToggleButton>
</Grid>
</xtk:ButtonChrome>
<Popup x:Name="PART_Popup" AllowsTransparency="True" Focusable="False" HorizontalOffset="1" IsOpen="{Binding IsChecked, ElementName=PART_ToggleButton}" Placement="{TemplateBinding DropDownPosition}" VerticalOffset="1"
StaysOpen="False">
<Border BorderThickness="{DynamicResource DefaultBorderThickness}" Margin="10,0,10,10" Background="{DynamicResource DarkerBaseBrush}" BorderBrush="{DynamicResource PopupBorderBrush}" CornerRadius="{DynamicResource DefaultCornerRadius}">
<Grid MinWidth="100" Name="PART_ContentPresenter">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Button x:Name="PART_ButtonWith1" Grid.Row="0" Grid.ColumnSpan="2">
1
</Button>
<Button x:Name="PART_ButtonWith5" Grid.Row="1" Grid.ColumnSpan="2">
5
</Button>
<Button x:Name="PART_ButtonWith10" Grid.Row="2" Grid.ColumnSpan="2">
10
</Button>
<local:CustomIntegerUpDown Grid.Row="3" Value="1"
Increment="1" ClipValueToMinMax="True"
x:Name="MyCustomIntegerUpDown">
</local:CustomIntegerUpDown>
<Button x:Name="PART_ButtonWithCustom" Grid.Row="3" Grid.Column="1" Padding="2,2,2,2">
>
</Button>
</Grid>
<Border.Effect>
<DropShadowEffect ShadowDepth="0" BlurRadius="10" Color="{DynamicResource Base6Color}" />
</Border.Effect>
</Border>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Fill" TargetName="Arrow" Value="#FFAFAFAF"/>
<Setter Property="Foreground" TargetName="ActionButtonChrome" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
In the OnApplyTemplate
I would expect to be able to access children of templates inside templates inside this
. 我希望在
OnApplyTemplate
中能够访问this
内部模板内的模板子级。 But I did not find a way to do this. 但是我没有找到一种方法。
Related question of mine here . 我的相关问题在这里 。
The starting point of the example, updated (it uses the TryFindVisualChildElementByName
extension method from BionicCode's answer): 示例的起点已更新(它使用了BionicCode的答案中的
TryFindVisualChildElementByName
扩展方法):
internal Popup PartPopup;
internal Button PartButtonWith1, PartButtonWith5, PartButtonWith10, PartButtonWithCustom;
internal RepeatButton IncrementButton;
private void SplitButton_Loaded(object sender, RoutedEventArgs e)
{
PartPopup = (Popup)GetTemplateChild("PART_Popup");
PartButtonWith1 = (Button)GetTemplateChild("PART_ButtonWith1");
PartButtonWith5 = (Button)GetTemplateChild("PART_ButtonWith5");
PartButtonWith10 = (Button)GetTemplateChild("PART_ButtonWith10");
PartButtonWithCustom = (Button)GetTemplateChild("PART_ButtonWithCustom");
if (PartPopup != null)
{
PartPopup.ApplyTemplateRecursively();
if (PartPopup.TryFindVisualChildElementByName("PART_IncreaseButton", out FrameworkElement incButton))
{
IncrementButton = (RepeatButton)incButton;
// do something with IncrementButton here
}
PartPopup.PreviewMouseDown += PART_Popup_PreviewMouseUp;
PartPopup.PreviewMouseUp += PART_Popup_PreviewMouseUp;
}
if (PartButtonWith1 != null)
{
PartButtonWith1.Click += Btns_NewTimer_Click;
}
if (PartButtonWith5 != null)
{
PartButtonWith5.Click += Btns_NewTimer_Click;
}
if (PartButtonWith10 != null)
{
PartButtonWith10.Click += Btns_NewTimer_Click;
}
if (PartButtonWithCustom != null)
{
PartButtonWithCustom.Click += BtnCustom_Click;
}
}
The ApplyTemplateRecursively
extension method used above, in 2 versions: 上面使用的
ApplyTemplateRecursively
扩展方法有2个版本:
Is it possible to make this version work somehow? 是否可以使该版本以某种方式工作? I think it is more efficient.
我认为这样更有效。
/// <summary>
/// Not working because the ApplyTemplate affects the VisualTree and when applying
/// templates recursively it does not see the correct updated visual tree to be able
/// to continue.
/// </summary>
/// <param name="root"></param>
internal static void ApplyTemplateRecursively(this System.Windows.DependencyObject root)
{
if (root is System.Windows.Controls.Primitives.Popup p)
{
p.Child.ApplyTemplateRecursively();
return;
}
if (root is FrameworkElement r)
{
r.ApplyTemplate();
}
foreach (object element in System.Windows.LogicalTreeHelper.GetChildren(root))
{
if (element is System.Windows.DependencyObject el)
{
ApplyTemplateRecursively(el);
}
}
}
/// <summary>
/// I am not sure if this is sufficiently efficient, because it goes through the entire visual tree.
/// </summary>
/// <param name="root"></param>
internal static void ApplyTemplateRecursively(this System.Windows.DependencyObject root)
{
if (root is System.Windows.Controls.Primitives.Popup p)
{
p.Child.ApplyTemplateRecursively();
return;
}
if (root is FrameworkElement r)
{
r.ApplyTemplate();
}
for (int i = 0; i < System.Windows.Media.VisualTreeHelper.GetChildrenCount(root); ++i)
{
DependencyObject d = VisualTreeHelper.GetChild(root, i);
ApplyTemplateRecursively(d);
}
}
Now I am trying to solve the actual problem. 现在,我正在尝试解决实际问题。
I have reported this issue . 我已经报告了这个问题 。
The point is that the content of a Popup
is not directly part of the visual tree. 关键是
Popup
的内容不直接属于可视化树的一部分。 That's why looking for visual children of a Popup
will always return null
. 这就是为什么寻找
Popup
可视子代总是返回null
。 The content of a Popup
is rendered separately and is assigned to the Popup.Child
property. Popup
的内容是单独呈现的,并分配给Popup.Child
属性。 You need to extract those from the Child
property before continuing tree traversal inside the Popup
. 您需要从
Child
属性中提取那些内容,然后继续在Popup
内部进行树遍历。
The following is a custom visual tree helper method to return the first child element that matches a given name. 以下是自定义可视树帮助器方法,用于返回与给定名称匹配的第一个子元素。 This helper properly searches inside a
Popup
element. 该帮助器可以在
Popup
元素内正确搜索。 This method is an Extension Method of type DependencyObject
and has to be put into a static
class
: 此方法是
DependencyObject
类型的扩展方法,必须放入static
class
:
public static bool TryFindVisualChildElementByName(
this DependencyObject parent,
string childElementName,
out FrameworkElement resultElement)
{
resultElement = null;
if (parent is Popup popup)
{
parent = popup.Child;
if (parent == null)
{
return false;
}
}
for (var childIndex = 0; childIndex < VisualTreeHelper.GetChildrenCount(parent); childIndex++)
{
DependencyObject childElement = VisualTreeHelper.GetChild(parent, childIndex);
if (childElement is FrameworkElement uiElement && uiElement.Name.Equals(
childElementName,
StringComparison.OrdinalIgnoreCase))
{
resultElement = uiElement;
return true;
}
if (childElement.TryFindVisualChildElementByName(childElementName, out resultElement))
{
return true;
}
}
return false;
}
It's an Extension Method and it is used like this: 这是一种扩展方法,其用法如下:
CustomSplitButton.xaml.cs CustomSplitButton.xaml.cs
// Constructor
public CustomSplitButton()
{
this.Loaded += GetParts;
}
private void GetParts(object sender, RoutedEventArgs e)
{
if (this.TryFindVisualChildElementByName("PART_Popup", out FrameworkElement popupPart))
{
if (popupPart.TryFindVisualChildElementByName("PART_ContentPresenter", out FrameworkElement contentPresenter))
{
if (!contentPresenter.IsLoaded)
{
contentPresenter.Loaded += CompleteSearch;
}
else
{
CompleteSearch(contentPresenter, null);
}
}
}
}
private void CompleteSearch(object sender, RoutedEventArgs e)
{
contentPresenter.Loaded -= CompleteSearch;
if ((sender as DependencyObject).TryFindVisualChildElementByName("PART_IncreaseButton", out FrameworkElement increaseButton))
{
IncrementButton = (RepeatButton) increaseButton;
}
}
Remarks 备注
It is very important to search after a parent element was Loaded
. 在
Loaded
父元素之后进行搜索非常重要。
This is true for all elements in the visual tree. 对于可视树中的所有元素都是如此。 Since the
SplitButton
consists of a drop down that is collapsed by default, not all contents are loaded initially. 由于
SplitButton
包含一个默认情况下折叠的下拉菜单,因此并非所有内容最初都会加载。 Once the drop down is opened the SplitButton
makes its content visible, which will add them to the visual tree. 打开下拉菜单后,
SplitButton
使其内容可见,这会将它们添加到可视树中。 Up to this point the SplitButton.IsLoaded
property will return false
, indicating the button's incomplete visual state. 到目前为止,
SplitButton.IsLoaded
属性将返回false
,指示按钮的不完整视觉状态。 What you need to do is, once you encountered a FrameworkElement
where FrameworkElement.IsLoaded
returns false
you have to subscribe to the FrameworkElement.Loaded
event. 您需要做的是,一旦遇到
FrameworkElement.IsLoaded
返回false
的FrameworkElement
,则必须订阅FrameworkElement.Loaded
事件。 In this handler you can continue the visual tree traversal. 在此处理程序中,您可以继续可视树遍历。
Popup
like elements or collapsed controls add complexity to the visual tree traversal. Popup
式元素或折叠控件会增加可视化树遍历的复杂性。
Popup
open when content was clicked Popup
窗口打开 Now that you have told me that you are using the SplitButton
inside a ToolBar
I instantly knew the origin of your problem: 既然您已经告诉我您正在使用
ToolBar
SplitButton
,那么我立即知道问题的根源:
Classes in WPF which are focus scopes by default are
Window
,MenuItem
,ToolBar
, andContextMenu
.WPF中默认为焦点范围的类是
Window
,MenuItem
,ToolBar
和ContextMenu
。 [Microsoft Docs: Logical Focus ][Microsoft Docs: 逻辑焦点 ]
Simply remove the focus scope from the ToolBar
to prevent the focus from being removed from the Popup
as soon as any of its content was clicked (received logical focus): 只需从
ToolBar
上删除焦点范围,以防止在单击其任何内容时将焦点从Popup
窗口中删除(收到逻辑焦点):
<ToolBar FocusManager.IsFocusScope="False">
<CustomSplitButton />
</ToolBar>
Popup
open when clicked on PART_ToggleButton while the Popup
is open Popup
开放上PART_ToggleButton当点击而Popup
是开放的 To prevent the Popup
from closing and reopening when the PART_ToggleButton is clicked while the Popup
is open you need to handle the mouse down event (application wide) and the opening of the Popup yourself. 为了防止在
Popup
窗口打开时单击PART_ToggleButton时Popup
窗口关闭并重新打开,您需要自行处理鼠标按下事件(应用程序范围)和打开Popup
窗口。
First modify the PART_Popup to make it stay open and remove the binding from the IsOpen
property: 首先修改PART_Popup使其保持打开状态,并从
IsOpen
属性中删除绑定:
CustomSplitButton.xaml CustomSplitButton.xaml
<Popup x:Name="PART_Popup"
IsOpen="False"
StaysOpen="True"
AllowsTransparency="True"
Focusable="False"
HorizontalOffset="1"
Placement="{TemplateBinding DropDownPosition}"
VerticalOffset="1">
Then in your CustomSplitButton
observe the mouse device for mouse down events and determine the hit target. 然后在
CustomSplitButton
观察鼠标设备的鼠标按下事件并确定命中目标。 I assume that you retrieved the underlying PART_Popup and PART_ToggleButton element and stored it in a property named PartPopup
and PartToggleButton
(see first part of this answer on how to do it): 我假设您检索了基础PART_Popup和PART_ToggleButton元素,并将其存储在名为
PartPopup
和PartToggleButton
的属性中(有关此操作的更多信息,请参见本答案的第一部分):
CustomSplitButton.xaml.cs CustomSplitButton.xaml.cs
public CustomSplitButton()
{
this.Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Mouse.AddPreviewMouseDownHandler(Application.Current.MainWindow, KeepPopupOpen);
}
private void KeepPopupOpen(object sender, RoutedEventArgs routedEventArgs)
{
var mouseClickSourceElement = routedEventArgs.OriginalSource as DependencyObject;
var isPopupContentClicked = false;
var isPartToggleButtonClicked =
object.ReferenceEquals(routedEventArgs.Source, this)
&& mouseClickSourceElement.TryFindVisualParentElement(out ButtonBase button)
&& button.Name.Equals(this.PartToggleButton.Name, StringComparison.OrdinalIgnoreCase);
if (!isPartToggleButtonClicked)
{
isPopupContentClicked =
object.ReferenceEquals(routedEventArgs.Source, this)
&& mouseClickSourceElement.TryFindVisualParentElementByName("PART_ContentPresenter", out FrameworkElement popupContentPresenter));
}
this.PartPopup.IsOpen = this.IsOpen = isPartToggleButtonClicked || isPopupContentClicked ;
}
Extension Methods to find the visual parent by type and by name 通过类型和名称查找可视父项的扩展方法
public static class HelperExtensions
{
public static bool TryFindVisualParentElement<TParent>(this DependencyObject child, out TParent resultElement)
where TParent : DependencyObject
{
resultElement = null;
if (child == null)
{
return false;
}
DependencyObject parentElement = VisualTreeHelper.GetParent(child);
if (parentElement is TParent parent)
{
resultElement = parent;
return true;
}
return parentElement.TryFindVisualParentElement(out resultElement);
}
public static bool TryFindVisualParentElementByName(
this DependencyObject child,
string elementName,
out FrameworkElement resultElement)
{
resultElement = null;
if (child == null)
{
return false;
}
DependencyObject parentElement = VisualTreeHelper.GetParent(child);
if (parentElement is FrameworkElement frameworkElement &&
frameworkElement.Name.Equals(elementName, StringComparison.OrdinalIgnoreCase))
{
resultElement = frameworkElement;
return true;
}
return parentElement.TryFindVisualParentElementByName(elementName, out resultElement);
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.