WPF 在项目控件中 canvas 上的元素之间画线

WPF draw lines between elements on canvas in a itemscontrol

我想在项目控件中的多个椭圆之间画线。

最终结果应该是这样的。

问题是所有的圆都是在运行时绘制的。它们在 itemscontrol 中,其代码如下所示。

<ItemsControl x:Name="SpaceObjectsIC"
                      Grid.Row="0" Grid.Column="0"
                      ItemsSource="{Binding SpaceObjects, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}">
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas Background="LightGray" Width="800" Height="600" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Ellipse Fill="{Binding Color, Converter={StaticResource ColorConverter}}"
                             Stroke="{Binding Border, Converter={StaticResource ColorConverter}}" 
                             StrokeThickness="{Binding BorderThickness}"
                             Height="{Binding Diameter}" Width="{Binding Diameter}" Tag="{Binding Name}"
                             ClipToBounds="True">
                        <Ellipse.RenderTransform>
                            <TranslateTransform X="{Binding X}" Y="{Binding Y}" />
                        </Ellipse.RenderTransform>
                    </Ellipse>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

如您所见,我使用的是一个只能使用一个元素的数据模板。我不知道如何在 XAML 中解释我想在这些特定的省略号之间画线。

只有带有所谓'neighbours'的圆需要线条。如果您查看 xaml,您会看到绑定了一个名为 SpaceObjects 的列表。每个有邻居的空间物体都知道这些邻居(它们也是空间物体)。这意味着找到圆的 x 和 y 位置很容易。 我只是不知道如何在不破坏 MVVM 规则的情况下在它们之间动态绘制这些线,而且我也不想使用代码隐藏。

xaml 的数据上下文是一个名为 SpaceViewModel 的 clise,它包含一个带有空间对象的 ObservableCollection。

SpaceObject 代码如下所示(我省略了属性,但只知道它们确实存在):

public class SpaceObject : ISpaceObject, INotifyPropertyChanged
{
    private decimal _x;
    private decimal _y;
    private List<SpaceObject> _spaceNeighbours = new List<SpaceObject>();
}

既然我已经获得了我需要的所有数据,我该如何让 Xaml 做我想做的事?

从您的项目源绑定中删除 , UpdateSourceTrigger=PropertyChanged。

使 SpaceObjects 成为 ObservableCollection

将您当前的数据模板移动到项目控件的资源中。使用 Datatemplate DataType="{x:Type YourPlanetVM}"

将其与您当前的视图模型类型相关联

定义一个新的视图模型,其属性指定行开始 X 和 Y 以及结束 X 和 Y。

计算 x 和 y 的开始和结束并在那些视图模型上设置它们。 首先将这些添加到 observablecollection 中,因为这会影响 z-index。大概你想要你的线条在行星下面。

使用 ItemContainerStyle 定位行星

  <ItemsControl.ItemContainerStyle>
         <Style>
              <Setter Property="Canvas.Top" Value="{Binding X}"/>
              <Setter Property="Canvas.Left" Value="{Binding Y}"/>

您的 LineVM 需要将这些设置为 0 它还需要属性绑定到 Line:

https://docs.microsoft.com/en-us/dotnet/api/system.windows.shapes.line?view=netcore-3.1

您需要 linevm 的数据模板具有开始和结束 x、y 和描边(画笔)和描边厚度。

我建议为您的路径使用椭圆几何。这以 X,Y co-ordinates 为中心,而不是定位在左上角。类似这样的数据模板(但有一些硬编码值)看起来像:

                                    <Path  
                                                Height="2"
                                                Width="2"
                                                Fill="Black">
                                        <Path.Data>
                                            <EllipseGeometry 
                                                            RadiusX="1" 
                                                            RadiusY="1"
                                                                       />
                                        </Path.Data>
                                    </Path>