In C# UWP I am creating custom tooltip style.
I have changed the default style of tooltip as below.
<Style TargetType="ToolTip">
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundChromeHighBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource ToolTipBorderThemeThickness}" />
<Setter Property="FontSize" Value="{ThemeResource ToolTipContentThemeFontSize}" />
<Setter Property="Padding" Value="40,40,40,35"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Grid Background="Transparent">
<Grid
MinWidth="100"
MinHeight="90"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}"
Padding="15"
Background="Transparent">
<local:ArrowDown x:Name="arrowDown" TooltipPlacement="{TemplateBinding Placement}"/>
And my custom control ArrowDown is getting information of ToolTip placement, so I can show it depends if tooltip is under or above control.
In the ArrowDown control I have added a DependencyProperty as below:
public PlacementMode TooltipPlacement
{
get { return (PlacementMode)GetValue(TooltipPlacementProperty); }
set { SetValue(TooltipPlacementProperty, value); }
}
public static readonly DependencyProperty TooltipPlacementProperty =
DependencyProperty.Register("TooltipPlacement", typeof(PlacementMode), typeof(ArrowDown), new PropertyMetadata(null, TooltipPlacementChangedCallback));
private static void TooltipPlacementChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var self = (ArrowDown)d;
self.CalculateArrowVisibility();
}
// Method to show or hide arrow
private void CalculateArrowVisibility()
{
}
And the problem is that the CalculateArrowVisibility is fire only the first time when tooltip is shown, and it always returns Top for TooltipPlacement, no matter if tooltip is shown below or above control.
I need CalculateArrowVisibility to be fired whenever the tooltip is shown, and I need TooltipPlacement property to show if tooltip is Under or Above control.
Anyone have the idea about this?
The fact is that you cannot use the ToolTipService
attached properties (e.g. <Button ToolTipService.Placement="Bottom" ToolTipService.ToolTip="!!!" />
) to define the tooltip and it placement. This way the Placement
is not set on the actual ToolTip
control itself, and that's why it will always return Top
.
In order to have the ToolTip
pass down its Placement
value to your custom dependency property, you will have to attach it like the following -
<Button>
<ToolTipService.ToolTip>
<ToolTip Placement="Bottom" Content="Hahaha..." />
</ToolTipService.ToolTip>
</Button>
Update
Turns out that even though the app Window pushes the tooltip above or below its parent, its Placement
value is never changed, what's changed is its horizontal & vertical offsets.
So, in your case, if we could work out its exact vertical offset, we would be able to determine whether the tooltip is above or below (its parent).
Given we have a ToolTip
Style in place, we can create an attached property of type ToolTip
and attach it to the Grid
that contains the ArrowDown
control.
<Grid MinWidth="100"
MinHeight="90"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}"
Padding="15"
Background="Transparent"
local:ToolTipHelper.ToolTip="{Binding RelativeSource={RelativeSource TemplatedParent}}">
Because the TemplatedParent
of the Grid
is the ToolTip
, we can use RelativeSource
binding to link the ToolTip
on the screen with our attached property, as shown above.
Now, we have a reference to the actual ToolTip
, let's find its offsets. After some digging, I've found that the offsets of the ToolTip
are always 0
, they are useless; however, the offsets of its parent - a Popup
, sometimes gives me the correct values, but not always. This is because I was using the Opened
event where those values weren't yet populated; as soon as I changed it to SizeChanged
, they have been giving me the expected values.
public static class ToolTipHelper
{
public static ToolTip GetToolTip(DependencyObject obj)
{
return (ToolTip)obj.GetValue(ToolTipProperty);
}
public static void SetToolTip(DependencyObject obj, ToolTip value)
{
obj.SetValue(ToolTipProperty, value);
}
public static readonly DependencyProperty ToolTipProperty =
DependencyProperty.RegisterAttached("ToolTip", typeof(ToolTip), typeof(ToolTipHelper),
new PropertyMetadata(null, (s, e) =>
{
var panel = (Panel)s; // The Grid that contains the ArrowDown control.
var toolTip = (ToolTip)e.NewValue;
// We need to monitor SizeChanged instead of Opened 'cause the offsets
// are yet to be properly set in the latter event.
toolTip.SizeChanged += (sender, args) =>
{
var popup = (Popup)toolTip.Parent; // The Popup that contains the ToolTip.
// Note we have to use the Popup's offset here as the ToolTip's are always 0.
var arrowDown = (ArrowDown)panel.FindName("arrowDown");
arrowDown.TooltipPlacement = popup.VerticalOffset > 0
? PlacementMode.Bottom
: PlacementMode.Top;
};
}));
}
Now, with this approach, you should be able to use the ToolTipService
attached properties too. So the following XAML would work.
<Button ToolTipService.ToolTip="!!!" Content="Hover Me" />
Hope this helps!