C# 强制 WPF 工具提示留在屏幕上
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/896574/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Forcing a WPF tooltip to stay on the screen
提问by TimothyP
I have a tooltip for a Label and I want it to stay open until the user moves the mouse to a different control.
我有一个 Label 的工具提示,我希望它保持打开状态,直到用户将鼠标移动到不同的控件上。
I have tried the following properties on the tooltip:
我在工具提示上尝试了以下属性:
StaysOpen="True"
and
和
ToolTipService.ShowDuration = "60000"
But in both cases the tooltip is only displayed for exactly 5 seconds.
但在这两种情况下,工具提示仅显示 5 秒。
Why are these values being ignored?
为什么这些值会被忽略?
采纳答案by John Whiter
Just put this code in initialization section.
只需将此代码放在初始化部分即可。
ToolTipService.ShowDurationProperty.OverrideMetadata(
typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));
回答by Daniel Earwicker
I was wrestling with the WPF Tooltip only the other day. It doesn't seem to be possible to stop it from appearing and disappearing by itself, so in the end I resorted to handling the Opened
event. For example, I wanted to stop it from opening unless it had some content, so I handled the Opened
event and then did this:
前几天我还在与 WPF 工具提示搏斗。似乎无法阻止它自己出现和消失,所以最后我求助于处理Opened
事件。例如,我想阻止它打开,除非它有一些内容,所以我处理了Opened
事件,然后这样做:
tooltip.IsOpen = (tooltip.Content != null);
It's a hack, but it worked.
这是一个黑客,但它奏效了。
Presumably you could similarly handle the Closed
event and tell it to open again, thus keeping it visible.
据推测,您可以类似地处理该Closed
事件并告诉它再次打开,从而使其保持可见。
回答by micahtan
You probably want to use Popup instead of Tooltip, since Tooltip assumes that you're using it in the pre-defined UI-standards way.
您可能想要使用 Popup 而不是 Tooltip,因为 Tooltip 假定您以预定义的 UI 标准方式使用它。
I'm not sure why StaysOpen doesn't work, but ShowDuration works as documented in MSDN -- it's the amount of time the Tooltip is displayed WHEN it's displayed. Set it to a small amount (e.g. 500 msec) to see the difference.
我不确定为什么 StaysOpen 不起作用,但 ShowDuration 的工作方式如 MSDN 中所述——这是显示工具提示时显示的时间量。将其设置为少量(例如 500 毫秒)以查看差异。
The trick in your case is maintaining the "last hovered control" state, but once you have that it should be fairly trivial to change the placement target and the content dynamically (either manually, or via binding) if you're using one Popup, or hiding the last visible Popup if you're using multiple.
在您的情况下,诀窍是保持“最后悬停控件”状态,但是一旦您拥有了,如果您使用的是一个弹出窗口,那么动态(手动或通过绑定)更改放置目标和内容应该是相当简单的,或者如果您使用多个,则隐藏最后一个可见的弹出窗口。
There are some gotchas with Popups as far as Window resizing and moving (Popups don't move w/the containers), so you may want to also have that in mind while you're tweaking the behavior. See this linkfor more details.
就窗口大小调整和移动而言,弹出窗口存在一些问题(弹出窗口不随容器移动),因此您可能希望在调整行为时也考虑到这一点。有关更多详细信息,请参阅此链接。
HTH.
哈。
回答by Carlo
Also if you ever want to put any other control in your ToolTip, it won't be focusable since a ToolTip itself can get focus. So Like micahtan said, your best shot is a Popup.
此外,如果您想在 ToolTip 中放置任何其他控件,它将无法获得焦点,因为 ToolTip 本身可以获得焦点。所以就像micahtan所说,你最好的镜头是弹出窗口。
回答by Martin Konicek
TooltipService.ShowDuration
works, but you must set it on the object having the Tooltip, like this:
TooltipService.ShowDuration
有效,但您必须将其设置在具有 Tooltip 的对象上,如下所示:
<Label ToolTipService.ShowDuration="12000" Name="lblShowTooltip" Content="Shows tooltip">
<Label.ToolTip>
<ToolTip>
<TextBlock>Hello world!</TextBlock>
</ToolTip>
</Label.ToolTip>
</Label>
I'd say that this design was chosen because it allows same tooltip with different timeouts on different controls.
我会说选择这种设计是因为它允许相同的工具提示在不同的控件上具有不同的超时。
回答by Vibhuti Sharma
ToolTipService.ShowDurationProperty.OverrideMetadata(
typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));
It is working for me. Copy this line into your class constructor.
它对我有用。将此行复制到您的类构造函数中。
回答by kevinarpe
This was also driving me crazy tonight. I created a ToolTip
subclass to handle the issue. For me, on .NET 4.0, the ToolTip.StaysOpen
property is not "really" stays open.
这也让我今晚发疯。我创建了一个ToolTip
子类来处理这个问题。对我来说,在 .NET 4.0 上,该ToolTip.StaysOpen
属性不是“真正”保持打开状态。
In the class below, use the new property ToolTipEx.IsReallyOpen
, instead of property ToolTip.IsOpen
. You will get the control you want. Via the Debug.Print()
call, you can watch in the debugger Output window just how many times this.IsOpen = false
is called! So much for StaysOpen
, or should I say "StaysOpen"
? Enjoy.
在下面的类中,使用新的 property ToolTipEx.IsReallyOpen
,而不是 property ToolTip.IsOpen
。你会得到你想要的控制。通过Debug.Print()
调用,您可以在调试器输出窗口中查看调用了多少次this.IsOpen = false
!这么多StaysOpen
,还是应该说"StaysOpen"
?享受。
public class ToolTipEx : ToolTip
{
static ToolTipEx()
{
IsReallyOpenProperty =
DependencyProperty.Register(
"IsReallyOpen",
typeof(bool),
typeof(ToolTipEx),
new FrameworkPropertyMetadata(
defaultValue: false,
flags: FrameworkPropertyMetadataOptions.None,
propertyChangedCallback: StaticOnIsReallyOpenedChanged));
}
public static readonly DependencyProperty IsReallyOpenProperty;
protected static void StaticOnIsReallyOpenedChanged(
DependencyObject o, DependencyPropertyChangedEventArgs e)
{
ToolTipEx self = (ToolTipEx)o;
self.OnIsReallyOpenedChanged((bool)e.OldValue, (bool)e.NewValue);
}
protected void OnIsReallyOpenedChanged(bool oldValue, bool newValue)
{
this.IsOpen = newValue;
}
public bool IsReallyOpen
{
get
{
bool b = (bool)this.GetValue(IsReallyOpenProperty);
return b;
}
set { this.SetValue(IsReallyOpenProperty, value); }
}
protected override void OnClosed(RoutedEventArgs e)
{
System.Diagnostics.Debug.Print(String.Format(
"OnClosed: IsReallyOpen: {0}, StaysOpen: {1}", this.IsReallyOpen, this.StaysOpen));
if (this.IsReallyOpen && this.StaysOpen)
{
e.Handled = true;
// We cannot set this.IsOpen directly here. Instead, send an event asynchronously.
// DispatcherPriority.Send is the highest priority possible.
Dispatcher.CurrentDispatcher.BeginInvoke(
(Action)(() => this.IsOpen = true),
DispatcherPriority.Send);
}
else
{
base.OnClosed(e);
}
}
}
Small rant: Why didn't Microsoft make DependencyProperty
properties (getters/setters) virtual so we can accept/reject/adjust changes in subclasses? Or make a virtual OnXYZPropertyChanged
for each and every DependencyProperty
? Ugh.
小咆哮:为什么 Microsoft 不将DependencyProperty
属性(getter/setter)设为虚拟,以便我们可以接受/拒绝/调整子类中的更改?或者virtual OnXYZPropertyChanged
为每一个做一个DependencyProperty
?啊。
---Edit---
- -编辑 - -
My solution above looks weird in the XAML editor -- the tooltip is always showing, blocking some text in Visual Studio!
我上面的解决方案在 XAML 编辑器中看起来很奇怪——工具提示总是显示,在 Visual Studio 中阻止了一些文本!
Here is a better way to solve this problem:
这里有一个更好的方法来解决这个问题:
Some XAML:
一些 XAML:
<!-- Need to add this at top of your XAML file:
xmlns:System="clr-namespace:System;assembly=mscorlib"
-->
<ToolTip StaysOpen="True" Placement="Bottom" HorizontalOffset="10"
ToolTipService.InitialShowDelay="0" ToolTipService.BetweenShowDelay="0"
ToolTipService.ShowDuration="{x:Static Member=System:Int32.MaxValue}"
>This is my tooltip text.</ToolTip>
Some code:
一些代码:
// Alternatively, you can attach an event listener to FrameworkElement.Loaded
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
// Be gentle here: If someone creates a (future) subclass or changes your control template,
// you might not have tooltip anymore.
ToolTip toolTip = this.ToolTip as ToolTip;
if (null != toolTip)
{
// If I don't set this explicitly, placement is strange.
toolTip.PlacementTarget = this;
toolTip.Closed += new RoutedEventHandler(OnToolTipClosed);
}
}
protected void OnToolTipClosed(object sender, RoutedEventArgs e)
{
// You may want to add additional focus-related tests here.
if (this.IsKeyboardFocusWithin)
{
// We cannot set this.IsOpen directly here. Instead, send an event asynchronously.
// DispatcherPriority.Send is the highest priority possible.
Dispatcher.CurrentDispatcher.BeginInvoke(
(Action)delegate
{
// Again: Be gentle when using this.ToolTip.
ToolTip toolTip = this.ToolTip as ToolTip;
if (null != toolTip)
{
toolTip.IsOpen = true;
}
},
DispatcherPriority.Send);
}
}
Conclusion: Something is different about classes ToolTip
and ContextMenu
. Both have "service" classes, like ToolTipService
and ContextMenuService
, that manage certain properties, and both use Popup
as a "secret" parent control during display. Finally, I noticed ALLthe XAML ToolTip examples on the Web do not use class ToolTip
directly. Instead, they embed a StackPanel
with TextBlock
s. Things that make you say: "hmmm..."
结论:类ToolTip
和ContextMenu
. 两者都有管理某些属性的“服务”类,例如ToolTipService
和ContextMenuService
,并且都Popup
在显示期间用作“秘密”父控件。最后,我注意到Web 上的所有XAML 工具提示示例都没有ToolTip
直接使用类。相反,他们将 aStackPanel
与TextBlock
s嵌入。那些让你说:“嗯……”的事情
回答by Ulrich Beckert
Just for the sake of completeness: In code it looks like this:
只是为了完整性:在代码中它看起来像这样:
ToolTipService.SetShowDuration(element, 60000);
回答by Jonathan Allan
If you want to specify that only certain elements in your Window
have
effectively indefinite ToolTip
duration you can define a Style
in your Window.Resources
for those elements. Here is a Style
for Button
that has such a ToolTip
:
如果您想指定只有某些元素Window
具有有效的无限期ToolTip
持续时间,您可以Style
在您Window.Resources
的元素中为这些元素定义一个。下面是一个Style
为Button
有这样的ToolTip
:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
...>
...
<Window.Resources>
<Style x:Key="ButtonToolTipIndefinate" TargetType="{x:Type Button}">
<Setter Property="ToolTipService.ShowDuration"
Value="{x:Static Member=sys:Int32.MaxValue}"/>
</Style>
...
</Window.Resources>
...
<Button Style="{DynamicResource ButtonToolTipIndefinate}"
ToolTip="This should stay open"/>
<Button ToolTip="This Should disappear after the default time.">
...
One can also add Style.Resources
to the Style
to change the appearance of the ToolTip
it shows, for example:
还可以添加Style.Resources
到Style
以更改ToolTip
它显示的外观,例如:
<Style x:Key="ButtonToolTipTransparentIndefinate" TargetType="{x:Type Button}">
<Style.Resources>
<Style x:Key="{x:Type ToolTip}" TargetType="{x:Type ToolTip}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="HasDropShadow" Value="False"/>
</Style>
</Style.Resources>
<Setter Property="ToolTipService.ShowDuration"
Value="{x:Static Member=sys:Int32.MaxValue}"/>
</Style>
Note: When I did this I also used BasedOn
in the Style
so everything else defined for the version of my custom control with a normal ToolTip
would be applied.
注意:当我这样做时,我也使用BasedOn
了,Style
因此ToolTip
将应用为我的自定义控件版本定义的所有其他内容。
回答by Aravind S
Got my issue fixed with the same code.
用相同的代码解决了我的问题。
ToolTipService.ShowDurationProperty.OverrideMetadata( typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));
ToolTipService.ShowDurationProperty.OverrideMetadata( typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));