Прикрепленная привязка поведения к элементу в шаблоне управления - PullRequest
3 голосов
/ 02 апреля 2011

Я добавляю прикрепленное поведение к ползунку, который заставляет его прокручивать некоторый контент, когда большой палец перетаскивается и удерживается над определенной областью. (Невозможно использовать простой триггер IsMouseOver, так как у большого пальца слайдера есть MouseCapture.)

Поведение имеет 3 свойства:

    #region IsScrollHoverProperty
    public static readonly DependencyProperty IsScrollHoverProperty = DependencyProperty.RegisterAttached(
                                                "IsScrollHover",
                                                typeof(Boolean),
                                                typeof(ScrollHoverAreaBehaviour),
                                                new UIPropertyMetadata(false));
    #endregion

    #region ScrollLeftRectProperty
    public static readonly DependencyProperty ScrollLeftRectProperty = DependencyProperty.RegisterAttached(
                                                "ScrollLeftRect",
                                                typeof(Rectangle),
                                                typeof(ScrollHoverAreaBehaviour),
                                                new UIPropertyMetadata(null));
    #endregion

    #region ScrollRightRectProperty
    public static readonly DependencyProperty ScrollRightRectProperty = DependencyProperty.RegisterAttached(
                                                "ScrollRightRect",
                                                typeof(Rectangle),
                                                typeof(ScrollHoverAreaBehaviour),
                                                new UIPropertyMetadata(null));
    #endregion

IsScrollHoverProperty устанавливается в значение true, когда пользователь перетаскивает ползунок, все это делается в ControlTemplates.Triggers ползунка и работает правильно.

Когда установлено значение true, обратный вызов собирается подключить PreviewMouseEnterHandlers к двум прямоугольникам, чтобы определить, когда мышь вводит их.

Указанные прямоугольники также определены в шаблоне управления ползунка следующим образом:

        <StackPanel Grid.Row="0" Grid.RowSpan="3" HorizontalAlignment="Left" Orientation="Horizontal">
            <Rectangle  Width="40" Fill="#AAAAAAAA" Name="ScrollLeftRect"/>

            </StackPanel>
        <StackPanel Grid.Row="0" Grid.RowSpan="3" HorizontalAlignment="Right" Orientation="Horizontal">
            <Rectangle  Width="40" Fill="#AAAAAAAA" Name="ScrollRightRect"/>
         </StackPanel>

У меня проблема с привязкой этих прямоугольников к присоединенным свойствам ScrollRightRect и ScrollLeftRect. Я попробовал несколько вещей и подозреваю, что сделал глупую ошибку привязки или пытаюсь сделать что-то недопустимое. В настоящее время я связываю их в controltemplate.triggers следующим образом:

        <Trigger Property="local:ScrollHoverAreaBehaviour.IsScrollHover" Value="False">

            <Setter Property="local:ScrollHoverAreaBehaviour.ScrollLeftRect" Value="{Binding ElementName=ScrollLeftRect}"/>
            <Setter Property="local:ScrollHoverAreaBehaviour.ScrollRightRect" Value="{Binding ElementName=ScrollRightRect}"/>

            <Setter TargetName="ScrollLeftRect" Property="Fill" Value="Red"/>
            <Setter TargetName="ScrollRightRect" Property="Fill" Value="Red"/>
        </Trigger>

Я знаю, что этот триггер отключается, поскольку прямоугольники заполняют красный цвет, как и ожидалось. Кто-нибудь может заметить, что я делаю неправильно из этих фрагментов?

Заранее спасибо.

Rob

1 Ответ

13 голосов
/ 03 апреля 2011

Во-первых, давайте подтвердим, что вы не делаете ничего плохого, и проблема не имеет ничего общего с прикрепленным поведением.

<Button>
    <Button.Template>
        <ControlTemplate TargetType="Button">
            <Border Background="Yellow">
                <StackPanel>
                    <TextBlock x:Name="theText" Text="Hello" />
                    <ContentPresenter />
                </StackPanel>
            </Border>

            <ControlTemplate.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Content" Value="{Binding ElementName=theText, Path=Text}" />
                </Trigger>
            </ControlTemplate.Triggers>
        </ControlTemplate>
    </Button.Template>
</Button>

Этот фрагмент должен вызывать появление «Привет» дважды, когда я наводю курсор мыши на кнопку, но это не так, и я получаю ту же ошибку, что и вы:

System.Windows.Data Ошибка: 4: не удается найти источник для привязки со ссылкой «ElementName = theText». BindingExpression: Path = Text; DataItem = NULL; Целевым элементом является «Кнопка» (Имя = ''); Целевым свойством является «Содержимое» (тип «Объект»)

Это объяснимо - после того, как привязка установлена ​​на Button, он не сможет найти элемент управления с именем 'theText', потому что Button находится в другом NameScope .

Альтернатива

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

Начните с того, что дайте элементам управления имя - принято использовать префикс "PART_":

<Rectangle ... Name="PART_ScrollLeftRect" />

Теперь добавьте такой код в ваш обратный вызов, когда установлено IsScrollHover:

private static void IsScrollHoverSetCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    var target = (Slider) d;
    if ((bool)e.NewValue == false)
        return;

    target.ApplyTemplate();
    var leftRectangle = target.Template.FindName("PART_ScrollLeftRect", target);
    var rightRectangle = target.Template.FindName("PART_ScrollRightRect", target);

    // Do things with the rectangles
}

Обратите внимание, что в зависимости от того, когда установлено свойство IsScrollHost, шаблон может быть еще не готов. В этом случае вы можете подписаться на Loaded или подобное событие, а затем позвонить ApplyTemplate().

Хотя это может показаться более сложным, у него есть одно приятное преимущество: разметка будет проще. Дизайнеру, использующему Blend, не нужно будет забывать подключать эти сложные триггеры, он просто должен правильно называть элементы управления.

Использование префикса PART_ является соглашением WPF и обычно используется вместе с атрибутом TemplatePart . Примером этого является TextBox. При переопределении шаблона TextBox, он не будет работать, пока вы не добавите элемент управления с именем PART_ContentHost.

Обновление: я только что написал о деталях шаблона здесь: http://www.paulstovell.com/wpf-part-names

...