ItemPresenter 使用 Canvas ItemPanel 混淆了 ItemTemplate 内的 PathGeometry 绝对位置
ItemPresenter messes with PathGeometry absolute position inside ItemTemplate with a Canvas ItemPanel
我有一个 ItemsControl
,Canvas
作为它的 ItemsPanel
,Path
作为 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));
}
我有一个 ItemsControl
,Canvas
作为它的 ItemsPanel
,Path
作为 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));
}