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
中显示一大堆staffmyListBox.ItemsSource = GetAllStaff();
很标准的东西。我遇到的问题是,显示某人存活的秒数的工具提示没有更新。当你第一次将鼠标移到一个员工上时,它会正常工作,但从那时起,它会永远保持这个值。我可以实现INotifyPropertyChanged来解决这个问题,但每当SecondsAlive改变时,为每个工作人员这样做似乎有点过分。假设我在列表中有400名员工,那么我必须引发400个事件,即使用户可能永远不会查看其他工具提示。我想要的是使工具提示每次显示时都请求SecondsAlive属性。这可能吗?
请注意,这只是一个例子,我不需要知道我的工作人员活了多少秒:-)但我有同样的问题,我需要提高一个甚至400次左右,只是为了一个工具提示,有人可能不会看。
天哪!我终于找到解决这个问题的办法了!!这事已经困扰我好几个月了。我并不惊讶没有人回答这个问题,因为我在顶部输入的代码实际上并没有显示我试图重现的问题,实际上它显示了解决方案。答案是,如果您像这样定义工具提示
<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>
我不太明白为什么需要这样做,或者为什么这样做会使它的行为不同,但它解决了问题。