自定义控件模板绑定问题

发布于 2024-10-21 08:44:58 字数 5767 浏览 0 评论 0原文

想象一下,表单设计器具有表示平面上坐标的网格覆盖层。我正在尝试将网格覆盖层的属性绑定到自定义 ItemsControl 内的 Canvas

网格是使用 VisualBrush 创建的。 VisualBrushViewboxViewport 绑定到代码中的 Rect,以及 用于显示网格图块的矩形的>高度宽度。然而,当控件显示时,网格图块似乎“无限小”(网格只是灰色),因为如果我放大网格,程序最终将卡住,无法渲染它。显然,这不是我想要的效果。

<Style TargetType="{x:Type Controls:FormControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Controls:FormControl}">
                <Border Background="White"
                        BorderBrush="Black"
                        BorderThickness="1"
                        Padding="49">
                    <Grid Height="{TemplateBinding CanvasHeight}"
                          Width="{TemplateBinding CanvasWidth}">
                        <Grid.Background>
                            <!--"0,0,6,11.3266666666667"-->
                            <VisualBrush TileMode="Tile"
                                         Viewbox="{TemplateBinding GridUnitViewbox}"
                                         ViewboxUnits="Absolute"
                                         Viewport="{TemplateBinding GridUnitViewbox}"
                                         ViewportUnits="Absolute">
                                <VisualBrush.Visual>
                                    <Rectangle Height="{Binding RelativeSource={RelativeSource TemplatedParent},Path=GridUnitViewbox.Height}"
                                               HorizontalAlignment="Left"
                                               Opacity="{TemplateBinding GridOpacity}"
                                               Stroke="Black"
                                               StrokeThickness=".1"
                                               VerticalAlignment="Top"
                                               Width="{Binding RelativeSource={RelativeSource TemplatedParent},Path=GridUnitViewbox.Width}" />
                                </VisualBrush.Visual>
                            </VisualBrush>
                        </Grid.Background>
                        <ItemsPresenter />
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="Controls:FormControlItem">
                <Setter Property="Canvas.Left"
                        Value="{Binding Path=X}" />
                <Setter Property="Canvas.Top"
                        Value="{Binding Path=Y}" />
            </Style>
        </Setter.Value>
    </Setter>
</Style>

知道我在这里做错了什么吗?谢谢。

编辑: 也许更多了解我正在做的事情的背景可能会更好。我在一家税务准备软件公司工作,目前,我们的表单部门使用一百万年前为我们的产品编写的标记创建替代表单。以这种方式创建表单有点麻烦,所以我正在为他们开发一个可视化的“表单设计器”,它将更像是所见即所得,并将设计器的内容转换为标记。嗯,国税局对表格上的所有内容都严格要求与原始表格上的内容完全相同,因此有一个非常宽松的标准,可以在表格上放置“网格覆盖”来确定需要处理的内容;基本上是一个坐标平面。

FormControl 本质上是表单设计者之一将创建的这些替代表单之一的视觉表示。

CanvasWidthCanvasHeight 是依赖属性的 CLR 包装器。它们在 OnApplyTemplate() 中通过将 GridUnitViewbox 的尺寸乘以网格覆盖层中需要的网格图块数量来分配值,即。大多数情况下为 78x63 网格。

我认为名称 CanvasWidthCanvasHeight 可能有点误导,因为它们不是指 Canvas 控件,而是指 容纳 Canvas 的网格(可能需要更改命名约定)。也就是说,CanvasHeight、CanvasWidth 和 GridUnitViewbox 不依赖于任何控件的属性,而是依赖于 OnApplyTemplate() 中完成的计算

    public static readonly DependencyProperty GridUnitViewboxProperty =
        DependencyProperty.Register("GridUnitViewbox", typeof(Rect), typeof(FormControl),
        new FrameworkPropertyMetadata(new Rect(0, 0, 6, 11.3266666666667),
            FrameworkPropertyMetadataOptions.AffectsMeasure |
            FrameworkPropertyMetadataOptions.AffectsParentMeasure));

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        FormattedText formattedText = new FormattedText(
            "X",
            CultureInfo.GetCultureInfo("en-us"),
            FlowDirection.LeftToRight,
            new Typeface("Courier New"),
            10,
            Brushes.Black);

        this.GridUnitViewbox = new Rect(0, 0, formattedText.WidthIncludingTrailingWhitespace, formattedText.Height);

        if (this.PageLayout == PageLayoutType.Landscape)
        {
            this.CanvasHeight = this.GridUnitViewbox.Height * 48.0;
            this.CanvasWidth = this.GridUnitViewbox.Width * 109.0;
        }
        if (this.PageLayout == PageLayoutType.Portrait)
        {
            this.CanvasHeight = this.GridUnitViewbox.Height * 63.0;
            this.CanvasWidth = this.GridUnitViewbox.Width * 78.0;
        }
    }

