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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 22:02:03  来源:igfitidea点击:

Forcing a WPF tooltip to stay on the screen

c#wpfxamltooltip

提问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 Openedevent. For example, I wanted to stop it from opening unless it had some content, so I handled the Openedevent 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 Closedevent 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.ShowDurationworks, 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 ToolTipsubclass to handle the issue. For me, on .NET 4.0, the ToolTip.StaysOpenproperty 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 = falseis 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 DependencyPropertyproperties (getters/setters) virtual so we can accept/reject/adjust changes in subclasses? Or make a virtual OnXYZPropertyChangedfor 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 ToolTipand ContextMenu. Both have "service" classes, like ToolTipServiceand ContextMenuService, that manage certain properties, and both use Popupas a "secret" parent control during display. Finally, I noticed ALLthe XAML ToolTip examples on the Web do not use class ToolTipdirectly. Instead, they embed a StackPanelwith TextBlocks. Things that make you say: "hmmm..."

结论:类ToolTipContextMenu. 两者都有管理某些属性的“服务”类,例如ToolTipServiceContextMenuService,并且都Popup在显示期间用作“秘密”父控件。最后,我注意到Web 上的所有XAML 工具提示示例都没有ToolTip直接使用类。相反,他们将 aStackPanelTextBlocks嵌入。那些让你说:“嗯……”的事情

回答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 Windowhave effectively indefinite ToolTipduration you can define a Stylein your Window.Resourcesfor those elements. Here is a Stylefor Buttonthat has such a ToolTip:

如果您想指定只有某些元素Window具有有效的无限期ToolTip持续时间,您可以Style在您Window.Resources的元素中为这些元素定义一个。下面是一个StyleButton有这样的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.Resourcesto the Styleto change the appearance of the ToolTipit shows, for example:

还可以添加Style.ResourcesStyle以更改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 BasedOnin the Styleso everything else defined for the version of my custom control with a normal ToolTipwould 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));