135 votes

Obliger une infobulle WPF à rester à l'écran

J'ai une infobulle pour une étiquette et je veux qu'elle reste ouverte jusqu'à ce que l'utilisateur déplace la souris vers un autre contrôle. déplace la souris vers un autre contrôle.

J'ai essayé les propriétés suivantes sur l'infobulle :

StaysOpen="True"

et

TooltipService.ShowDuration = "60000"

Mais dans les deux cas, l'infobulle ne s'affiche que pendant exactement 5 secondes.

Pourquoi ces valeurs sont-elles ignorées ?

221voto

Martin Konicek Points 7999

TooltipService.ShowDuration fonctionne, mais vous devez le définir sur l'objet ayant l'infobulle, comme ceci :

<Label ToolTipService.ShowDuration="12000" Name="lblShowTooltip" Content="Shows tooltip">
    <Label.ToolTip>
        <ToolTip>
            <TextBlock>Hello world!</TextBlock>
        </ToolTip>
    </Label.ToolTip>
</Label>

Je dirais que ce design a été choisi parce qu'il permet d'avoir la même infobulle avec des délais différents sur des contrôles différents.

131voto

John Whiter Points 176

Il suffit de mettre ce code dans la section d'initialisation.

 ToolTipService.ShowDurationProperty.OverrideMetadata(
    typeof(DependencyObject), new FrameworkPropertyMetadata(Int32.MaxValue));
 

18voto

kevinarpe Points 2902

Cela m'a également rendu fou ce soir. J'ai créé un ToolTip pour gérer le problème. En ce qui me concerne, sur .NET 4.0, la classe ToolTip.StaysOpen La propriété n'est pas "vraiment" ouverte.

Dans la classe ci-dessous, utilisez la nouvelle propriété ToolTipEx.IsReallyOpen au lieu de la propriété ToolTip.IsOpen . Vous obtiendrez le contrôle que vous souhaitez. Via le Debug.Print() vous pouvez observer dans la fenêtre de sortie du débogueur combien de fois this.IsOpen = false est appelé ! Tant pis pour StaysOpen ou devrais-je dire "StaysOpen" ? Profitez-en.

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);
        }
    }
}

Petit coup de gueule : Pourquoi Microsoft n'a pas fait DependencyProperty les propriétés (getters/setters) virtuelles afin que nous puissions accepter/rejeter/ajuster les changements dans les sous-classes ? Ou faire un virtual OnXYZPropertyChanged pour chaque DependencyProperty ? Ugh.

---Edit---

Ma solution ci-dessus a l'air bizarre dans l'éditeur XAML - l'infobulle est toujours affichée, bloquant du texte dans Visual Studio !

Voici une meilleure façon de résoudre ce problème :

Un peu de 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>

Un certain 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 : Les classes ont quelque chose de différent ToolTip et ContextMenu . Les deux ont des classes de "service", comme ToolTipService et ContextMenuService qui gèrent certaines propriétés, et tous deux utilisent Popup comme un contrôle parental "secret" pendant l'affichage. Enfin, j'ai remarqué TOUTES les exemples de ToolTip XAML sur le Web n'utilisent pas la classe ToolTip directement. Au lieu de cela, ils intègrent un StackPanel avec TextBlock s. Des choses qui te font dire : "hmmm..."

8voto

micahtan Points 6457

Vous voudrez probablement utiliser Popup au lieu de Tooltip, car Tooltip suppose que vous l'utilisez de la manière prédéfinie par les normes UI.

Je ne sais pas pourquoi StaysOpen ne fonctionne pas, mais ShowDuration fonctionne comme documenté dans MSDN -- c'est la durée d'affichage de l'infobulle LORSQU'elle est affichée. Réglez-le à une petite quantité (par exemple 500 msec) pour voir la différence.

L'astuce dans votre cas est de maintenir l'état du "dernier contrôle survolé", mais une fois que vous l'avez, il devrait être assez trivial de changer la cible de placement et le contenu dynamiquement (soit manuellement, soit par liaison) si vous utilisez un Popup, ou de cacher le dernier Popup visible si vous utilisez plusieurs.

Il y a quelques problèmes avec les fenêtres pop-up en ce qui concerne le redimensionnement et le déplacement de la fenêtre (les fenêtres pop-up ne se déplacent pas avec les conteneurs), vous devez donc en tenir compte lorsque vous modifiez le comportement. Voir ce lien pour plus de détails.

HTH.

6voto

Daniel Earwicker Points 63298

L'autre jour, je me débattais avec le WPF Tooltip seulement. Il ne semble pas possible de l'empêcher d'apparaître et de disparaître d'elle-même, et j'ai donc fini par manipuler l'info-bulle de WPF. Opened événement. Par exemple, je voulais l'empêcher de s'ouvrir s'il n'y avait pas de contenu, alors j'ai traité l'événement Opened et a ensuite fait ceci :

tooltip.IsOpen = (tooltip.Content != null);

C'est un hack, mais ça a marché.

Vous pourriez sans doute traiter de la même manière le Closed et lui demander de s'ouvrir à nouveau, ce qui lui permet de rester visible.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X