【问题标题】:WPF Show data from multiple DataContexts in ToolTip of ItemsControlWPF 在 ItemsControl 的 ToolTip 中显示来自多个 DataContexts 的数据
【发布时间】:2010-12-21 23:56:49
【问题描述】:

我正在尝试显示由ItemsControl 生成的项目的工具提示,该项目需要从概念上不相关的来源中提取数据。例如,假设我有一个 Item 类,如下所示:

public class Item
{
    public string ItemDescription { get; set; }
    public string ItemName { get; set; }
}

我可以在 ItemsControl 中显示带有工具提示的项目,如下所示:

<ItemsControl x:Name="itemsControl" ItemsSource="{Binding Items}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding ItemName}">
                <TextBlock.ToolTip>
                    <ToolTip>
                        <TextBlock Text="{Binding ItemDescription}" />
                    </ToolTip>
                </TextBlock.ToolTip>
            </TextBlock>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

但是假设我有另一个属性可以通过ItemsControlDataContext 访问。有没有办法从工具提示中做到这一点?例如,

<ItemsControl x:Name="itemsControl" ItemsSource="{Binding Items}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding ItemName}">
                <TextBlock.ToolTip>
                    <ToolTip>
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition />
                                <RowDefinition />
                            </Grid.RowDefinitions>
                            <TextBlock Text="{Binding ItemDescription}" />
                            <TextBlock Grid.Row="1" Text="{Bind this to another property of the ItemsControl DataContext}" />
                        </Grid>
                    </ToolTip>
                </TextBlock.ToolTip>
            </TextBlock>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

我使用的测试窗口的代码如下:

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();

        List<Item> itemList = new List<Item>() {
            new Item() { ItemName = "First Item", ItemDescription = "This is the first item." },
            new Item() { ItemName = "Second Item", ItemDescription = "This is the second item." } 
        };

        this.Items = itemList;
        this.GlobalText = "Something else for the tooltip.";
        this.DataContext = this;
    }

    public string GlobalText { get; private set; }

    public List<Item> Items { get; private set; }
}

所以在本例中,我想显示GlobalText 属性的值(实际上这将是另一个自定义对象)。

为了使事情复杂化,我实际上在使用 DataTemplates 并在 ItemsControl 中显示两种不同类型的对象,但我们将不胜感激任何帮助!

