附加行为绑定到 controltemplate 中的元素

发布于 2024-10-29 00:38:03 字数 2986 浏览 4 评论 0原文

我正在向滑块添加附加行为,这将导致当拇指被拖动并保持在特定区域上时滚动一些内容。 (不能使用简单的 IsMouseOver 触发器,因为 Slider Thumb 具有 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,这一切都在 Slider 的 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>

我知道当矩形按预期填充红色时,此触发器将被触发。 谁能从这些片段中发现我做错了什么?

提前致谢。

I am adding an attached behaviour to a slider which will cause it to scroll some content when the thumb is dragged and held over a specific region. (Can't use a straightforward IsMouseOver trigger as the Slider Thumb has MouseCapture.)

The behaviour has 3 properties:

    #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

The IsScrollHoverProperty is being set to true when the user drags the slider, this is all done in the Slider's ControlTemplates.Triggers, and works correctly.

When it's set to true the callback is going to hook PreviewMouseEnterHandlers into the two Rectangles to detect when the mouse enters them.

The Rectangles in question are also defined in the Slider's controltemplate thusly:

        <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>

The problem I have is binding these Rectangles to the attached ScrollRightRect and ScrollLeftRect Properties. I have tried a few things and suspect I have made a stupid binding error or am trying to do something not allowed. I am currently binding them in the controltemplate.triggers as follows:

        <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>

I know this Trigger is being tripped as the rectangles fill Red as expected.
Can anyone spot what I'm doing wrong from these snippets?

Thanks in advance.

Rob

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

街角迷惘 2024-11-05 00:38:03

首先,我们确认一下你没有做错什么,而且问题与附加行为无关。

<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>

当我将鼠标悬停在按钮上时,此代码片段应该会导致“Hello”出现两次,但事实并非如此,并且我得到了与您相同的错误:

System.Windows.Data 错误:4:找不到引用“ElementName=theText”的绑定源。绑定表达式:路径=文本;数据项=空;目标元素是“按钮”(名称=“”);目标属性是“内容”(类型“对象”)

这是可以解释的 - 一旦在 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 何时设置code> 属性已设置,模板可能尚未准备好。在这种情况下,您可能需要订阅 Loaded 或类似事件,然后调用 ApplyTemplate()

虽然它可能看起来更复杂,但它有一个很好的好处:标记会更简单。使用 Blend 的设计人员不必记住连接那些复杂的触发器,他们只需正确命名控件即可。

PART_ 前缀的使用是 WPF 约定,通常与 TemplatePart 属性。文本框就是一个例子。当您覆盖 TextBox 的模板时,它不会'直到您添加名为 PART_ContentHost 的控件为止,该函数才会起作用。

更新:我刚刚在此处发布了有关模板部分的博客:http://www.paulstovell.com/ wpf-部分名称

First, let's confirm you're not doing anything wrong, and the problem has nothing to do with the attached behaviors.

<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>

This snippet should cause "Hello" to appear twice when I mouse over the button, but it doesn't, and I get the same error as you:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=theText'. BindingExpression:Path=Text; DataItem=null; target element is 'Button' (Name=''); target property is 'Content' (type 'Object')

This is explainable - once the binding is set on the Button, it won't be able to find a control named 'theText', because the Button lives in a different NameScope.

An alternative

Some WPF controls need to do something similar to you - they assume that a specific control exists in the tree that they will interact with. But they don't use properties - they use names.

Start by giving the controls a name - the convention is to use "PART_" prefix:

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

Now put code like this in your callback when IsScrollHover is set:

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
}

Note that depending on when the IsScrollHost property is set, the template might not be ready yet. In that case, you might want to subscribe to the Loaded or similar event, and then call ApplyTemplate().

Although it might seem more complicated, it has one nice benefit: the markup will be simpler. A designer using Blend won't have to remember to wire up those complicated triggers, they just have to name the controls correctly.

The use of the PART_ prefix is a WPF convention, and normally used along with the TemplatePart attribute. An example of this is the TextBox. When you override the template of a TextBox, it won't function until you add a control named PART_ContentHost.

Update: I just blogged about template parts here: http://www.paulstovell.com/wpf-part-names

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文