WPF Mostrar dados de várias DataContexts na dica de ItemsControl
-
22-07-2019 - |
Pergunta
Eu estou tentando exibir uma dica de ferramenta para um item gerado por um ItemsControl
que as necessidades para extrair dados a partir de fontes conceitualmente não relacionados. Por exemplo, digamos que eu tenho uma classe Item da seguinte forma:
public class Item
{
public string ItemDescription { get; set; }
public string ItemName { get; set; }
}
I pode exibir o item dentro de um ItemsControl com uma dica de ferramenta como segue:
<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>
Mas digamos que eu tenho uma outra propriedade que pode ser acessado através do DataContext
do ItemsControl
. Existe alguma maneira de fazer isso de dentro da dica? Por exemplo.,
<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>
O código para o teste Janela I utilizado é a seguinte:
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; }
}
Assim, neste exemplo eu quero mostrar o valor da propriedade GlobalText
(na realidade, isso seria um outro objeto personalizado).
Para assuntos complicar, estou realmente usando DataTemplates e mostrar dois tipos diferentes de objetos dentro do ItemsControl, mas qualquer ajuda seria muito apreciada!
Solução
Depois de uma hora de puxar cabelo Eu vim para a convicção de que você não pode fazer referência a outro DataContext dentro de um DataTemplate para uma dica de ferramenta ??em>. Para outras ligações é perfeitamente possível, como outros cartazes têm provado. É por isso que você não pode usar o truque RelativeSource quer. O que você pode fazer é implementar uma propriedade estática em sua classe Item e referência que :
<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;}
}
}
Outras dicas
segunda tentativa
Ok, a Fonte Relativa ligação não trabalho , neste caso, . Ele realmente funciona a partir de um modelo de dados, você pode encontrar muitos exemplos disso nos Internet. Mas aqui (você estava certo, David, em seu comentário) dica de ferramenta é uma besta especial que não está colocada corretamente na VisualTree (é uma propriedade, não um controle de per se) e que não tem acesso ao escopo nome próprio para usar relativa vinculativo.
Depois de algum mais procura achei este artigo , que descreve este efeito em detalhes e propõe uma implementação de um BindableToolTip.
Pode ser um exagero, porque você tem outras opções -. Como o uso de uma propriedade estática em uma classe (como na resposta de Dabblernl) ou adicionar uma nova propriedade de instância para o seu Item
Primeira tentativa:)
Você deve consultar com a Fonte Relativa tipos de ligação (em este folha de fraude para exemplo):
Assim sua ligação irá procurar de alguma forma semelhante a esta:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path= GlobalText}
Yacoder Quase correto, e maneira errada adivinhou há Dabblernl;)
A sua maneira de pensar está correta e é possível referenciar o DataContext do seu ItemsControl
Está faltando a propriedade DataContext no caminho:
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ItemsControl}}, Path=DataContext.GlobalText}
segunda tentativa;)
http: / /blogs.msdn.com/tom_mathews/archive/2006/11/06/binding-a-tooltip-in-xaml.aspx
Aqui está um artigo com o mesmo problema. Eles podem referenciar o DataContext de sua controle pai pela propriedade PlacementTarget:
<ToolTip DataContext=”{Binding RelativeSource={RelativeSource Self},Path=PlacementTarget.Parent}”>
Se você colocaria o DataContext em um nível mais profundo, você evita mudar seu item DataContext
A segunda sugestão (Neil e Adam Smith) foi que poderíamos usar PlacementTarget na ligação. Isso é bom, como eu estou realmente herdar o DataContext já a partir da página que hospeda o DataControl, e isso permitiria que a dica de ferramenta para ter acesso de volta ao controle origial. Como Adam notou, porém, você tem que estar ciente da estrutura pai / filho fora de sua marcação:
Este é um caso em que eu acho que é conceitualmente mais apropriada para fazer isso no modelo de vista do que é na visualização de qualquer maneira. Expor as informações dica para a vista como uma propriedade do item de vista do modelo. Que permite a visualização fazer o que é bom em (apresentando propriedades do item) e o modelo de vista fazer o que é bom em (decidir que informação deve ser apresentada).
Eu tive um problema muito semelhante e chegou a esta pergunta em busca de respostas. No final, eu vim com uma solução diferente, que trabalhou no meu caso e pode ser útil para os outros.
Na minha solução, eu adicionei uma propriedade para o item filho que faz referência ao modelo pai, e preenchida quando as crianças foram gerados. No XAML para a dica de ferramenta, então eu simplesmente referenciada a propriedade do modelo pai em cada elemento e definir o DataContext para a propriedade modelo de pai.
Eu me senti mais confortável com esta solução de criar elementos de proxy no XAML e referenciá-los.
Usando o código de exemplo para esta pergunta, você faria o seguinte. Nota eu não testei este cenário em um compilador, mas o fizeram com sucesso implementado esta solução no código para o meu próprio cenário.
Item:
public class Item
{
public List<Item> Parent { get; set; }
public string ItemDescription { get; set; }
public string ItemName { get; set; }
}
Janela:
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>