У меня проблема с попыткой отобразить всплывающую подсказку для элементов списка в WPF. Итак, вот что у меня есть для основного управления:
<Grid>
<Grid.Resources>
<DataTemplate x:Key = "ItemDataTemplate">
<TextBlock Text = "{Binding .}" />
</DataTemplate>
<local:ItemValueToTooltipConverter x:Key = "MyItemValueToTooltipConverter" />
<local:ItemValueToTooltipVisibilityConverter x:Key = "MyItemValueToTooltipVisibilityConverter" />
</Grid.Resources>
<ListBox
x:Name = "ItemsListBox"
ItemTemplate = "{DynamicResource ItemDataTemplate}"
ItemsSource = "{Binding MyItems}">
<ListBox.ItemContainerStyle>
<Style TargetType = "{x:Type ListBoxItem}">
<Setter Property = "Tag" Value = "{Binding ElementName=ItemsListBox, Path=DataContext}"></Setter>
<Setter Property = "ToolTip">
<Setter.Value>
<ToolTip DataContext = "{Binding RelativeSource = {RelativeSource Mode=Self}, Path=PlacementTarget}">
<ToolTip.Visibility>
<MultiBinding Converter = "{StaticResource MyItemValueToTooltipVisibilityConverter}">
<Binding Path = "Content"></Binding>
<Binding Path = "Tag.ItemValueTooltipProvider"></Binding>
</MultiBinding>
</ToolTip.Visibility>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter = "{StaticResource MyItemValueToTooltipConverter}">
<Binding Path = "Content"></Binding>
<Binding Path = "Tag.ItemValueTooltipProvider"></Binding>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</ToolTip>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
Есть список, связанный с некоторыми элементами и стилем для listboxitem, я хочу отобразить определенный текст во всплывающей подсказке на основе содержимого listboxitem.
ItemValueToTooltipConverter: использует значение listboxitem и Tag.ItemValueTooltipProvider для генерации текста, который я хочу показать во всплывающей подсказке.
ItemValueToTooltipVisibilityConverter: если текст для всплывающей подсказки пуст, для параметра «Видимость» установлено значение «Свернутый», в противном случае — «Видимый».
ItemValueTooltipProvider: объект в DataContext, используемый для преобразования текста listboxitem в текст всплывающей подсказки.
Для этого примера проекта я инициализировал список со следующими значениями:
MyItems.Add(new ItemValue("1"));
MyItems.Add(new ItemValue("2"));
MyItems.Add(new ItemValue("3"));
MyItems.Add(new ItemValue("4"));
и жестко закодировал Tag.ItemValueTooltipProvider, чтобы возвращать этот текст для всплывающей подсказки:
if (input == "1")
{
return "";
}
if (input == "2")
{
return "some more text";
}
if (input == "3")
{
return "350";
}
return input;
Теперь происходит то, что когда я навожу курсор на элемент: 2, он показывает всплывающую подсказку с «еще немного текста»
затем, когда я наводил курсор на элемент: 1, он не показывает всплывающую подсказку, что хорошо, так как я свернул для пустого текста
но затем, когда я наводил курсор на элемент: 2, он снова показывает пустую всплывающую подсказку
и когда я навожу курсор на элемент: 3, он показывает правильную всплывающую подсказку
После этого при наведении курсора на элементы 2,3,4 будет отображаться правильная всплывающая подсказка, пока я не наведу указатель мыши на 1, и он снова не начнет давать сбой.
Я пытался поместить стили ListBoxItem и ToolTip в ресурсы, сделать их x: Shared = False, а затем использовать их как DynamicResource, но это не удалось: невозможно преобразовать объект типа «System.String» в тип «Система». .Стиль.Windows.
Затем я попытался установить ControlTemplate для всплывающей подсказки, и это сработало (правильный текст показывался все время), но затем я потерял стиль всплывающей подсказки (в реальном случае всплывающие подсказки также используют стиль от стороннего библиотека), и мне пришлось бы моделировать элемент управления всплывающей подсказки.
Так что на данный момент я не уверен, должен ли я выбрать маршрут ControlTemplate, или есть что-то более простое, что исправило бы это.
@Tarazed Я пробовал иметь свойство для текста всплывающей подсказки, а затем инициализировать с ним элементы в начале и просто привязываться к этому свойству для всплывающей подсказки, но проблема не исчезла.
Итак, после долгой борьбы с WPF я, наконец, заставил его работать.
Сначала я попытался создать DataTemplate для ToolTip, указав его как x:Shared="False", а затем использовать его как DynamicResource в свойстве ContentTemplate ToolTip. Но у этого все еще были проблемы, поскольку, если первое наведение произойдет над элементом 1 (со свернутой подсказкой), я получу исключение:
System.InvalidCastException: 'Unable to cast object of type 'System.String' to type 'System.Windows.DataTemplate'.'
Затем я подумал, давайте посмотрим, что я мог бы сделать, если бы использовал ContentTemplateSelector, и это сработало.
сначала часть xaml:
<DataTemplate x:Key = "ToolTipContTemplate" x:Shared = "False">
<TextBlock>
<TextBlock.Text>
<Binding RelativeSource = "{RelativeSource Mode=FindAncestor, AncestorType = {x:Type ToolTip}}" Path = "DataContext" Converter = "{StaticResource MyItemValueToTooltipConverter}"></Binding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
<local:MyTemplateSelector x:Key = "MyTemplateSelector" MyTemplate = "{StaticResource ToolTipContTemplate}" x:Shared = "False"></local:MyTemplateSelector>
...
<Setter Property = "ToolTip">
<Setter.Value>
<ToolTip DataContext = "{Binding RelativeSource = {RelativeSource Mode=Self}, Path=PlacementTarget}" ContentTemplateSelector = "{DynamicResource MyTemplateSelector}">
<ToolTip.Visibility>
<MultiBinding Converter = "{StaticResource MyItemValueToTooltipVisibilityConverter}">
<Binding Path = "Content"></Binding>
<Binding Path = "Tag.ItemValueTooltipProvider"></Binding>
</MultiBinding>
</ToolTip.Visibility>
</ToolTip>
</Setter.Value>
</Setter>
затем очень простой селектор шаблонов:
public class MyTemplateSelector : DataTemplateSelector
{
public DataTemplate MyTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
return MyTemplate;
}
}
и немного работы со свойствами на стороне преобразователя:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || value == DependencyProperty.UnsetValue)
{
return "";
}
var tooltipProvider = ((value as ListBoxItem)?.Tag as MainWindowVm)?.ItemValueTooltipProvider;
var itemValue = (value as ListBoxItem)?.DataContext as ItemValue;
return tooltipProvider?.GetTooltip(itemValue?.Value);
}
Также обязательным было использование x:Shared="False" для ресурсов и их использование в качестве DynamicResource.
Я не вижу немедленного решения вашей проблемы, поэтому я не собираюсь публиковать это как ответ, я, честно говоря, не уверен, что происходит. Однако я бы рассмотрел альтернативную стратегию, которая в любом случае могла бы решить проблему. Рассмотрите возможность создания структуры со значением и всплывающей подсказкой в качестве свойств. Используйте ListBox.DisplayMemberPath, чтобы выбрать свойство для визуализации (значение), а затем привязать всплывающую подсказку в шаблоне данных текстового блока к свойству всплывающей подсказки. Это избавляет вас от необходимости использовать поставщик всплывающей подсказки. Тогда у вас есть только триггеры видимости. Это также более устойчиво для добавления элементов.