DevGold
DevGold

Reputation: 39

Can't binding in ToolTip

I'm working with ToolTip in WPF C#. I want to bind an element property to ToolTip content, but the output is empty. Initially, I used the binding below, and it did not work.

Content="{Binding ElementName=txtf1, Path=Text}"

So I found related solutions here, but the result did not change. Can anyone point me where I'm going wrong?

<StackPanel Height="Auto" Margin="15,10,15,0" Width="74">
    <Image Source="/Image/AutoCAD.png" Width="50"/>
    <TextBlock x:Name="txtf1" Text="Border19072021 Border19072021"/>
    <StackPanel.ToolTip>
        <ToolTip DataContext="{Binding Path=PlacementTarget.DataContext , 
                              RelativeSource={RelativeSource Self}}">
            <Label Content="{Binding ElementName=txtf1, Path=Text}"/>
        </ToolTip>
     </StackPanel.ToolTip>
</StackPanel>

Upvotes: 0

Views: 1196

Answers (2)

EldHasp
EldHasp

Reputation: 7978

I reference here, How to binding other element in ToolTip

You have not carefully read the topic you are referring to.

ToolTip, ContextMenu, Popup (and some other elements) pop up over the contents of the Window without changing it.
Think for yourself: How can you introduce new elements into a Window without changing the visual tree of this Window?
Answer: this cannot be done in any way.

Therefore, these pop-up elements are implemented as the contents of a new small Window shown above the main Window (the one in which they were called).
And since these are DIFFERENT Windows, they have different visual trees.
Therefore, it is impossible to find elements from the main Window in the tree of the Pop-up Window (Bindings of the ElementName and FindAncestor types).

You can use an element reference (Binding Sourse = {x: Reference ...}), since references are resolved at compile time and without regard to the visual tree.

Example:

    <StackPanel Height="Auto" Margin="15,10,15,0" Width="74">
        <Image Source="/Image/AutoCAD.png" Width="50"/>
        <TextBlock x:Name="txtf1" Text="Border19072021 Border19072021"/>
        <StackPanel.ToolTip>
            <ToolTip>
                <Label Content="{Binding Source={x:Reference txtf1}, Path=Text}"/>
            </ToolTip>
         </StackPanel.ToolTip>
    </StackPanel>

In addition, the Popup's DataContext inherits from the element in which It is created.
In your case, this is from StackPanel.
Therefore, you can simply set the default Bindings to receive data.

Example:

    <StackPanel Height="Auto" Margin="15,10,15,0" Width="74">
        <Image Source="/Image/AutoCAD.png" Width="50"/>
        <TextBlock x:Name="txtf1" Text="{Binding SomeViewModelProperty}"/>
        <StackPanel.ToolTip>
            <ToolTip>
                <Label Content="{Binding SomeViewModelProperty}"/>
            </ToolTip>
         </StackPanel.ToolTip>
    </StackPanel>

The PlacementTarget property is used to change the target element for the popup.
DataContext inherits from this element.
If it is not specified, the DataContext is inherited from the element in which the flyout is specified.
You don't specify it, so it is null, which is what your binding returns.

Example:

<StackPanel DataContext="{Binding Text, ElementName=txtf1}" Height="Auto" Margin="15,10,15,0" Width="74">
   <Image Source="/Image/AutoCAD.png" Width="50"/>
   <TextBlock x:Name="txtf1" Text="{Binding SomeViewModelProperty}"/>
   <StackPanel.ToolTip>
      <ToolTip  PlacementTarget="{Binding ElementName=txtf1}"
                DataContext="{Binding Path=PlacementTarget.DataContext,
                                      RelativeSource={RelativeSource Self}}">
         <Label Content="{Binding SomeViewModelProperty}"/>
      </ToolTip>
   </StackPanel.ToolTip>
</StackPanel>

Upvotes: 1

thatguy
thatguy

Reputation: 22119

[...] initially, I use Content="{Binding ElementName=txtf1, Path=Text}" and it not work.

Yes, that is because each window in WPF has its own visual tree with elements and a ToolTip and other popups are actually displayed in a separate window. ElementName bindings do not work here (RelativeSource too), since these are different XAML namescopes.

The question you cited is the right approach, but you applied it the wrong way. In theory you can use the DataContext property as binding indirection for the ToolTip, but you should not.

<StackPanel DataContext="{Binding Text, ElementName=txtf1}" Height="Auto" Margin="15,10,15,0" Width="74">
   <Image Source="/Image/AutoCAD.png" Width="50"/>
   <TextBlock x:Name="txtf1" Text="Border19072021 Border19072021"/>
   <StackPanel.ToolTip>
      <ToolTip DataContext="{Binding Path=PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
         <Label Content="{Binding}"/>
      </ToolTip>
   </StackPanel.ToolTip>
</StackPanel>

The above code works, but the DataContext property serves a core purpose in WPF for data binding. Do not abuse this property for this purpose or you break data-binding down the visual tree. Apart from that, the binding on Label and ToolTip is redundant, as you can see below. There is another property called Tag that you can assign any value to.

Gets or sets an arbitrary object value that can be used to store custom information about this element.

<StackPanel Tag="{Binding Text, ElementName=txtf1}" Height="Auto" Margin="15,10,15,0" Width="74">
   <Image Source="/Image/AutoCAD.png" Width="50"/>
   <TextBlock x:Name="txtf1" Text="Border19072021 Border19072021"/>
   <StackPanel.ToolTip>
      <ToolTip>
         <Label Content="{Binding Path=PlacementTarget.Tag, RelativeSource={RelativeSource AncestorType={x:Type ToolTip}}}"/>
      </ToolTip>
   </StackPanel.ToolTip>
</StackPanel>

You can use a RelativeSource binding to refer to the ToolTip.Tag directly from the label. If there are multiple bindings like this, you could consider creating custom attached properties for each of them and bind them the same way.

Upvotes: 1

Related Questions