Привязка всплывающей подсказки к DependencyProperty внутри пользовательского элемента управления - PullRequest
1 голос
/ 06 марта 2019

Я пытаюсь привязать некоторые значения к ToolTipService.ShowDuration и некоторым другим свойствам всплывающей подсказки в CellStyle из DataGridTextColumn.

Обычно я делаю что-то вроде этого:

<UserControl
    ...namespace declarations...>
    <UserControl.Resources>
        <mycontrols:BindingProxy x:Key="proxy" Data="{Binding MySettings}"/>
    </UserControl.Resources>
    <DataGrid>
        <DataGridTextColumn
            Binding="{Binding SomeBinding}">
            <DataGridTextColumn.CellStyle>
                <Style 
                    TargetType="DataGridCell" 
                    BasedOn="{StaticResource ResourceKey={x:Type DataGridCell}}">
                    <Setter 
                        Property="ToolTipService.ShowDuration" 
                        Value="{Binding Data.ToolTipDuration, Source={StaticResource proxy}}"/>
                    <Setter Property="ToolTip">
                        <Setter.Value>
                            <TextBlock 
                                Text="{Binding SomeBinding}" 
                                MaxWidth="{Binding Data.ToolTipMaxWidth, Source={StaticResource proxy}}" 
                                TextWrapping="Wrap" TextTrimming="CharacterEllipsis"/>
                        </Setter.Value>
                    </Setter>
                </Style>
            </DataGridTextColumn.CellStyle>
        </DataGridTextColumn>
    </DataGrid>
</UserControl>

Поскольку ToolTip имеет свое собственное визуальное дерево, я использую прокси привязки, например, так:

