WPF ItemsControlのツールチップに複数のDataContextsからのデータを表示
-
22-07-2019 - |
質問
概念的に無関係なソースからデータをプルする必要がある ItemsControl
によって生成されたアイテムのツールチップを表示しようとしています。たとえば、次のようなItemクラスがあるとします:
public class Item
{
public string ItemDescription { get; set; }
public string ItemName { get; set; }
}
次のようにツールチップでItemsControl内にItemを表示できます:
<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>
しかし、 ItemsControl
の DataContext
からアクセスできる別のプロパティがあるとします。ツールチップ内からこれを行う方法はありますか?例:
<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内に2つの異なるタイプのオブジェクトを表示していますが、どんな援助でも大歓迎です!
解決
1時間髪を引っ張った後、DataTemplate ToolTip 内で別の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;}
}
}
他のヒント
2回目の試行
OK、相対ソースバインディングは機能しませんこの場合。実際にはデータテンプレートから機能します。これについては、インターネットで多くの例を見つけることができます。しかし、ここ(あなたは正しい、デビッド、コメント)ToolTipはVisualTreeに正しく配置されない特別な獣(プロパティであり、それ自体はコントロールではありません)であり、適切な名前スコープへのアクセス権がありません相対バインディングを使用します。
さらに検索した結果、この記事が見つかりました。 BindableToolTipの実装を提案します。
クラスで静的プロパティを使用する(Dabbllernlの応答など)、または Item
に新しいインスタンスプロパティを追加するなど、他のオプションがあるため、やり過ぎかもしれません。
最初の試行:)
相対ソースバインディングタイプ(このチートシート例):
したがって、バインディングは次のようになります。
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path= GlobalText}
ほぼ正しいYacoder、およびDabblernlの間違った推測;)
あなたの考え方は正しく、ItemsControlのDataContextを参照することが可能です
パスにDataContextプロパティがありません:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.GlobalText}
2回目の試行;)
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の変更を避けます
2番目の提案(NeilとAdam Smith)は、バインディングでPlacementTargetを使用できるというものでした。 DataControlをホストするページから既にDataContextを継承しているため、これは便利です。これにより、ToolTipが元のコントロールにアクセスできるようになります。ただし、Adamが指摘したように、マークアップの親/子構造に注意する必要があります。
これは、とにかくビューよりもビューモデルでこれを行う方が概念的に適切だと思う場合です。ビューモデルアイテムのプロパティとしてツールチップ情報をビューに公開します。これにより、ビューが得意なこと(アイテムのプロパティを表示)を実行し、ビューモデルが得意なこと(表示する情報を決定する)を実行できます。
私は非常によく似た問題を抱えていて、答えを求めてこの質問にたどり着きました。最終的に、私は自分のケースで機能し、他の人に役立つかもしれない別のソリューションを思いつきました。
ソリューションでは、親モデルを参照するプロパティを子アイテムに追加し、子が生成されたときにそれを設定しました。 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>