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>
我想在项目控件中的多个椭圆之间画线。
最终结果应该是这样的。
问题是所有的圆都是在运行时绘制的。它们在 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>