Forcer une info-bulle WPF à restr à l’écran

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

J’ai essayé les propriétés suivantes sur l’info-bulle:

StaysOpen="True" 

et

 TooltipService.ShowDuration = "60000" 

Mais dans les deux cas, l’info-bulle ne s’affiche que pendant 5 secondes exactement.

Pourquoi ces valeurs sont-elles ignorées?

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

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

TooltipService.ShowDuration fonctionne, mais vous devez le définir sur l’object ayant l’info-bulle, comme ceci:

  

Je dirais que cette conception a été choisie car elle permet la même infobulle avec des délais différents sur différents contrôles.

Cela me rendait aussi fou ce soir. J’ai créé une sous-classe ToolTip pour traiter le problème. Pour moi, sur .NET 4.0, la propriété ToolTip.StaysOpen ne rest 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 voulez. Via l’appel Debug.Print() , vous pouvez regarder dans la fenêtre de sortie du débogueur combien de fois this.IsOpen = false est appelé! Tellement pour StaysOpen , ou devrais-je dire "StaysOpen" ? Prendre plaisir.

 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(Ssortingng.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 problème: Pourquoi Microsoft n’a-t-il pas rendu DependencyProperty propriétés DependencyProperty (getters / setters) virtuelles pour accepter / rejeter / ajuster les modifications dans les sous-classes? Ou créer un virtual OnXYZPropertyChanged pour chaque virtual OnXYZPropertyChanged DependencyProperty ? Pouah.

—Modifier—

Ma solution ci-dessus semble étrange dans l’éditeur XAML – l’info-bulle est toujours affichée, bloquant du texte dans Visual Studio!

Voici un meilleur moyen de résoudre ce problème:

Certains XAML:

  This is my tooltip text. 

Un 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: quelque chose est différent en ce qui concerne les classes ContextMenu et ContextMenu . Les deux ont des classes “service”, comme ToolTipService et ContextMenuService , qui gèrent certaines propriétés, et les deux utilisent Popup comme contrôle parent “secret” lors de l’affichage. Enfin, j’ai remarqué que tous les exemples de XAML ToolTip sur le Web n’utilisent pas directement la classe ToolTip . Au lieu de cela, ils intègrent un StackPanel avec TextBlock s. Des choses qui vous font dire: “hmmm …”

Vous souhaitez probablement utiliser Popup au lieu de l’info-bulle, car l’info-bulle suppose que vous l’utilisez de la manière standard pré-définie de l’interface utilisateur.

Je ne sais pas pourquoi StaysOpen ne fonctionne pas, mais ShowDuration fonctionne comme indiqué dans MSDN – c’est la durée d’affichage de l’info-bulle QUAND elle est affichée. Réglez-le sur une petite quantité (par exemple 500 ms) pour voir la différence.

L’astuce dans votre cas est de maintenir l’état “dernier contrôle survolé”, mais une fois que vous avez cela, il devrait être assez sortingvial de changer la cible de placement et le contenu dynamicment (manuellement ou par liaison) si vous utilisez un Popup. ou masquer la dernière Popup visible si vous utilisez plusieurs.

Il y a des pièges avec les fenêtres pop-up en ce qui concerne le redimensionnement et le déplacement de fenêtres (les fenêtres contextuelles ne bougent pas avec les conteneurs), vous pouvez donc avoir cela à l’esprit lorsque vous modifiez le comportement. Voir ce lien pour plus de détails.

HTH.

Si vous souhaitez spécifier que seuls certains éléments de votre Window ont effectivement une durée d’ ToolTip indéfinie, vous pouvez définir un Style dans vos Window.Resources pour ces éléments. Voici un Style pour le Button qui a une telle ToolTipToolTip :

  ...   ...  ...   

On peut également append Style.Resources au Style pour modifier l’apparence de l’ ToolTipToolTip , par exemple:

     

Remarque: Lorsque je l’ai fait, j’ai également utilisé BasedOn dans le Style afin que tout le rest défini pour la version de mon contrôle personnalisé avec une ToolTipToolTip normale soit appliqué.

Je me débattais avec l’info-bulle de WPF seulement l’autre jour. Il ne semble pas possible d’empêcher son apparition et sa disparition, donc j’ai finalement eu recours à l’événement Opened . Par exemple, je voulais l’empêcher de s’ouvrir à moins d’avoir du contenu, alors je me suis occupé de l’événement Opened puis je l’ai fait:

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

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

Vous pouvez probablement gérer l’événement Closed manière similaire et lui dire de l’ouvrir à nouveau, le gardant ainsi visible.

Juste pour être complet: Dans le code, cela ressemble à ceci:

 ToolTipService.SetShowDuration(element, 60000); 

De plus, si vous souhaitez placer un autre contrôle dans votre info-bulle, il ne sera pas possible de se concentrer car une info-bulle elle-même peut être mise au point. Donc, comme dit Micahtan, votre meilleur coup est un Popup.

J’ai résolu mon problème avec le même code.

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

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

Ça marche pour moi. Copiez cette ligne dans votre constructeur de classe.