ItemPresenter 使用 Canvas ItemPanel 混淆了 ItemTemplate 内的 PathGeometry 绝对位置

ItemPresenter messes with PathGeometry absolute position inside ItemTemplate with a Canvas ItemPanel

我有一个 ItemsControlCanvas 作为它的 ItemsPanelPath 作为 ItemTemplate。目标是绘制图形,因此 Path.Data 应包含要在 Canvas 中绘制的具有绝对坐标的几何图形。

如果我实例化一个 Canvas 并将路径直接放在其中,它工作正常。

但是如果我使用 ItemsControl,每个路径最终都会包裹在 ContentPresenter 中,然后坐标会丢失,因为 ContentPresenter 与 Canvas 原点对齐。

这是我的代码:

        <ItemsControl
            ItemsSource="{Binding Signals}"
        >
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Path 
                        Stroke="Red"
                        StrokeThickness="1" 
                        StrokeDashCap="Round" 
                        StrokeLineJoin="Round" 
                        Stretch="Fill" 
                        Data={Binding Converter=SignalToGeometryConverter}
                    />
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

如果您确实需要在 ItemsControl 中为 ItemsPanel 使用 Canvas,您确实需要填写parent,你可能处境艰难。如果您可以为 ItemsPanel 使用 Grid,那么您就大功告成了。

让您来到这里的不是 ContentPresenter,而是 Stretch。这导致 Path 填充它的 parent -- 但不是您所想的那样。 "M 100,100 L 200,200"None 以外的任何 Stretch 一起,将从 parent 的左上角开始画一条线。它将使用的边界框是路径几何实际使用的最大和最小 x 和 y 值。它假设您只关心您懒得去的地方。如果您要叠加具有不同左上角( 和右下角)边界的多条路径,它们将全部以不同方式缩放和偏移,从而对所有内容进行总哈希。

想想看,即使拉伸确实从 0,0 开始,它怎么知道右下边界使用什么?这些路径彼此不知道。如果你有一个路径,并将你当前的每一个东西作为一个不同的图形添加到它,那将是一回事。但这样一来,就没有他们知道的共同参考框架。

所以最快的解决方法是删除 Stretch="Fill",但这样就不会拉伸了。并且只要您将 Canvas 用于 ItemsPanel,您最好不要尝试拉伸路径,因为(据我通过测试可以看出)它们无论如何都不会拉伸那种情况。

如果您确实想要拉伸,首先您需要所有路径都具有相同的边界框,无论它们是否实际使用了整个边界框。这意味着首先计算你需要多少水平和垂直 space,并在每个路径数据前加上

M 0,0 M xmax,ymax

...在移动到 实际 点之前,您想要开始 Path

然后将您的 Canvas 更改为 Grid

这是我测试的代码:

XAML

<Grid>
    <ItemsControl
        ItemsSource="{Binding Signals, RelativeSource={RelativeSource AncestorType=Window}}"
        >
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Grid />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Path 
                    Stroke="DeepSkyBlue" 
                    StrokeThickness="1"
                    Data="{Binding}"
                    Stretch="Fill"
                    />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

C#

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        int left = 10;
        int top = 100;
        int bottom = 200;

        var signals = 
            Enumerable.Range(0, 19)
            .Select(n => $"M 0,0 M 300,300 M {(n * 10) + left},{top} L {(n * 15) + left},{bottom}")
            .ToList();

        //  Show shared bounding box
        signals.Add("M 0,0 L 300,0 L 300,300 L 0,300 Z");

        Signals = signals;
    }

    public IList Signals
    {
        get { return (IList)GetValue(SignalsProperty); }
        set { SetValue(SignalsProperty, value); }
    }

    public static readonly DependencyProperty SignalsProperty =
        DependencyProperty.Register("Signals", typeof(IList), typeof(MainWindow),
            new PropertyMetadata(null));
}