WPF Показать данные из нескольких DataContexts в ToolTip of ItemsControl - PullRequest
7 голосов
/ 01 декабря 2009

Я пытаюсь отобразить всплывающую подсказку для элемента, сгенерированного 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>

Но, скажем, у меня есть другое свойство, к которому можно получить доступ через DataContext из ItemsControl. Есть ли способ сделать это из всплывающей подсказки? Например.,

<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, но любая помощь будет принята с благодарностью!

Ответы [ 5 ]

3 голосов
/ 01 декабря 2009

После часа растягивания волос я пришел к выводу, что вы не можете ссылаться на другой DataContext внутри DataTemplate для всплывающей подсказки . Для других Привязок это вполне возможно, как показали другие постеры. Вот почему вы не можете использовать трюк RelativeSource. То, что вы можете сделать, это реализовать статическое свойство для вашего класса Item и ссылаться на , что :

<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;}
     }
}
1 голос
/ 28 июня 2013

У меня была очень похожая проблема, и я пришел к этому вопросу в поисках ответов. В конце концов я нашел другое решение, которое сработало в моем случае и может быть полезным для других.

В моем решении я добавил свойство к дочернему элементу, которое ссылается на родительскую модель, и заполнил его при создании дочерних элементов. В 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>
1 голос
/ 06 февраля 2010

Это тот случай, когда я думаю, что концептуально более целесообразно делать это в модели представления, чем в любом случае. Предоставьте информацию всплывающей подсказки представлению как свойство элемента модели представления. Это позволяет представлению делать то, что у него хорошо (представление свойств элемента), а модель представления - то, что у него получается (решать, какую информацию следует представлять).

1 голос
/ 01 декабря 2009

Почти правильный Yacoder, и, как он там догадался, не прав Dabblernl;)

Ваш образ мышления правильный, и можно ссылаться на DataContext вашего ItemsControl

Вам не хватает свойства 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

Вот статья с той же проблемой. Они могут ссылаться на DataContext своего родительского элемента управления с помощью свойства PlacementTarget:

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

Если вы поместите DataContext на более глубокий уровень, вы не будете изменять свой ItemContext

Второе предложение (Нил и Адам Смит) состояло в том, что мы можем использовать PlacementTarget в привязке. Это хорошо, поскольку я на самом деле наследую DataContext уже со страницы, на которой размещен DataControl, и это позволило бы всплывающей подсказке получить доступ обратно к исходному элементу управления. Однако, как заметил Адам, вы должны знать о структуре родительских / дочерних элементов вашей разметки:

1 голос
/ 01 декабря 2009

Вторая попытка

Хорошо, относительная привязка источника не работает в этом случае . Это на самом деле работает из шаблона данных, вы можете найти много примеров этого в Интернете. Но здесь (вы были правы, Дэвид, в вашем комментарии) ToolTip - это специальный зверь, который неправильно размещен в VisualTree (это свойство, а не элемент управления как таковой), и он не имеет доступа к нужной области имен для используйте относительное связывание.

После еще одного поиска я нашел эту статью , в которой подробно описывается этот эффект и предлагается реализация BindableToolTip.

Это может быть излишним, потому что у вас есть другие варианты - например, использование статического свойства в классе (как в ответе Dabblernl) или добавление нового свойства экземпляра в ваш Item.

Первая попытка:)

Вы должны проконсультироваться с типами привязки относительного источника (например, в этом листе ):

Итак, ваша привязка будет выглядеть примерно так:

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