Grid 控件实际上正在做我想要它做的事情。这是其中的 VisualBrushRectangle 未正确绑定或未正确更新。 下面的注释是我用于 VisualBrushViewbox的硬编码测试值>Viewport 以及 Rectangle 的尺寸,然后我开始绑定值,它在视觉上产生了我想要的结果。我还确认 6 和 11.32666666666667 实际上是运行时 GridUnitViewbox 尺寸的值。

然而,我有一种感觉,绑定正在生成“0,0,0,0”,因为网格覆盖只是一个灰色阴影,正在消耗大量资源;事实上,如果你放大它,就会锁定程序。正如您所看到的,在代码中,我尝试将 AffectsMeasureAffectsParentMeasure 添加到依赖项属性的元数据选项中,希望 UI 在 之后可能无法正确更新GridUnitViewbox 的尺寸已更新,但我错了。我不确定还可能是什么。

Imagine a form designer with a grid overlay that would represent coordinates on a plane. I'm trying to bind the properties of the grid overlay to the Canvas within a custom ItemsControl.

The grid is created using a VisualBrush. The VisualBrush's Viewbox and Viewport are bound to a Rect in the code, as well are the Height and Width of the Rectangle used to display the grid tile. However, when the control displays, the grid tiles seem to be "infinitely small" (the grid is just grey) in that if I zoom into the grid, the program will eventually just seize up, unable to render it. Obviously, this is not the effect I'm going for.

<Style TargetType="{x:Type Controls:FormControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Controls:FormControl}">
                <Border Background="White"
                        BorderBrush="Black"
                        BorderThickness="1"
                        Padding="49">
                    <Grid Height="{TemplateBinding CanvasHeight}"
                          Width="{TemplateBinding CanvasWidth}">
                        <Grid.Background>
                            <!--"0,0,6,11.3266666666667"-->
                            <VisualBrush TileMode="Tile"
                                         Viewbox="{TemplateBinding GridUnitViewbox}"
                                         ViewboxUnits="Absolute"
                                         Viewport="{TemplateBinding GridUnitViewbox}"
                                         ViewportUnits="Absolute">
                                <VisualBrush.Visual>
                                    <Rectangle Height="{Binding RelativeSource={RelativeSource TemplatedParent},Path=GridUnitViewbox.Height}"
                                               HorizontalAlignment="Left"
                                               Opacity="{TemplateBinding GridOpacity}"
                                               Stroke="Black"
                                               StrokeThickness=".1"
                                               VerticalAlignment="Top"
                                               Width="{Binding RelativeSource={RelativeSource TemplatedParent},Path=GridUnitViewbox.Width}" />
                                </VisualBrush.Visual>
                            </VisualBrush>
                        </Grid.Background>
                        <ItemsPresenter />
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
    <Setter Property="ItemContainerStyle">
        <Setter.Value>
            <Style TargetType="Controls:FormControlItem">
                <Setter Property="Canvas.Left"
                        Value="{Binding Path=X}" />
                <Setter Property="Canvas.Top"
                        Value="{Binding Path=Y}" />
            </Style>
        </Setter.Value>
    </Setter>
</Style>

Any idea what I am doing wrong here. Thanks.

EDIT:
Maybe a little more background of what I'm doing may put it in better context. I work for a tax preparation software company and, currently, our forms division creates substitute forms using a markup that was written for our product like a million years ago. It's a bit cumbersome creating forms this way, so I'm developing a visual "Form Designer" for them that will be more like a WYSIWYG and translate the contents of the designer into markup. Well, the IRS is real anal about everything on the form being EXACTLY where it was on the original, so there is a very loose standard by which a "grid overlay" can be placed over the form to determine where things need to go; basically a coordinate plane of sorts.

FormControl is essentially the visual representation of one of these substitute forms that one of the forms designers would be creating.

CanvasWidth and CanvasHeight are CLR wrappers to Dependency Properties. They are assigned values in OnApplyTemplate() by multiplying the dimensions of GridUnitViewbox by however many grid tiles need to be in the grid overlay, ie. a 78x63 grid in most cases.

