WPF工具提示不更新

本文关键字:更新 工具提示 WPF | 更新日期: 2023-09-27 18:01:37

假设我有一个代表员工的简单类

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

和我有一个DataTemplate为员工

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

然后在ListBox

中显示一大堆staff
myListBox.ItemsSource = GetAllStaff();

很标准的东西。我遇到的问题是,显示某人存活的秒数的工具提示没有更新。当你第一次将鼠标移到一个员工上时,它会正常工作,但从那时起,它会永远保持这个值。我可以实现INotifyPropertyChanged来解决这个问题,但每当SecondsAlive改变时,为每个工作人员这样做似乎有点过分。假设我在列表中有400名员工,那么我必须引发400个事件,即使用户可能永远不会查看其他工具提示。我想要的是使工具提示每次显示时都请求SecondsAlive属性。这可能吗?

请注意,这只是一个例子,我不需要知道我的工作人员活了多少秒:-)但我有同样的问题,我需要提高一个甚至400次左右,只是为了一个工具提示,有人可能不会看。

WPF工具提示不更新

天哪!我终于找到解决这个问题的办法了!!这事已经困扰我好几个月了。我并不惊讶没有人回答这个问题,因为我在顶部输入的代码实际上并没有显示我试图重现的问题,实际上它显示了解决方案。答案是,如果您像这样定义工具提示

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

然后一切都工作得很好,没有必要在"SecondsAlive"上引发propertyChanged事件。框架将在每次显示工具提示时调用SecondsAlive属性。当您像这样定义工具提示时,问题就出现了:

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

在那里有额外的工具提示标签是有意义的,当然你需要创建一个工具提示对象来分配它的工具提示属性,但这是不正确的。您分配给工具提示属性的实际上是工具提示的内容。我假设你需要给它控件,如textblock和图像显示,但你可以传入任何东西,它会显示内容就像一个内容控件。看到它继承自内容控制,这是有意义的:-)这一切似乎都很明显,一旦你知道:-)

谢谢大家看这个。

p。我发现了另一个问题,简化代码的下一个逻辑步骤是直接将文本分配给工具提示,就像这样(假设您的工具提示是纯文本):

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

这也导致了我最初遇到的问题。这是有意义的,因为属性StaffToolTip的结果被分配给tooltip属性,并且再也不会被调用。然而,为什么将TextBlock分配给tooltip属性实际上解决了问题,这并不是很有意义。

虽然这是一个老问题。

在本例中:

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

ToolTip控件由隔离的HWND(也就是本机窗口)承载。它应该有自己的DataContext,正确的绑定表达式应该是:

    <StackPanel.ToolTip>
        <ToolTip 
            DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
            <TextBlock Text="{Binding SecondsAlive}"/>
        </ToolTip>
    </StackPanel.ToolTip>

在这种情况下,有一个很酷的技巧可以使用

Seconds Alive Now = Seconds Alive original + Elapsed Time

您可以绑定到Elapsed Time属性,并指定一个将初始值添加到该属性的转换器。这样你只需要触发一个事件,工具提示就会全部更新。

编辑:您可以将ElapsedTime属性(使用INotifyPropertyChanged)添加到许多地方—一个逻辑位置可能是存储Staff对象的集合

编辑:您还需要将每个工具提示绑定到共享的ElapsedTime属性,而不是SecondsAlive属性

值得注意的是,在用新数据重新加载自己之前,工具提示似乎会检查它所绑定的对象是否相等。

在我的例子中,我重写了
public override bool Equals(object obj) 

public override int GetHashCode()

上的属性

public class MultipleNameObject { string Name, string[] OtherNames};

不幸的是,我只对multienameobject的Name属性做了一个string.Compare(),以达到相等的目的。工具提示应该在ItemsControl中显示OtherNames,但如果鼠标悬停在网格上的前一个MultipleNameObject上的Name相等,即使OtherNames不同,也不会更新。

[edit]在启用调试的情况下运行确认工具提示正在使用我的对象的GetHashCode()重写来决定是否抓取回新数据。

这个例子展示了如何在网格中添加一个工具提示,当用户将鼠标悬停在网格中的一个单元格上时,该工具提示会根据需要重新计算。

如果你有一个包含10,000个项目的巨大网格,并且你想要持续更新网格,但是你不想持续更新工具提示,因为这很耗时,这是有用的。

这个例子是针对Infragistics的,但这个原则同样适用于其他优秀的库,如DevExpress。

Xaml

<ig:TemplateColumn Key="ColumnKey">
    <ig:TemplateColumn.HeaderTemplate>
        <DataTemplate>
            <TextBlock Text="Header"></TextBlock>
        </DataTemplate>
    </ig:TemplateColumn.HeaderTemplate>
    <ig:TemplateColumn.ItemTemplate>
        <DataTemplate>
            <StackPanel HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                <StackPanel.ToolTip>
                    <TextBlock Text="{Binding ToolTip}"/>
                </StackPanel.ToolTip>
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="MouseEnter" >
                        <i:InvokeCommandAction Command="{Binding ToolTipHoverRefreshCommand}" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>
            </StackPanel>
        </DataTemplate>
    </ig:TemplateColumn.ItemTemplate>
</ig:TemplateColumn>

ViewModel

public ICommand ToolTipHoverRefreshCommand => new DelegateCommand(() =>
{
    this.OnPropertyChanged(this.ToolTip);
});  
public string ToolTip
{
    get
    {
        return DateTime.Now.ToString();
    }
    set
    {
        this.OnPropertyChanged(nameof(this.ToolTip));
    }
}

我有同样的问题,它不更新。我发现解决方案是将控件添加到ToolTip模板:

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

我不太明白为什么需要这样做,或者为什么这样做会使它的行为不同,但它解决了问题。