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..."