The names CanvasWidth and CanvasHeight I think might be a little misleading in that they do not refer to the Canvas control, but to the Grid that houses the Canvas (probably need to change the naming convention). That said, CanvasHeight, CanvasWidth and GridUnitViewbox are not dependent on any control's properties, but rather calculations that are done in OnApplyTemplate().

    public static readonly DependencyProperty GridUnitViewboxProperty =
        DependencyProperty.Register("GridUnitViewbox", typeof(Rect), typeof(FormControl),
        new FrameworkPropertyMetadata(new Rect(0, 0, 6, 11.3266666666667),
            FrameworkPropertyMetadataOptions.AffectsMeasure |
            FrameworkPropertyMetadataOptions.AffectsParentMeasure));

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        FormattedText formattedText = new FormattedText(
            "X",
            CultureInfo.GetCultureInfo("en-us"),
            FlowDirection.LeftToRight,
            new Typeface("Courier New"),
            10,
            Brushes.Black);

        this.GridUnitViewbox = new Rect(0, 0, formattedText.WidthIncludingTrailingWhitespace, formattedText.Height);

        if (this.PageLayout == PageLayoutType.Landscape)
        {
            this.CanvasHeight = this.GridUnitViewbox.Height * 48.0;
            this.CanvasWidth = this.GridUnitViewbox.Width * 109.0;
        }
        if (this.PageLayout == PageLayoutType.Portrait)
        {
            this.CanvasHeight = this.GridUnitViewbox.Height * 63.0;
            this.CanvasWidth = this.GridUnitViewbox.Width * 78.0;
        }
    }

The Grid control is actually doing exactly what I want it to do. It is the VisualBrush and the Rectangle within that are either not binding correctly or are not being updated properly. The comment right below <Grid.Background> was the hard-coded testing value that I was using for VisualBrush's Viewbox and Viewport as well as Rectangle's dimensions before I got around to binding the values and it produced, visually, exactly what I was going for. I've also confirmed that 6 and 11.3266666666667 are, in fact, the values for the GridUnitViewbox's dimensions during runtime.

I have a feeling that the binding is producing '0,0,0,0' however, because the grid overlay is just a grey shading that is eating up an immense amount of resources; locks the program, in fact, if you zoom into it. As you can see, in the code I tried adding AffectsMeasure and AffectsParentMeasure to the Metadata options of the dependency property in hopes that perhaps the UI was not updating properly after GridUnitViewbox's dimensions were updated, but I was wrong. I'm not sure what else it could be.

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

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

发布评论

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

评论(2

想挽留 2024-10-28 08:44:59

好吧,以防万一其他人遇到类似的问题,我找到了解决方法。显然,VisualBrushTemplateBinding 有点挑剔。我因此调整了 XAML,它解决了问题:

<VisualBrush TileMode="Tile"
             Viewbox="{Binding RelativeSource={RelativeSource TemplatedParent},Path=GridUnitViewbox}"
             ViewboxUnits="Absolute"
             Viewport="{Binding RelativeSource={RelativeSource TemplatedParent},Path=GridUnitViewbox}"
             ViewportUnits="Absolute">

这是 文章 我从中获取信息。

Alright, just in case anyone else encounters a similar issue, I found the workaround. Apparently VisualBrush is a little finicky about TemplateBindings. I adjusted the XAML thusly and it solved the problem:

<VisualBrush TileMode="Tile"
             Viewbox="{Binding RelativeSource={RelativeSource TemplatedParent},Path=GridUnitViewbox}"
             ViewboxUnits="Absolute"
             Viewport="{Binding RelativeSource={RelativeSource TemplatedParent},Path=GridUnitViewbox}"
             ViewportUnits="Absolute">

Here is the article where I got the information from.

幸福%小乖 2024-10-28 08:44:59

什么是 CanvasWidth 和 CanvasHeight?它们被定义了吗?

另外,我相信除非明确指定,否则画布没有大小。当您绑定到它时,宽度/高度可能为零。

尝试 ActualWidth 和 ActualHeight 或渲染过程后的画布,看看它们是否包含任何值,但据我所知,除非您提供一个值,否则它们不会包含任何值。

What is CanvasWidth and CanvasHeight? Are they defined?

Also, I believe that a canvas has no size unless explicitly specified. When you bind to it, the width/height may be zero.

Try ActualWidth and ActualHeight or the canvas after the rendering pass to see if they contain any values, but afaik they don't unless you provide one.

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