【问题讨论】:

    标签: wpf data-binding tooltip datacontext itemscontrol


    【解决方案1】:

    经过一个小时的拉扯,我确信您不能在 DataTemplate 中引用另一个 DataContext作为工具提示。正如其他海报所证明的那样,对于其他绑定是完全可能的。这就是为什么你也不能使用 RelativeSource 技巧。您可以做的是在您的 Item 类上实现一个静态属性并引用 that:

    <Window x:Class="ToolTipSpike.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="300" Width="300"
        Name="Root"
        xmlns:ToolTipSpike="clr-namespace:ToolTipSpike">
        <Grid>
            <ItemsControl x:Name="itemsControl" ItemsSource="{Binding Items}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding ItemName}"> 
                            <TextBlock.ToolTip>
                                <ToolTip>
                                    <Grid>
                                        <Grid.RowDefinitions>
                                            <RowDefinition />
                                            <RowDefinition />
                                        </Grid.RowDefinitions>
                                        <TextBlock Text="{Binding ItemDescription}" />
                                        <TextBlock Grid.Row="1" 
                       Text="{Binding Source={x:Static ToolTipSpike:Item.GlobalText},
                       Path=.}"
                                        />
                                    </Grid>
                                </ToolTip>
                            </TextBlock.ToolTip>
                        </TextBlock>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Grid>
    </Window>
    
    using System.Collections.Generic;
    using System.Windows;
    
    namespace ToolTipSpike
    {
        public partial class Window1 : Window
        {
    
            public List<Item> Items { get; private set; }
            public Window1()
            {
                InitializeComponent();
                var itemList = new List<Item>
                      {
                          new Item { ItemName = "First Item", ItemDescription = "This is the first item." },
                          new Item { ItemName = "Second Item", ItemDescription = "This is the second item." }
                      };
                this.Items = itemList;
                this.DataContext = this;
           }
        }
    
         public class Item
         {
             static Item()
             {
                 GlobalText = "Additional Text";
             }
             public static string GlobalText { get; set; }
             public string ItemName{ get; set;}
             public string ItemDescription{ get; set;}
         }
    }
    

    【讨论】:

    • 谢谢,我以为是这样。我想我要么将数据与原始项目相关联,要么使用 ObjectDataProvider。
    • 您可以使用RelativeSource 绑定!请参阅下面的答案
    • OOP 与 ToolTip 行为无关,但您的解决方法仍然有效,请参阅下面的答案。
    • 谢谢,但是datatempaten中的第二个datacontext是错误的,我有使用它的工作代码!突然失明……
    【解决方案2】:

    第二次尝试

    好的,相对源绑定不起作用在这种情况下。它实际上是通过数据模板工作的,您可以在 Internet 上找到许多这样的示例。但是在这里(你是对的,大卫,在你的评论中)工具提示是一个特殊的野兽,它没有正确放置在 VisualTree 中(它是一个属性,而不是一个控件本身)并且它无法访问正确的名称范围使用相对绑定。

    经过一番搜索,我找到了this article,它详细描述了这个效果,并提出了一个 BindableToolTip 的实现。

    这可能有点矫枉过正,因为您还有其他选择——例如在类上使用静态属性(如 Dabblernl 的响应中所示)或向您的 Item 添加新的实例属性。

    第一次尝试:)

    您应该咨询相对源绑定类型(例如this cheat sheet):

    所以你的绑定看起来和这个有点相似:

    {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path= GlobalText}
    

    【讨论】:

    • 感谢 Yacoder。我试过了,但没有用 - 我认为这是因为 ToolTip 弹出窗口与 ItemsControl 不在同一个可视化树中。
    • 如果尝试绑定到 AncestorType={x:Type Window} 会怎样?
    • 非常感谢 Yacoder 和 Arcturus。 BindableToolTip 看起来很有趣,但我想我会回到原来的解决方法,即向“Item”类添加额外信息。
    • 我相信这是一个很好的开始解决方案,稍后您可以根据需要尝试特殊的工具提示 =)
    【解决方案3】:

    Yacoder 几乎是正确的,但 Dabblernl 猜错了;)

    你的思路是对的,可以引用你的ItemsControl的DataContext

    您在路径中缺少 DataContext 属性:

    {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.GlobalText}
    

    第二次尝试;)

    http://blogs.msdn.com/tom_mathews/archive/2006/11/06/binding-a-tooltip-in-xaml.aspx

    这是一篇有同样问题的文章。他们可以通过 PlacementTarget 属性引用其父控件的 DataContext:

    <ToolTip DataContext=”{Binding RelativeSource={RelativeSource Self},Path=PlacementTarget.Parent}”>
    

    如果您将 DataContext 置于更深层次,则避免更改您的 Item DataContext

    第二个建议(Neil 和 Adam Smith)是我们可以在绑定中使用 PlacementTarget。这很好,因为我实际上已经从承载 DataControl 的页面继承了 DataContext,这将允许 ToolTip 重新获得对原始控件的访问权限。不过,正如 Adam 指出的那样,您必须了解标记中的父/子结构:

    【讨论】:

    • 如果我想直接在 ItemsControl 模板中显示信息,这会很好,但我不想 - 我想在 ToolTip 中显示它。此绑定对 ToolTip 根本不起作用。
    • Yacoder 在这一点上是正确的。工具提示是一种特殊的野兽,它不驻留在 VisualTree 中。在这种情况下,RelativeSource 绑定将不起作用。
    • 找到有趣的文章后编辑答案:)
    【解决方案4】:

    在这种情况下,我认为在视图模型中执行此操作比在视图中执行此操作在概念上更合适。将工具提示信息作为视图模型项的属性公开给视图。这让视图做它擅长的事情(呈现项目的属性),而视图模型做它擅长的事情(决定应该呈现什么信息)。

    【讨论】:

      【解决方案5】:

      我遇到了一个非常相似的问题,并来到这个问题寻求答案。最后我想出了一个不同的解决方案,它适用于我的情况,可能对其他人有用。

      在我的解决方案中,我向引用父模型的子项添加了一个属性,并在生成子项时填充它。在 ToolTip 的 XAML 中,我随后简单地从每个元素的父模型中引用了该属性,并将 DataContext 设置为父模型属性。

      我觉得这个解决方案比在 XAML 中创建代理元素并引用它们更舒服。

      使用此问题的示例代码,您将执行以下操作。注意我没有在编译器中测试过这个场景,但是已经成功地在我自己场景的代码中实现了这个解决方案。

      项目:

      public class Item
      {
          public List<Item> Parent { get; set; }
          public string ItemDescription { get; set; }
          public string ItemName { get; set; }
      }
      

      窗口:

      public partial class Window1 : Window
          {
              public Window1()
              {
                  InitializeComponent();
      
                  List<Item> itemList = new List<Item>();
                  itemList.Add(new Item() { Parent = this, ItemName = "First Item", ItemDescription = "This is the first item." });
                  itemList.Add(new Item() { Parent = this, ItemName = "Second Item", ItemDescription = "This is the second item." });
      
      
                  this.Items = itemList;
                  this.GlobalText = "Something else for the tooltip.";
                  this.DataContext = this;
              }
      
              public string GlobalText { get; private set; }
      
              public List<Item> Items { get; private set; }
          }
      

      XAML:

      <ItemsControl x:Name="itemsControl" ItemsSource="{Binding Items}">
          <ItemsControl.ItemTemplate>
              <DataTemplate>
                  <TextBlock Text="{Binding ItemName}">
                      <TextBlock.ToolTip>
                          <ToolTip>
                              <Grid>
                                  <Grid.RowDefinitions>
                                      <RowDefinition />
                                      <RowDefinition />
                                  </Grid.RowDefinitions>
                                  <TextBlock Text="{Binding ItemDescription}" />
                                  <TextBlock Grid.Row="1" DataContext={Binding Parent} Text="{Bind this to aproperty of the parent data model}" />
                              </Grid>
                          </ToolTip>
                      </TextBlock.ToolTip>
                  </TextBlock>
              </DataTemplate>
          </ItemsControl.ItemTemplate>
      </ItemsControl>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-05-28
        • 1970-01-01
        • 2023-03-10
        • 1970-01-01
        • 2021-06-04
        • 2011-04-28
        • 1970-01-01
        相关资源
        最近更新 更多