public class BindingProxy : Freezable
    {
        protected override Freezable CreateInstanceCore()
        {
            return new BindingProxy();
        }

        public object Data
        {
            get { return (object)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Data.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
    }

До этого момента все работало, как ожидалось.Но я хотел повторно использовать этот DataGridTextColumn, поэтому я создал новый файл, подобный этому:

<DataGridTextColumn
    x:Class="Test.MyControls.DataGridLargeTextColumn"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Test.MyControls">
    <DataGridTextColumn.CellStyle>
        <Style 
            TargetType="DataGridCell" 
            BasedOn="{StaticResource ResourceKey={x:Type DataGridCell}}">
            <Setter 
                Property="ToolTipService.ShowDuration" 
                Value="{Binding ToolTipDuration}"/>
            <Setter Property="ToolTip">
                <Setter.Value>
                    <TextBlock 
                        Text="{Binding SomeBinding}" 
                        MaxWidth="{Binding ToolTipWidth}" 
                        TextWrapping="Wrap" TextTrimming="CharacterEllipsis"/>
                </Setter.Value>
            </Setter>
        </Style>
    </DataGridTextColumn.CellStyle>
</DataGridTextColumn>

С кодом behinde:

public partial class DataGridLargeTextColumn : DataGridTextColumn
{
    public int ToolTipDuration
    {
        get { return (int)GetValue(ToolTipDurationProperty); }
        set { SetValue(ToolTipDurationProperty, value); }
    }
    public static readonly DependencyProperty ToolTipDurationProperty =
        DependencyProperty.Register("ToolTipDuration", typeof(int), typeof(DataGridLargeTextColumn), new UIPropertyMetadata(default(int)));

    public string SomeBinding
    {
        get { return (string)GetValue(SomeBindingProperty); }
        set { SetValue(SomeBindingProperty, value); }
    }
    public static readonly DependencyProperty SomeBindingProperty =
        DependencyProperty.Register("SomeBinding", typeof(string), typeof(DataGridLargeTextColumn), new UIPropertyMetadata(default(string)));

    public int ToolTipWidth
    {
        get { return (int)GetValue(ToolTipWidthProperty); }
        set { SetValue(ToolTipWidthProperty, value); }
    }
    public static readonly DependencyProperty ToolTipWidthProperty =
        DependencyProperty.Register("ToolTipWidth", typeof(int), typeof(DataGridLargeTextColumn), new UIPropertyMetadata(default(int)));

    public DataGridLargeTextColumn()
    {
        InitializeComponent();
    }
}

Это не работает, потому что ToolTip имеет свою собственнуювизуальное дерево, но так как мне некуда поставить прокси, я не знаю, как заставить это работать или вообще возможно ли это.Я нашел этот ответ , и он, кажется, находится на правильном пути, однако я попытался реализовать его следующим образом, но безуспешно:

<Setter 
    Property="ToolTipService.ShowDuration"
    Value="{Binding Path=PlacementTarget.(local:DataGridLargeTextColumn.ToolTipDuration), RelativeSource={RelativeSource Self}}"/>
<Setter Property="ToolTip">
    <Setter.Value>
        <TextBlock 
            Text="{Binding Path=PlacementTarget.(local:DataGridLargeTextColumn.SomeBinding), RelativeSource={RelativeSource Self}}" 
            MaxWidth="{Binding Path=PlacementTarget.(local:DataGridLargeTextColumn.ToolTipWidth), RelativeSource={RelativeSource Self}}" 
            TextWrapping="Wrap" TextTrimming="CharacterEllipsis"/>
    </Setter.Value>
</Setter>

Я неправильно использую PlacementTarget?Если нет, то почему он не работает, и есть ли другое решение?

ОБНОВЛЕНИЕ:

Согласно ответу Марка, я изменил DataGridLargeTextColumnСтиль:

<Style
    TargetType="DataGridCell" 
    BasedOn="{StaticResource {x:Type DataGridCell}}">
    <Setter 
        Property="ToolTipService.ShowDuration" Value="{Binding Path=PlacementTarget.ToolTipShowDuration, RelativeSource={x:Static RelativeSource.Self}}"/>
    <Setter Property="ToolTip">
        <Setter.Value>
            <ToolTip DataContext="{Binding Path=PlacementTarget, RelativeSource={x:Static RelativeSource.Self}}">
                <TextBlock 
                    Text="{Binding DataContext.SomeBinding}"
                    MaxWidth="{Binding Column.ToolTipWidth}"
                    TextWrapping="Wrap" TextTrimming="CharacterEllipsis"/>
            </ToolTip>
        </Setter.Value>
    </Setter>
</Style>

И я использую этот элемент управления следующим образом:

<UserControl
    ...namespace declarations...>
    <UserControl.Resources>
        <mycontrols:BindingProxy x:Key="proxy" Data="{Binding MySettings}"/>
    </UserControl.Resources>
    <DataGrid>
        <DataGrid.Columns>
            <mycontrols:DataGridLargeTextColumn
                Binding="{Binding SomeBinding}"
                ToolTipShowDuration="{Binding Data.ToolTipDuration, Source={StaticResource proxy}}"
                ToolTipWidth="{Binding Data.ToolTipMaxWidth, Source={StaticResource proxy}}"/>
        </DataGrid.Columns>
    </DataGrid>
</UserControl>

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

  1. Я не могу привязать длительность всплывающей подсказки, я пробовал несколько разных подходов, но, поскольку она абстрактна, ее нельзя объявить явно * Свойство Text ToolTip *1041* установлено в SomeBinding, что нормально в данном конкретном случае, но я хочу иметь возможность установить его на что угодно, поэтому я попытался объявить его, используя DependencProperty сверху, например:

Text="{Binding Column.ToolTipText}"

Это работает нормально, если я использую его со строковым литералом:

<myControls:DataGridLargeTextColumn
    Binding="{Binding SomeBinding}"
    ToolTipText="12345"
    ToolTipShowDuration="{Binding Data.ToolTipDuration, Source={StaticResource proxy}}"
    ToolTipWidth="{Binding Data.ToolTipMaxWidth, Source={StaticResource proxy}}"/>

Но это не работает, когда я пытаюсь связать его, чего я и пытаюсь достичь:

<myControls:DataGridLargeTextColumn
    Binding="{Binding SomeBinding}"
    ToolTipText="{Binding SomeOtherPropertyBinding}" 
    ToolTipShowDuration="{Binding Data.ToolTipDuration, Source={StaticResource proxy}}"
    ToolTipWidth="{Binding Data.ToolTipMaxWidth, Source={StaticResource proxy}}"/>

1 Ответ

1 голос
/ 07 марта 2019

По умолчанию DataContext вашего ToolTip устанавливается равным DataContext ячейки. Однако вместо этого вы пытаетесь связать свойства зависимостей в столбце ячейки, поэтому вам придется изменить DataContext, чтобы он указывал на саму ячейку, а затем явно указывать DataContext, когда вы хотите получить доступ к данным и Column когда вы хотите получить доступ к DP в вашем DataGridLargeTextColumn.

Другими словами, объявите всплывающую подсказку в явном виде в дополнение к ее содержимому и установите ее DataContext, например:

<Setter Property="ToolTip">
    <Setter.Value>
        <ToolTip DataContext="{Binding Path=PlacementTarget, RelativeSource={x:Static RelativeSource.Self}}">
            <TextBlock 
                Text="{Binding DataContext.SomeBinding}"
                Width="{Binding Column.ToolTipWidth}" />
        </ToolTip>
    </Setter.Value>
</Setter>

... где Text в этом случае привязывается к данным, а Width привязывается к пользовательскому столбцу DP.

В качестве альтернативы вы также можете просто оставить DataContext как есть и вместо этого использовать свойство Tag всплывающей подсказки в качестве прокси-сервера привязки к DataGridLargeTextColumn:

<Setter Property="ToolTip">
    <Setter.Value>
        <ToolTip Tag="{Binding Path=PlacementTarget.Column, RelativeSource={x:Static RelativeSource.Self}}">
            <TextBlock 
                Text="{Binding SomeBinding}"
                Width="{Binding Tag.ToolTipWidth, RelativeSource={RelativeSource AncestorType=ToolTip}}" />
        </ToolTip>
    </Setter.Value>
</Setter>
...