Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF ToolTip does not update

Tags:

c#

.net

binding

wpf

Assuming I have a simple class that represents a staff member

class Staff
{
    public string FirstName { get; set; }
    public string FamilyName { get; set; }
    public int SecondsAlive { get; set; }        
}

and I have a DataTemplate for staff

<DataTemplate DataType={x:Type Staff}>
    <StackPanel Orientation="Horizontal">
        <TextBlock Text={Binding FirstName}/>
        <TextBlock Text=" ">
        <TextBlock Text={Binding FamilyName}/>
        <StackPanel.ToolTip>
            <TextBlock Text={Binding SecondsAlive}/>
        </StackPanel.ToolTip>
     </StackPanel>
</DataTemplate>

I then show a whole bunch of staff in a ListBox

myListBox.ItemsSource = GetAllStaff();

Pretty standard stuff. The problem I have is that the tooltip which shows the number of seconds that someone has been alive does not get updated. When you first mouse over a staff member then it works fine but from then on it keeps that value for ever. I could implement INotifyPropertyChanged to get around this but it seems like overkill to do this for every staff member whenever SecondsAlive changes. Say I have 400 staff in the list then I have to raise 400 events even though the user might never look at another tooltip. What I would like is to make the tooltip request the SecondsAlive property ever time it is shown. Is that possible?

Please note that this is just an example and I don't need to know how many seconds my staff have been alive :-) But I have the same issue that I need to raise an even around 400 times just for a tooltip which someone probably won't look at.

like image 844
MikeKulls Avatar asked Jul 11 '11 03:07

MikeKulls


3 Answers

OMG!!! I have finally found the solution to this problem!!! This has been bugging me for months. I'm not surprised no one answered this because the code I typed out at the top actually DIDN'T show the problem I was trying to reproduce, in fact it showed the solution. The answer is that if you define your tooltip like this

    <StackPanel.ToolTip>
        <TextBlock Text="{Binding SecondsAlive}"/>
    </StackPanel.ToolTip>

Then everything works just fine and dandy and there is no need to raise a propertyChanged event on "SecondsAlive". The framework will call the SecondsAlive property every time the tooltip is shown. The problem comes when you define your tooltip like this:

    <StackPanel.ToolTip>
        <ToolTip>
            <TextBlock Text="{Binding SecondsAlive}"/>
        </ToolTip>
    </StackPanel.ToolTip>

Having the extra tooltip tag in there makes sense, surely you need to create a tooltip object to assign it to the tooltip property but this is incorrect. What you are assigning to the tooltip property is actually the content of the tooltip. I was assuming you needed to give it controls such as textblock and image to display but you can pass in anything and it will display the content just like a content control. Seeing it inherits from content control this makes sense :-) It all seems obvious once you know :-)

Thanks everyone for looking at this.

PS. I found an additional problem in that the next logical step in simplifying code is to just assign text straight to the tooltip like this (assuming your tooltip is plain text):

 <TextBlock Text="{Binding Path=StaffName}" ToolTip="{Binding Path=StaffToolTip}"/>

This also causes the original problem I was having. This makes sense because the results of the property StaffToolTip get assigned to the tooltip property and never get called again. However, it doesn't quite make sense why then assigning a TextBlock to the tooltip property actually solves the problem.

like image 187
MikeKulls Avatar answered Nov 17 '22 02:11

MikeKulls


In this particular case there is a cool trick you can use

Seconds Alive Now = Seconds Alive originally + Elapsed Time

You can bind to the Elapsed Time property and specify a converter that adds the initial value to it. That way you only need to raise 1 event and the tooltips would all be updated.

Edit: You can add the ElapsedTime property (with INotifyPropertyChanged) to many places -- one logical place could be to the collection that is storing your Staff objects

Edit: You would also need to bind each tooltip to the shared ElapsedTime property rather than the SecondsAlive property

like image 39
cordialgerm Avatar answered Nov 17 '22 02:11

cordialgerm


Although this is an old question.

In this case:

    <StackPanel.ToolTip>
        <ToolTip>
            <TextBlock Text="{Binding SecondsAlive}"/>
        </ToolTip>
    </StackPanel.ToolTip>

The ToolTip control is hosted by an isolate HWND (a.k.a, a native window). It should have its own DataContext, the correct binding expresion should be like:

    <StackPanel.ToolTip>
        <ToolTip 
            DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
            <TextBlock Text="{Binding SecondsAlive}"/>
        </ToolTip>
    </StackPanel.ToolTip>
like image 3
Doubleft Avatar answered Nov 17 '22 01:11

Doubleft