1

I have a custom control I wrote in WPF, which has a Boolean dependency property:

public static readonly DependencyProperty IsAlertProperty
    = DependencyProperty.Register("IsAlert", typeof(bool), typeof(AlertControl),
        new FrameworkPropertyMetadata(default(bool),
            FrameworkPropertyMetadataOptions.None,
            OnIsAlertChanged,
            null,
            false,
            UpdateSourceTrigger.PropertyChanged));

public bool IsAlert
{
    get { return (bool)GetValue(IsAlertProperty); }
    set { SetValue(IsAlertProperty, value); }
}

In my Generic.xaml, I have the following xaml code:

<Style TargetType="{x:Type local:AlertControl}">
    <Setter Property="Template">
        <Setter.Value> ... </Setter.Value>
    </Setter>

    <Setter Property="ToolTip">
        <Setter.Value>
            <ToolTip Visiblity="{Binding Path=IsAlert,
                             RelativeSource={RelativeSource TemplatedParent},
                             Converter={StaticResource BooleanToVisiblityConverter}">
                <!-- Tooltip content goes here -->
            </ToolTip>
        </Setter.Value>
    </Setter>
<Style />

The problem is that this doesn't seem to work. I used Snoop to spy on my xaml, and the IsAlert property is changing appropriately, but if I delve into my AlertControl.ToolTip, I see that the Visiblity DependencyProperty has a binding error. I also tried using RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:AlertControl}}, but that also gave a binding issue. I don't know how else to diagnose this because my output window is not spitting out any binding expression errors either.

michael
  • 14,844
  • 28
  • 89
  • 177

3 Answers3

1

When it comes to a WPF ToolTip, you have to understand that it is not part of the VisualTree the same way other controls are. It only comes into play when it is necessary to create it, which is when your mouse is hovering over the control. At this time, WPF will create the ToolTip, but it will not place it as a child element of the AlertControl, which is why both of the RelativeSourceModes (TemplatedParent and FindAncestor) did not work.

There is one saving grace though, and that is the ToolTip.PlacementTarget property.

<ToolTip Visibility="{Binding Path=PlacementTarget.(local:AlertControl.IsAlert),
                              RelativeSource={RelativeSource Self},
                              Converter={...}}">
    ...
<ToolTip>

What you are doing here is telling the WPF BindingExtension to bind to the property named PlacementTarget (which happens to be the UIElement that created the ToolTip, the AlertControl in your situation), and you are stating to locate this property from the ToolTip itself (not the DataContext of the ToolTip). Beyond that, you are also planning to use a IValueConverter. Additionally, the full unparsed PropertyInfo not only looks for the PlacementTarget on the ToolTip, but also checks to see if the object returned from PlacementTarget can be cast as a type of AlertControl, and then access its IsAlert CLR property. I could have easily have done Path=PlacementTarget.IsAlert and, with reflection, it would have worked out just fine, but I prefer to explicitly state that the IsAlert property should be accessed from a type of AlertControl.

myermian
  • 31,823
  • 24
  • 123
  • 215
  • Thanks! This worked perfectly. I never knew that the tooltip wasn't created under my alert control. – michael Jun 10 '14 at 15:30
0

Did you also add the variable to your codebehind?

Like:

public bool IsAlert
    {
        get { return (bool)GetValue(IsAlertProperty); }
        set { SetValue(IsAlertProperty, value); }
    }

Using this should allow you to bind it without any hierarchic information. By the way, you can check easily if your binding is working by setting a breakpoint within the converter.

user2799180
  • 719
  • 1
  • 11
  • 29
  • @Sheridan: Actually, the WPF `BindingExtension` contains a property named `Path`, which happens to be a `PropertyInfo`. It uses the `PropertyInfo` and reflection to get the value of the CLR property, regardless of how that property is implemented. A `DependencyProperty` is just one way of implementing a CLR property, which will allow it to handle property value validation, coercion, and change notifications automatically, as well as provide the ability to be used in animations and the such. So, you *clearly* have absolutely no idea what you're talking about. – myermian Jun 10 '14 at 14:02
  • @Sheridan: The answer given was a valid, albeit incorrect, attempt to answer the question given the fact that the OP left out the CLR property from the question. So, it makes sense to think that perhaps that was what was missing. However, the comment about not needing the CLR property is clearly 100% wrong. The entire WPF Binding Engine works from CLR Properties, and it could care less how the property is actually implemented, so long as it provides the necessary get (and optionally set) methods with public access. – myermian Jun 10 '14 at 14:23
  • 1
    @Sheridan: *sigh*, if I could only downvote your comments. By all means, believe what you want to believe (even though it is 100% wrong), but accept that fact that you are wrong and move on. – myermian Jun 10 '14 at 15:29
0

Writing a CustomControl is unlike creating a basic UserControl. For a start, you don't have your own XAML file to define your control... you have to share the generic.xaml file. This often causes new developers problems when it comes to either data binding, or event handling. However, the solution is simple.

All you need to do is to use a RelativeSource Binding, not to the TemplatedParent, but to your control's property. Try this:

<ToolTip Visiblity="{Binding Path=IsAlert, RelativeSource={RelativeSource 
    AncestorType={x:Type YourXamlNamespacePrefix:AlertControl}}, 
    Converter={StaticResource BooleanToVisiblityConverter}">
    <!-- Tooltip content goes here -->
</ToolTip>
Sheridan
  • 68,826
  • 24
  • 143
  • 183
  • I already attempted to use the `AncestorType` mode, but that did not work. I used the Snoop tool to inspect what was going on, and I saw that the binding expression had an issue. Additionally, I rolled out my own converter so I could place a breakpoint in it, which worked in other areas of code, but not on this control. – michael Jun 10 '14 at 13:23