* + SetEdgesDrag

* + Edge가 Drag 가능하도록 설정

* + Edge Drag

* Fix

* + PrepareEdgePathFromMousePointer Target Position Edit
This commit is contained in:
LaborJos 2017-05-21 20:31:42 +09:00 коммит произвёл Alexander Smirnov
Родитель 23ca31e797
Коммит 0f2eb18e49
17 изменённых файлов: 815 добавлений и 338 удалений

Просмотреть файл

@ -289,6 +289,7 @@ namespace ShowcaseApp.WPF.Pages
erg_Area.GenerateGraph(gr, true);
erg_Area.SetVerticesDrag(true, true);
erg_Area.SetEdgesDrag(true);
erg_zoomctrl.ZoomToFill();
return;

Просмотреть файл

@ -138,6 +138,7 @@ namespace ShowcaseApp.WPF.Pages
_opMode = EditorOperationMode.Select;
ClearEditMode();
graphArea.SetVerticesDrag(true, true);
graphArea.SetEdgesDrag(true);
return;
}
}

Просмотреть файл

@ -49,6 +49,7 @@ namespace ShowcaseApp.WPF.Pages
gg_Area.RelayoutFinished += gg_Area_RelayoutFinished;
gg_Area.GenerateGraphFinished += gg_Area_GenerateGraphFinished;
gg_Area.VertexLabelFactory = new DefaultVertexlabelFactory();
gg_Area.SetEdgesDrag(true);
ggLogic.DefaultEdgeRoutingAlgorithm = EdgeRoutingAlgorithmTypeEnum.SimpleER;
ggLogic.EdgeCurvingEnabled = true;

Просмотреть файл

@ -1,63 +1,73 @@
<UserControl x:Class="ShowcaseApp.WPF.Pages.Mini.LayoutVCP"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:wpf="clr-namespace:ShowcaseApp.WPF"
xmlns:controls="http://schemas.panthernet.ru/graphx/"
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="clr-namespace:ShowcaseApp.WPF.Models"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="600">
xmlns:mui="http://firstfloorsoftware.com/ModernUI"
xmlns:wpf="clr-namespace:ShowcaseApp.WPF"
d:DesignHeight="300"
d:DesignWidth="600"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Templates/MiniFeatures.xaml"/>
<ResourceDictionary Source="/Templates/Mini/LayoutVCPTemplate.xaml"/>
<ResourceDictionary Source="/Templates/MiniFeatures.xaml" />
<ResourceDictionary Source="/Templates/Mini/LayoutVCPTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Grid.Resources>
<Border BorderBrush="{DynamicResource ButtonBorder}" BorderThickness="1" Margin="2" >
<controls:ZoomControl x:Name="zoomControl" Background="{DynamicResource ButtonBackground}" Foreground="{DynamicResource ButtonText}" HorizontalContentAlignment="Center" VerticalAlignment="Center">
<wpf:GraphAreaExample x:Name="graphArea"/>
<Border Margin="2"
BorderBrush="{DynamicResource ButtonBorder}"
BorderThickness="1">
<controls:ZoomControl x:Name="zoomControl"
VerticalAlignment="Center"
HorizontalContentAlignment="Center"
Background="{DynamicResource ButtonBackground}"
Foreground="{DynamicResource ButtonText}">
<wpf:GraphAreaExample x:Name="graphArea" />
</controls:ZoomControl>
</Border>
<Expander VerticalAlignment="Bottom"
Margin="2"
IsExpanded="false"
ExpandDirection="Down"
Header="SETTINGS"
Template="{DynamicResource MiniExpander}"
Padding="5">
<StackPanel Orientation="Vertical" MinHeight="50" VerticalAlignment="Center"
Margin="10">
<CheckBox Height="25"
Name="cbMathShape"
IsChecked="True"
Content ="Use math shape for connector"
ToolTip="If math shape is specified an edge will connect to the point around some &#10;math shape. If not it will simply point to connector center."
/>
<Button Content="Add VCP from runtime" Margin="2" Name="butAddVcp" HorizontalAlignment="Left"/>
<Expander Margin="2"
VerticalAlignment="Bottom"
ExpandDirection="Down"
Header="SETTINGS"
IsExpanded="false"
Padding="5"
Template="{DynamicResource MiniExpander}">
<StackPanel MinHeight="50"
Margin="10"
VerticalAlignment="Center"
Orientation="Vertical">
<CheckBox Name="cbMathShape"
Height="25"
Content="Use math shape for connector"
IsChecked="True"
ToolTip="If math shape is specified an edge will connect to the point around some &#10;math shape. If not it will simply point to connector center." />
<Button Name="butAddVcp"
Margin="2"
HorizontalAlignment="Left"
Content="Add VCP from runtime" />
</StackPanel>
</Expander>
<Border HorizontalAlignment="Right"
VerticalAlignment="Top"
<Border Margin="0,2,2,0"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="{DynamicResource WindowBackground}"
BorderBrush="{DynamicResource ButtonBorder}"
BorderThickness="1"
CornerRadius="0,0,0,15"
Margin="0,2,2,0">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<mui:ModernButton Style="{DynamicResource MainControlButtonHelp}"
Command="models:LinkCommands.ShowMiniSpecialDialog"
BorderBrush="{DynamicResource ButtonBorder}"
BorderThickness="1"
CornerRadius="0,0,0,15">
<StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
<mui:ModernButton Command="models:LinkCommands.ShowMiniSpecialDialog"
CommandParameter="{x:Static models:MiniSpecialType.LayoutVCP}"
/>
Style="{DynamicResource MainControlButtonHelp}" />
</StackPanel>
</Border>
</Grid>

Просмотреть файл

@ -54,7 +54,7 @@ namespace ShowcaseApp.WPF.Pages.Mini
private void CbMathShapeOnChecked(object sender, RoutedEventArgs routedEventArgs)
{
foreach(var item in graphArea.VertexList.Values)
item.VertexConnectionPointsList.ForEach(a => a.Shape = cbMathShape.IsChecked == true ? VertexShape.Circle : VertexShape.None);
item.VertexConnectionPointsList.ForEach(a => a.Shape = cbMathShape.IsChecked == true ? VertexShape.Circle : VertexShape.None);
graphArea.UpdateAllEdges(true);
}
@ -85,7 +85,7 @@ namespace ShowcaseApp.WPF.Pages.Mini
ShowcaseHelper.AddEdge(logicCore.Graph, vList[5], vList[2], 2, 3);
graphArea.LogicCore = logicCore;
//set positions
//set positions
var posList = new Dictionary<DataVertex, Point>()
{
{vList[0], new Point(0, 0)},
@ -99,6 +99,7 @@ namespace ShowcaseApp.WPF.Pages.Mini
//settings
graphArea.SetVerticesDrag(true, true);
graphArea.SetEdgesDrag(true);
zoomControl.ZoomToFill();
}

Просмотреть файл

@ -1,42 +1,53 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="http://schemas.panthernet.ru/graphx/">
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="http://schemas.panthernet.ru/graphx/">
<!--<Style TargetType="{x:Type local:GraphAreaExample}">
<Setter Property="Background" Value="LightGray"/>
</Style>-->
</Style>-->
<Style TargetType="controls:StaticVertexConnectionPoint" x:Shared="False">
<Setter Property="Shape" Value="Circle"/>
<Setter Property="Padding" Value="0"/>
<Style x:Shared="False" TargetType="controls:StaticVertexConnectionPoint">
<Setter Property="Shape" Value="Circle" />
<Setter Property="Padding" Value="0" />
<Setter Property="Content">
<Setter.Value>
<Path Stroke="Black" Data="M 38,0.791664C 58.5496,0.791664 75.2083,17.4504 75.2083,38C 75.2083,58.5496 58.5496,75.2083 38,75.2083C 17.4504,75.2083 0.791662,58.5496 0.791662,38C 0.791662,17.4504 17.4504,0.791664 38,0.791664 Z M 38,5.54166C 20.0738,5.54166 5.54167,20.0738 5.54167,38C 5.54167,55.9262 20.0738,70.4583 38,70.4583C 55.9262,70.4583 70.4583,55.9262 70.4583,38C 70.4583,20.0738 55.9262,5.54166 38,5.54166 Z "
Width="10" Height="10" Stretch="Uniform" StrokeThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Path Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 38,0.791664C 58.5496,0.791664 75.2083,17.4504 75.2083,38C 75.2083,58.5496 58.5496,75.2083 38,75.2083C 17.4504,75.2083 0.791662,58.5496 0.791662,38C 0.791662,17.4504 17.4504,0.791664 38,0.791664 Z M 38,5.54166C 20.0738,5.54166 5.54167,20.0738 5.54167,38C 5.54167,55.9262 20.0738,70.4583 38,70.4583C 55.9262,70.4583 70.4583,55.9262 70.4583,38C 70.4583,20.0738 55.9262,5.54166 38,5.54166 Z "
Stretch="Uniform"
Stroke="Black"
StrokeThickness="1" />
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type controls:VertexControl}">
<Setter Property="Background" Value="Yellow"/>
<Setter Property="Background" Value="Yellow" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:VertexControl}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Name="bb" Background="{TemplateBinding Background}" Grid.Column="1"
BorderBrush="{TemplateBinding BorderBrush}" Width="20" Height="20" BorderThickness="1">
</Border>
<StackPanel Orientation="Vertical" Grid.Column="0" Name="PART_vcproot">
<Border Padding="0" Margin="2,2,0,2">
<controls:StaticVertexConnectionPoint Id="1"/>
<StackPanel Name="PART_vcproot"
Grid.Column="0"
Orientation="Vertical">
<Border Margin="2,2,0,2" Padding="0">
<controls:StaticVertexConnectionPoint Id="1" />
</Border>
</StackPanel>
<Border Name="bb"
Grid.Column="1"
Width="20"
Height="20"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="1" />
</Grid>
</ControlTemplate>
</Setter.Value>
@ -44,11 +55,13 @@
<Style.Triggers>
<DataTrigger Binding="{Binding IsBlue}" Value="True">
<DataTrigger.Setters>
<Setter Property="Background" Value="Blue"/>
<Setter Property="Background" Value="Blue" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:VertexControl}">
<Ellipse Width="50" Height="50" Fill="{TemplateBinding Background}"/>
<Ellipse Width="50"
Height="50"
Fill="{TemplateBinding Background}" />
</ControlTemplate>
</Setter.Value>
</Setter>
@ -59,71 +72,89 @@
<controls:VisibilityToBoolConverter x:Key="VisibilityToBoolConverter" Inverted="True" />
<controls:VisibilityToBoolConverter x:Key="VisibilityToBoolConverterNot" Inverted="True" Not="True"/>
<controls:VisibilityToBoolConverter x:Key="VisibilityToBoolConverterNot"
Inverted="True"
Not="True" />
<Style TargetType="{x:Type controls:EdgeControl}">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Foreground" Value="Black" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:EdgeControl}">
<Grid>
<Path Stroke="{TemplateBinding Foreground}"
StrokeThickness="1"
x:Name="PART_edgePath"/>
<controls:DefaultEdgePointer NeedRotation="true" x:Name="PART_EdgePointerForSource" Visibility="{Binding ArrowTarget, Converter={StaticResource VisibilityToBoolConverterNot}}">
<Path Data="M0,0.5 L1,1 1,0" Fill="Black" Stretch="Uniform" Width="7" Height="7" />
<Path x:Name="PART_edgePath"
Stroke="{TemplateBinding Foreground}"
StrokeThickness="1" />
<controls:DefaultEdgePointer x:Name="PART_EdgePointerForSource"
NeedRotation="true"
Visibility="{Binding ArrowTarget,
Converter={StaticResource VisibilityToBoolConverterNot}}">
<Path Width="7"
Height="7"
Data="M0,0.5 L1,1 1,0"
Fill="Black"
Stretch="Uniform" />
</controls:DefaultEdgePointer>
<controls:DefaultEdgePointer NeedRotation="true" x:Name="PART_EdgePointerForTarget" Visibility="{Binding ArrowTarget, Converter={StaticResource VisibilityToBoolConverter}}">
<Path Data="M0,0.5 L1,1 1,0" Fill="Black" Stretch="Uniform" Width="7" Height="7"/>
<controls:DefaultEdgePointer x:Name="PART_EdgePointerForTarget"
NeedRotation="true"
Visibility="{Binding ArrowTarget,
Converter={StaticResource VisibilityToBoolConverter}}">
<Path Width="7"
Height="7"
Data="M0,0.5 L1,1 1,0"
Fill="Black"
Stretch="Uniform" />
</controls:DefaultEdgePointer>
<!-- <Path x:Name="PART_SelfLoopedEdge"
Width="10"
Height="10"
Stretch="Uniform"
Fill="{TemplateBinding Foreground}"
Data="F1 M 17.4167,32.25L 32.9107,32.25L 38,18L 43.0893,32.25L 58.5833,32.25L 45.6798,41.4944L 51.4583,56L 38,48.0833L 26.125,56L 30.5979,41.7104L 17.4167,32.25 Z "/>
-->
<!-- <Path x:Name="PART_SelfLoopedEdge"
Width="10"
Height="10"
Stretch="Uniform"
Fill="{TemplateBinding Foreground}"
Data="F1 M 17.4167,32.25L 32.9107,32.25L 38,18L 43.0893,32.25L 58.5833,32.25L 45.6798,41.4944L 51.4583,56L 38,48.0833L 26.125,56L 30.5979,41.7104L 17.4167,32.25 Z "/> -->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- REGION EDGE LABLE CONTROL -->
<!-- REGION EDGE LABLE CONTROL -->
<Style TargetType="{x:Type controls:EdgeLabelControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:EdgeLabelControl}">
<Grid>
<Border BorderBrush="Black" BorderThickness="1" Background="White" CornerRadius="8">
<ContentPresenter Margin="3"/>
<Border Background="White"
BorderBrush="Black"
BorderThickness="1"
CornerRadius="8">
<ContentPresenter Margin="3" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="DisplayForSelfLoopedEdges" Value="False"/>
<Setter Property="DisplayForSelfLoopedEdges" Value="False" />
</Style>
<!-- ENDREGION -->
<!-- ENDREGION -->
<!-- REGION VERTEX LABEL CONTROL -->
<!-- REGION VERTEX LABEL CONTROL -->
<Style TargetType="controls:VertexLabelControl">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Foreground" Value="White"/>
<Setter Property="LabelPositionMode" Value="Sides"/>
<Setter Property="LabelPositionSide" Value="BottomRight"/>
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="White" />
<Setter Property="LabelPositionMode" Value="Sides" />
<Setter Property="LabelPositionSide" Value="BottomRight" />
<!-- Custom label template body -->
<!-- Custom label template body -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:VertexLabelControl">
<Grid>
<ContentPresenter Margin="3"/>
<ContentPresenter Margin="3" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- ENDREGION -->
<!-- ENDREGION -->
</ResourceDictionary>

Просмотреть файл

@ -1,65 +1,76 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ShowcaseApp.WPF"
xmlns:controls="http://schemas.panthernet.ru/graphx/">
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="http://schemas.panthernet.ru/graphx/"
xmlns:local="clr-namespace:ShowcaseApp.WPF">
<local:ValueToImageConverter x:Key="ValueToImage"/>
<!-- VERTEX CONTROL -->
<local:ValueToImageConverter x:Key="ValueToImage" />
<!-- VERTEX CONTROL -->
<Style TargetType="{x:Type controls:VertexControl}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="VertexShape" Value="Circle"/>
<Setter Property="ShowLabel" Value="True"/>
<Setter Property="Background" Value="Transparent" />
<Setter Property="VertexShape" Value="Circle" />
<Setter Property="ShowLabel" Value="True" />
<Setter Property="Template">
<Setter.Value>
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:VertexControl}">
<Grid>
<Image Source="{Binding ImageId, Converter={StaticResource ValueToImage}}" Margin="0" Width="80" Stretch="UniformToFill"/>
<Image Width="80"
Margin="0"
Source="{Binding ImageId,
Converter={StaticResource ValueToImage}}"
Stretch="UniformToFill" />
<!-- removed in favor of new attachable vertex label logic
<controls:VertexLabelControl x:Name="PART_vertexLabel" Content="{Binding Vertex.Text, RelativeSource={RelativeSource TemplatedParent}}" LabelPositionSide="Bottom" />-->
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Setter.Value>
</Setter>
<!-- VERTEX DRAGGING CONTROL -->
<Setter Property="controls:DragBehaviour.IsDragEnabled"
Value="True" />
<Setter Property="controls:DragBehaviour.UpdateEdgesOnMove"
Value="True" />
<!-- VERTEX DRAGGING CONTROL -->
<Setter Property="controls:DragBehaviour.IsDragEnabled" Value="True" />
<Setter Property="controls:DragBehaviour.UpdateEdgesOnMove" Value="True" />
</Style>
<controls:VisibilityToBoolConverter Inverted="True" Not="True" x:Key="BooleanToVisibility"/>
<controls:VisibilityToBoolConverter x:Key="BooleanToVisibility"
Inverted="True"
Not="True" />
<!-- EDGE CONTROL -->
<!-- EDGE CONTROL -->
<Style TargetType="{x:Type controls:EdgeControl}">
<Setter Property="SelfLoopIndicatorOffset" Value="10,10"/>
<Setter Property="SelfLoopIndicatorOffset" Value="10,10" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:EdgeControl}">
<Grid>
<Path Stroke="#FF976969"
<Path x:Name="PART_edgePath"
Stroke="#FF976969"
StrokeThickness="3"
ToolTip="{TemplateBinding ToolTip}"
x:Name="PART_edgePath"/>
<controls:DefaultEdgePointer NeedRotation="true" x:Name="PART_EdgePointerForTarget" >
<Path Data="M0,0.5 L1,1 1,0" Fill="Black" Stretch="Uniform" Width="15" Height="15"/>
ToolTip="{TemplateBinding ToolTip}" />
<controls:DefaultEdgePointer x:Name="PART_EdgePointerForTarget" NeedRotation="true">
<Path Width="15"
Height="15"
Data="M0,0.5 L1,1 1,0"
Fill="Black"
Stretch="Uniform" />
</controls:DefaultEdgePointer>
<Path x:Name="PART_SelfLoopedEdge"
Width="10"
Height="10"
Stretch="Uniform"
Fill="Black"
<controls:DefaultEdgePointer x:Name="PART_EdgePointerForSource" NeedRotation="true">
<Path Width="15"
Height="15"
Data="M2,0.5 L1,1 1,0"
Fill="Red"
Stretch="Uniform" />
</controls:DefaultEdgePointer>
<Path x:Name="PART_SelfLoopedEdge"
Width="10"
Height="10"
Data="F1 M 17.4167,32.25L 32.9107,32.25L 38,18L 43.0893,32.25L 58.5833,32.25L 45.6798,41.4944L 51.4583,56L 38,48.0833L 26.125,56L 30.5979,41.7104L 17.4167,32.25 Z "
Fill="Black"
IsHitTestVisible="True"
ToolTip="This vertex has some self looped edges!"
Data="F1 M 17.4167,32.25L 32.9107,32.25L 38,18L 43.0893,32.25L 58.5833,32.25L 45.6798,41.4944L 51.4583,56L 38,48.0833L 26.125,56L 30.5979,41.7104L 17.4167,32.25 Z "/>
Stretch="Uniform"
ToolTip="This vertex has some self looped edges!" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

Просмотреть файл

@ -1,47 +1,61 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="http://schemas.panthernet.ru/graphx/"
>
xmlns:controls="http://schemas.panthernet.ru/graphx/">
<Style TargetType="controls:StaticVertexConnectionPoint" x:Shared="False">
<Setter Property="Shape" Value="Circle"/>
<Style x:Shared="False" TargetType="controls:StaticVertexConnectionPoint">
<Setter Property="Shape" Value="Circle" />
<Setter Property="Content">
<Setter.Value>
<Path Stroke="{DynamicResource ButtonText}" Data="M 38,0.791664C 58.5496,0.791664 75.2083,17.4504 75.2083,38C 75.2083,58.5496 58.5496,75.2083 38,75.2083C 17.4504,75.2083 0.791662,58.5496 0.791662,38C 0.791662,17.4504 17.4504,0.791664 38,0.791664 Z M 38,5.54166C 20.0738,5.54166 5.54167,20.0738 5.54167,38C 5.54167,55.9262 20.0738,70.4583 38,70.4583C 55.9262,70.4583 70.4583,55.9262 70.4583,38C 70.4583,20.0738 55.9262,5.54166 38,5.54166 Z "
Width="10" Height="10" Stretch="Uniform" StrokeThickness="1" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Path Width="10"
Height="10"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 38,0.791664C 58.5496,0.791664 75.2083,17.4504 75.2083,38C 75.2083,58.5496 58.5496,75.2083 38,75.2083C 17.4504,75.2083 0.791662,58.5496 0.791662,38C 0.791662,17.4504 17.4504,0.791664 38,0.791664 Z M 38,5.54166C 20.0738,5.54166 5.54167,20.0738 5.54167,38C 5.54167,55.9262 20.0738,70.4583 38,70.4583C 55.9262,70.4583 70.4583,55.9262 70.4583,38C 70.4583,20.0738 55.9262,5.54166 38,5.54166 Z "
Stretch="Uniform"
Stroke="{DynamicResource ButtonText}"
StrokeThickness="1" />
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type controls:VertexControl}">
<Setter Property="Foreground" Value="{DynamicResource ButtonText}"/>
<Setter Property="Foreground" Value="{DynamicResource ButtonText}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:VertexControl}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border BorderThickness="1" BorderBrush="{TemplateBinding Foreground}" Background="{DynamicResource ButtonBackgroundPressed}" Grid.Column="1" CornerRadius="5" Width="120">
<ContentPresenter Content="{TemplateBinding Vertex}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<StackPanel Name="PART_vcproot"
Grid.Column="0"
Orientation="Vertical">
<Border Margin="2,2,0,2" Padding="0">
<controls:StaticVertexConnectionPoint Id="1" />
</Border>
<Border Margin="2,2,0,2" Padding="0">
<controls:StaticVertexConnectionPoint Id="2" />
</Border>
</StackPanel>
<Border Grid.Column="1"
Width="120"
Background="{DynamicResource ButtonBackgroundPressed}"
BorderBrush="{TemplateBinding Foreground}"
BorderThickness="1"
CornerRadius="5">
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Vertex}" />
</Border>
<StackPanel Orientation="Vertical" Grid.Column="0" Name="PART_vcproot">
<Border Padding="0" Margin="2,2,0,2">
<controls:StaticVertexConnectionPoint Id="1"/>
<StackPanel Grid.Column="2" Orientation="Vertical">
<Border Margin="0,2,2,2" Padding="0">
<controls:StaticVertexConnectionPoint Id="3" />
</Border>
<Border Padding="0" Margin="2,2,0,2">
<controls:StaticVertexConnectionPoint Id="2"/>
</Border>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Column="2">
<Border Padding="0" Margin="0,2,2,2">
<controls:StaticVertexConnectionPoint Id="3"/>
</Border>
<Border Padding="0" Margin="0,2,2,2">
<controls:StaticVertexConnectionPoint Id="4"/>
<Border Margin="0,2,2,2" Padding="0">
<controls:StaticVertexConnectionPoint Id="4" />
</Border>
</StackPanel>
</Grid>
@ -51,16 +65,20 @@
</Style>
<Style TargetType="{x:Type controls:EdgeControl}">
<Setter Property="Foreground" Value="{DynamicResource ButtonText}"/>
<Setter Property="Foreground" Value="{DynamicResource ButtonText}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:EdgeControl}">
<Grid>
<Path Stroke="{TemplateBinding Foreground}"
StrokeThickness="2"
x:Name="PART_edgePath"/>
<controls:DefaultEdgePointer NeedRotation="true" x:Name="PART_EdgePointerForTarget" >
<Path Data="M0,0.5 L1,1 1,0" Fill="{TemplateBinding Foreground}" Stretch="Uniform" Width="10" Height="10"/>
<Path x:Name="PART_edgePath"
Stroke="{TemplateBinding Foreground}"
StrokeThickness="2" />
<controls:DefaultEdgePointer x:Name="PART_EdgePointerForTarget" NeedRotation="true">
<Path Width="10"
Height="10"
Data="M0,0.5 L1,1 1,0"
Fill="{TemplateBinding Foreground}"
Stretch="Uniform" />
</controls:DefaultEdgePointer>
</Grid>
</ControlTemplate>

Просмотреть файл

@ -1,46 +1,52 @@
using System;
using System.Diagnostics;
#if WPF
using System.Windows;
#elif METRO
using Windows.UI.Xaml;
using Windows.UI.Xaml.Input;
using Windows.Foundation;
using System.Linq;
#endif
using GraphX.PCL.Common.Exceptions;
using GraphX.PCL.Common.Interfaces;
using GraphX.PCL.Common.Models;
namespace GraphX.Controls
{
/// <summary>
/// Dragging behavior of objects in a GraphX graph area is influenced using the attached properties of this class.
///
///
/// To enable dragging of an individual object, set the IsDragEnabled attached property to true on that object. When IsDragEnabled is true, the
/// object can be used to initiate dragging.
///
///
/// To drag a group of vertices, set the IsTagged attached property to true for all of the vertices in the group. When dragging is started from
/// one of the tagged vertices, all of the tagged ones will be move.
///
///
/// "Primary drag object" defined: Whichever object gets the mouse/pointer events is treated as the primary drag object and its attached properties take
/// precedence for controlling drag behavior. When only one object is being dragged, it is the primary drag object. When a group of objects is tagged
/// and being dragged together, the one getting mouse events is the primary drag object.
///
///
/// There is limited support for dragging edges. It is achieved by setting IsDragEnabled to true for the edge AND tagging the edge and the vertices
/// it is attached to. When the user drags the edge, the drag is actually performed on the vertices.
///
///
/// For edges to be updated as a vertex is moved, set UpdateEdgesOnMove to true for the object being dragged.
///
///
/// Snapping is turned on or off by the GlobalIsSnappingPredicate or by the IsSnappingPredicate property on the primary drag object. The predicate is
/// called with each movement of the mouse/pointer and the primary drag object is passed in. If snapping should be performed, the predicate must return
/// true. To skip snapping logic, the predicate must return false. If no predicate is set using the IsSnappingPredicate, the GlobalIsSnappingPredicate
/// is used. The default behavior is to snap while a shift key alone is pressed.
///
///
/// When dragging a group of objects and using snapping, there is an additional refinement that can be used for the snapping behavior of the individual
/// objects in the group. The individual objects can move the exact same amount as the primary object when it snaps, or they can snap individually, with
/// the snap calculation being performed for each one. The behavior is controlled for the entire group by the GlobalIsSnappingIndividuallyPredicate or
/// the IsIndividualSnappingPredicate property setting ON THE PRIMARY DRAG OBJECT. The default behavior is to move all dragged objects by the same offset
/// as the primary drag object.
///
///
/// Snapping calculations are performed by the functions set on the primary drag object using the GlobalXSnapModifier or XSnapModifier property and the
/// GlobalYSnapModifier or YSnapModifier propery. These functions are called for each movement and provided the GraphAreaBase, object being moved, and
/// the pre-snapped x or y value. The passed in parameters are intended to provide an opportunity to find elements within the graph area and do things
@ -65,7 +71,7 @@ namespace GraphX.Controls
private static readonly SnapModifierFunc _builtinSnapModifier = (area, obj, val) => Math.Round(val * 0.1) * 10.0;
#endregion
#endregion Built-in snapping behavior
#region Global snapping behavior management
@ -94,7 +100,6 @@ namespace GraphX.Controls
}
}
private static Predicate<DependencyObject> _globalIsIndividualSnappingPredicate = _builtinIsIndividualSnappingPredicate;
/// <summary>
@ -171,19 +176,22 @@ namespace GraphX.Controls
}
}
#endregion
#endregion Global snapping behavior management
#region Attached DPs
public static readonly DependencyProperty IsDragEnabledProperty = DependencyProperty.RegisterAttached("IsDragEnabled", typeof(bool), typeof(DragBehaviour), new PropertyMetadata(false, OnIsDragEnabledPropertyChanged));
public static readonly DependencyProperty UpdateEdgesOnMoveProperty = DependencyProperty.RegisterAttached("UpdateEdgesOnMove", typeof(bool), typeof(DragBehaviour), new PropertyMetadata(false));
public static readonly DependencyProperty IsTaggedProperty = DependencyProperty.RegisterAttached("IsTagged", typeof(bool), typeof(DragBehaviour), new PropertyMetadata(false));
public static readonly DependencyProperty IsDraggingProperty = DependencyProperty.RegisterAttached("IsDragging", typeof(bool), typeof(DragBehaviour), new PropertyMetadata(false));
public static readonly DependencyProperty IsSnappingPredicateProperty = DependencyProperty.RegisterAttached("IsSnappingPredicate", typeof(Predicate<DependencyObject>), typeof(DragBehaviour), new PropertyMetadata(new Predicate<DependencyObject>(obj => { return _globalIsSnappingPredicate(obj); })));
public static readonly DependencyProperty IsIndividualSnappingPredicateProperty = DependencyProperty.RegisterAttached("IsIndividualSnappingPredicate", typeof(Predicate<DependencyObject>), typeof(DragBehaviour), new PropertyMetadata(new Predicate<DependencyObject>(obj => { return _globalIsIndividualSnappingPredicate(obj); })));
/// <summary>
/// Snap feature modifier delegate for X axis
/// </summary>
public static readonly DependencyProperty XSnapModifierProperty = DependencyProperty.RegisterAttached("XSnapModifier", typeof(SnapModifierFunc), typeof(DragBehaviour), new PropertyMetadata(new SnapModifierFunc((area, obj, val) => _globalXSnapModifier(area, obj, val))));
/// <summary>
/// Snap feature modifier delegate for Y axis
/// </summary>
@ -193,9 +201,11 @@ namespace GraphX.Controls
private static readonly DependencyProperty OriginalYProperty = DependencyProperty.RegisterAttached("OriginalY", typeof(double), typeof(DragBehaviour), new PropertyMetadata(0.0));
private static readonly DependencyProperty OriginalMouseXProperty = DependencyProperty.RegisterAttached("OriginalMouseX", typeof(double), typeof(DragBehaviour), new PropertyMetadata(0.0));
private static readonly DependencyProperty OriginalMouseYProperty = DependencyProperty.RegisterAttached("OriginalMouseY", typeof(double), typeof(DragBehaviour), new PropertyMetadata(0.0));
#endregion
#endregion Attached DPs
#region Get/Set method for Attached Properties
public static bool GetUpdateEdgesOnMove(DependencyObject obj)
{
return (bool)obj.GetValue(UpdateEdgesOnMoveProperty);
@ -276,7 +286,7 @@ namespace GraphX.Controls
obj.SetValue(YSnapModifierProperty, value);
}
#endregion
#endregion Get/Set method for Attached Properties
#region Get/Set methods for private Attached Properties
@ -319,9 +329,11 @@ namespace GraphX.Controls
{
obj.SetValue(OriginalMouseYProperty, value);
}
#endregion
#endregion Get/Set methods for private Attached Properties
#region PropertyChanged callbacks
private static void OnIsDragEnabledPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
#if WPF
@ -341,8 +353,16 @@ namespace GraphX.Controls
{
//register the event handlers
#if WPF
element.MouseLeftButtonDown += OnDragStarted;
element.PreviewMouseLeftButtonUp += OnDragFinished;
if (element.GetType() == typeof(VertexControl))
{
element.MouseLeftButtonDown += OnVertexDragStarted;
element.PreviewMouseLeftButtonUp += OnVertexDragFinished;
}
else if (element.GetType() == typeof(EdgeControl))
{
element.MouseLeftButtonDown += OnEdgeDrageStarted;
element.PreviewMouseLeftButtonUp += OnEdgeDragFinished;
}
#elif METRO
element.PointerPressed += OnDragStarted;
element.PointerReleased += OnDragFinished;
@ -352,18 +372,107 @@ namespace GraphX.Controls
{
//unregister the event handlers
#if WPF
element.MouseLeftButtonDown -= OnDragStarted;
element.PreviewMouseLeftButtonUp -= OnDragFinished;
if (element.GetType() == typeof(VertexControl))
{
element.MouseLeftButtonDown -= OnVertexDragStarted;
element.PreviewMouseLeftButtonUp -= OnVertexDragFinished;
}
else if (element.GetType() == typeof(EdgeControl))
{
element.MouseLeftButtonDown -= OnEdgeDrageStarted;
element.PreviewMouseLeftButtonUp -= OnEdgeDragFinished;
}
#elif METRO
element.PointerPressed -= OnDragStarted;
element.PointerReleased -= OnDragFinished;
#endif
}
}
#endregion
#endregion PropertyChanged callbacks
private static void OnEdgeDrageStarted(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
DependencyObject obj = sender as DependencyObject;
SetIsDragging(obj, true);
var element = obj as IInputElement;
if (element != null)
{
element.CaptureMouse();
element.MouseMove -= OnEdgeDragging;
element.MouseMove += OnEdgeDragging;
}
else throw new GX_InvalidDataException("The control must be a descendent of the FrameworkElement or FrameworkContentElement!");
e.Handled = false;
}
private static void OnEdgeDragging(object sender, System.Windows.Input.MouseEventArgs e)
{
var obj = sender as DependencyObject;
if (!GetIsDragging(obj))
return;
EdgeControl edgeControl = sender as EdgeControl;
edgeControl.PrepareEdgePathFromMousePointer();
e.Handled = true;
}
private static void OnEdgeDragFinished(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
EdgeControl edgeControl = sender as EdgeControl;
if (edgeControl == null) return;
GraphAreaBase graphAreaBase = edgeControl.RootArea;
VertexControl vertexControl = graphAreaBase.GetVertexControlAt(e.GetPosition(graphAreaBase));
if (vertexControl != null)
{
edgeControl.Target = vertexControl;
if (vertexControl.VertexConnectionPointsList.Count > 0)
{
IVertexConnectionPoint vertexConnectionPoint = vertexControl.GetConnectionPointAt(e.GetPosition(graphAreaBase));
var edge = edgeControl.Edge as IGraphXCommonEdge;
if (vertexConnectionPoint != null)
{
edge.TargetConnectionPointId = vertexConnectionPoint.Id;
}
else
{
edge.TargetConnectionPointId = null;
}
}
edgeControl.UpdateEdge();
var obj = (DependencyObject)sender;
SetIsDragging(obj, false);
//obj.ClearValue(OriginalMouseXProperty);
//obj.ClearValue(OriginalMouseYProperty);
//obj.ClearValue(OriginalXProperty);
//obj.ClearValue(OriginalYProperty);
var element = sender as IInputElement;
if (element != null)
{
element.MouseMove -= OnVertexDragging;
element.ReleaseMouseCapture();
}
}
}
#if WPF
private static void OnDragStarted(object sender, System.Windows.Input.MouseButtonEventArgs e)
private static void OnVertexDragStarted(object sender, System.Windows.Input.MouseButtonEventArgs e)
#elif METRO
private static void OnDragStarted( object sender, PointerRoutedEventArgs e )
#endif
@ -399,8 +508,8 @@ namespace GraphX.Controls
if (element != null)
{
element.CaptureMouse();
element.MouseMove -= OnDragging;
element.MouseMove += OnDragging;
element.MouseMove -= OnVertexDragging;
element.MouseMove += OnVertexDragging;
}
//else throw new GX_InvalidDataException("The control must be a descendent of the FrameworkElement or FrameworkContentElement!");
e.Handled = false;
@ -416,7 +525,8 @@ namespace GraphX.Controls
}
#if WPF
private static void OnDragFinished(object sender, System.Windows.Input.MouseButtonEventArgs e)
private static void OnVertexDragFinished(object sender, System.Windows.Input.MouseButtonEventArgs e)
#elif METRO
private static void OnDragFinished( object sender, PointerRoutedEventArgs e )
#endif
@ -445,7 +555,7 @@ namespace GraphX.Controls
var element = sender as IInputElement;
if (element != null)
{
element.MouseMove -= OnDragging;
element.MouseMove -= OnVertexDragging;
element.ReleaseMouseCapture();
}
#elif METRO
@ -459,7 +569,8 @@ namespace GraphX.Controls
}
#if WPF
private static void OnDragging(object sender, System.Windows.Input.MouseEventArgs e)
private static void OnVertexDragging(object sender, System.Windows.Input.MouseEventArgs e)
#elif METRO
private static void OnDragging( object sender, PointerRoutedEventArgs e )
#endif
@ -578,6 +689,7 @@ namespace GraphX.Controls
}
#if WPF
private static Point GetPositionInArea(GraphAreaBase area, System.Windows.Input.MouseEventArgs e)
#elif METRO
private static Windows.Foundation.Point GetPositionInArea(GraphAreaBase area, PointerRoutedEventArgs e)
@ -609,4 +721,4 @@ namespace GraphX.Controls
return area;
}
}
}
}

Просмотреть файл

@ -4,12 +4,16 @@ using GraphX.Controls.Models;
using GraphX.PCL.Common.Enums;
using GraphX.PCL.Common.Exceptions;
using GraphX.PCL.Common.Interfaces;
#if WPF
using System.Windows.Input;
#if WPF
using System.Windows.Controls;
using System.Windows;
using System.Windows.Media;
using System.Windows.Shapes;
using SysRect = System.Windows.Rect;
#elif METRO
using GraphX.PCL.Common;
using GraphX.Measure;
@ -33,10 +37,10 @@ namespace GraphX.Controls
public abstract class EdgeControlBase : Control, IGraphControl, IDisposable
{
#if METRO
void IPositionChangeNotify.OnPositionChanged()
{
//skip any actions on own position change
}
void IPositionChangeNotify.OnPositionChanged()
{
//skip any actions on own position change
}
#endif
#region Properties & Fields
@ -45,14 +49,18 @@ namespace GraphX.Controls
/// Gets or sets if edge pointer should be hidden when source and target vertices are overlapped making the 0 length edge. Default value is True.
/// </summary>
public bool HideEdgePointerOnVertexOverlap { get; set; } = true;
/// <summary>
/// Gets or sets the length of the edge to hide the edge pointers if less than or equal to. Default value is 0 (do not hide).
/// </summary>
public double HideEdgePointerByEdgeLength { get; set; } = 0.0d;
public abstract bool IsSelfLooped { get; protected set; }
public abstract void Dispose();
public abstract void Clean();
protected DoubleCollection StrokeDashArray { get; set; }
/// <summary>
@ -70,8 +78,13 @@ namespace GraphX.Controls
/// </summary>
private SysRect _selfLoopedEdgeLastKnownRect;
protected virtual void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { }
protected virtual void OnTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { }
protected virtual void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
protected virtual void OnTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
/// <summary>
/// Gets or sets parent GraphArea visual
@ -85,15 +98,16 @@ namespace GraphX.Controls
public static readonly DependencyProperty RootCanvasProperty =
DependencyProperty.Register(nameof(RootArea), typeof(GraphAreaBase), typeof(EdgeControlBase), new PropertyMetadata(null));
public static readonly DependencyProperty SelfLoopIndicatorRadiusProperty = DependencyProperty.Register(nameof(SelfLoopIndicatorRadius),
typeof(double),
typeof(EdgeControlBase),
new PropertyMetadata(5d));
/// <summary>
/// Radius of the default self-loop indicator, which is drawn as a circle (when custom template isn't provided). Default is 20.
/// </summary>
public double SelfLoopIndicatorRadius {
public double SelfLoopIndicatorRadius
{
get { return (double)GetValue(SelfLoopIndicatorRadiusProperty); }
set { SetValue(SelfLoopIndicatorRadiusProperty, value); }
}
@ -102,6 +116,7 @@ namespace GraphX.Controls
typeof(Point),
typeof(EdgeControlBase),
new PropertyMetadata(new Point()));
/// <summary>
/// Offset from the left-top corner of the vertex. Useful for custom vertex shapes. Default is 10,10.
/// </summary>
@ -115,6 +130,7 @@ namespace GraphX.Controls
typeof(bool),
typeof(EdgeControlBase),
new PropertyMetadata(true));
/// <summary>
/// Show self looped edge indicator on the vertex top-left corner. Default value is true.
/// </summary>
@ -124,7 +140,6 @@ namespace GraphX.Controls
set { SetValue(ShowSelfLoopIndicatorProperty, value); }
}
public static readonly DependencyProperty SourceProperty = DependencyProperty.Register(nameof(Source),
typeof(VertexControl),
typeof(EdgeControlBase),
@ -145,14 +160,12 @@ namespace GraphX.Controls
(d as EdgeControlBase)?.OnTargetChanged(d, e);
}
public static readonly DependencyProperty EdgeProperty = DependencyProperty.Register(nameof(Edge), typeof(object),
typeof(EdgeControlBase),
new PropertyMetadata(null));
private double _labelAngle;
/// <summary>
/// Gets or sets vertex label angle
/// </summary>
@ -189,6 +202,7 @@ namespace GraphX.Controls
case EdgeDashStyle.Dash:
ec.StrokeDashArray = new DoubleCollection { 4.0, 2.0 };
break;
case EdgeDashStyle.Dot:
ec.StrokeDashArray = new DoubleCollection { 1.0, 2.0 };
break;
@ -208,7 +222,6 @@ namespace GraphX.Controls
ec.UpdateEdge(false);
}
/// <summary>
/// Gets or sets edge dash style
/// </summary>
@ -217,7 +230,8 @@ namespace GraphX.Controls
get { return (EdgeDashStyle)GetValue(DashStyleProperty); }
set { SetValue(DashStyleProperty, value); }
}
#endregion
#endregion DashStyle
/// <summary>
/// Gets or sets if this edge can be paralellized if GraphArea.EnableParallelEdges is true.
@ -231,6 +245,7 @@ namespace GraphX.Controls
}
private bool _updateLabelPosition;
/// <summary>
/// Gets or sets if label position should be updated on edge update
/// </summary>
@ -240,6 +255,7 @@ namespace GraphX.Controls
protected PropertyChangeNotifier _sourceWatcher;
protected PropertyChangeNotifier _targetWatcher;
#endif
/// <summary>
/// Gets or set if hidden edges should be updated when connected vertices positions are changed. Default value is True.
/// </summary>
@ -266,14 +282,15 @@ namespace GraphX.Controls
public bool ShowArrows { get { return (bool)GetValue(ShowArrowsProperty); } set { SetValue(ShowArrowsProperty, value); } }
public static readonly DependencyProperty ShowLabelProperty = DependencyProperty.Register("ShowLabel",
typeof (bool),
typeof (EdgeControlBase),
typeof(bool),
typeof(EdgeControlBase),
new PropertyMetadata(false, showlabel_changed));
private static void showlabel_changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
(d as EdgeControlBase)?.UpdateEdge();
}
/// <summary>
/// Show edge label.Default value is False.
/// </summary>
@ -295,6 +312,7 @@ namespace GraphX.Controls
}
}
}
protected bool _alignLabelsToEdges;
public static readonly DependencyProperty LabelVerticalOffsetProperty = DependencyProperty.Register("LabelVerticalOffset",
@ -323,6 +341,7 @@ namespace GraphX.Controls
protected Path LinePathObject;
private IEdgeLabelControl _edgeLabelControl;
/// <summary>
/// Templated label control to display labels
/// </summary>
@ -335,6 +354,7 @@ namespace GraphX.Controls
/// Returns edge pointer for source if any
/// </summary>
public IEdgePointer GetEdgePointerForSource() { return EdgePointerForSource; }
/// <summary>
/// Returns edge pointer for target if any
/// </summary>
@ -350,6 +370,7 @@ namespace GraphX.Controls
get { return (VertexControl)GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
/// <summary>
/// Target visual vertex object
/// </summary>
@ -359,7 +380,6 @@ namespace GraphX.Controls
set { SetValue(TargetProperty, value); }
}
/// <summary>
/// Data edge object
/// </summary>
@ -393,11 +413,11 @@ namespace GraphX.Controls
/// </summary>
public void UpdateLabel()
{
if(_edgeLabelControl != null && ShowLabel)
if (_edgeLabelControl != null && ShowLabel)
UpdateLabelLayout(true);
}
#endregion
#endregion Properties & Fields
#region Position methods
@ -437,9 +457,11 @@ namespace GraphX.Controls
{
return new Measure.Point(final ? GraphAreaBase.GetFinalX(this) : GraphAreaBase.GetX(this), final ? GraphAreaBase.GetFinalY(this) : GraphAreaBase.GetY(this));
}
#endregion
#endregion Position methods
#region Manual path controls
/// <summary>
/// Gets current edge path geometry object
/// </summary>
@ -458,7 +480,8 @@ namespace GraphX.Controls
Linegeometry = geo;
UpdateEdge();
}
#endregion
#endregion Manual path controls
internal void SetVisibility(Visibility value)
{
@ -470,8 +493,8 @@ namespace GraphX.Controls
EdgeLabelControl?.UpdateLayout();
if (LinePathObject != null)
{
var pos = Source.GetPosition();
Source.SetPosition(pos.X, pos.Y);
var pos = this.Source.GetPosition();
this.Source.SetPosition(pos.X, pos.Y);
}
}
@ -480,12 +503,15 @@ namespace GraphX.Controls
/// </summary>
public bool IsTemplateLoaded => LinePathObject != null;
protected virtual void OnEdgeLabelUpdated() { }
protected virtual void OnEdgeLabelUpdated()
{
}
#if WPF
public override void OnApplyTemplate()
#elif METRO
protected override void OnApplyTemplate()
protected override void OnApplyTemplate()
#endif
{
base.OnApplyTemplate();
@ -499,18 +525,17 @@ namespace GraphX.Controls
throw new GX_ObsoleteException("PART_edgeArrowPath is obsolete! Please use new DefaultEdgePointer object in your EdgeControlBase template!");
EdgeLabelControl = EdgeLabelControl ?? GetTemplatePart("PART_edgeLabel") as IEdgeLabelControl;
EdgePointerForSource = GetTemplatePart("PART_EdgePointerForSource") as IEdgePointer;
EdgePointerForTarget = GetTemplatePart("PART_EdgePointerForTarget") as IEdgePointer;
SelfLoopIndicator = GetTemplatePart("PART_SelfLoopedEdge") as FrameworkElement;
if(SelfLoopIndicator != null)
if (SelfLoopIndicator != null)
SelfLoopIndicator.LayoutUpdated += (sender, args) =>
{
SelfLoopIndicator?.Arrange(_selfLoopedEdgeLastKnownRect);
};
// var x = ShowLabel;
// var x = ShowLabel;
MeasureChild(EdgePointerForSource as UIElement);
MeasureChild(EdgePointerForTarget as UIElement);
MeasureChild(SelfLoopIndicator);
@ -561,7 +586,6 @@ namespace GraphX.Controls
LinePathObject.StrokeDashArray = StrokeDashArray;
}
internal int ParallelEdgeOffset;
//internal int TargetOffset;
@ -583,10 +607,12 @@ namespace GraphX.Controls
sourcepos.Y + source.DesiredSize.Height * .5 - sideDistance * (mainVector.X / mainVector.Length));
return joint;
}
/// <summary>
/// Internal value to store last calculated Source vertex connection point
/// </summary>
protected internal Point? SourceConnectionPoint;
/// <summary>
/// Internal value to store last calculated Target vertex connection point
/// </summary>
@ -654,6 +680,206 @@ namespace GraphX.Controls
else _selfLoopedEdgeLastKnownRect = new SysRect(pt, SelfLoopIndicator.DesiredSize);
}
public virtual void PrepareEdgePathFromMousePointer(bool useCurrentCoords = false)
{
//do not calculate invisible edges
if ((Visibility != Visibility.Visible && !IsHiddenEdgesUpdated) && ManualDrawing || !IsTemplateLoaded) return;
//get the size of the source
var sourceSize = new Size
{
Width = this.Source.ActualWidth,
Height = this.Source.ActualHeight
};
//get the position center of the source
var sourcePos = new Point
{
X = (useCurrentCoords ? GraphAreaBase.GetX(this.Source) : GraphAreaBase.GetFinalX(this.Source)) + sourceSize.Width * 0.5,
Y = (useCurrentCoords ? GraphAreaBase.GetY(this.Source) : GraphAreaBase.GetFinalY(this.Source)) + sourceSize.Height * 0.5
};
//get the size of the target
var targetSize = new Size
{
Width = SystemParameters.CursorWidth,
Height = SystemParameters.CursorHeight
};
//get the position center of the target
var targetPos = new Point
{
X = Mouse.GetPosition(this.RootArea).X,/* + targetSize.Width * 0.5,*/
Y = Mouse.GetPosition(this.RootArea).Y/* + targetSize.Height * 0.5*/
};
var routedEdge = this.Edge as IRoutingInfo;
if (routedEdge == null)
throw new GX_InvalidDataException("Edge must implement IRoutingInfo interface");
//get the route informations
var routeInformation = routedEdge.RoutingPoints;
// Get the TopLeft position of the Source Vertex.
var sourcePos1 = new Point
{
X = (useCurrentCoords ? GraphAreaBase.GetX(this.Source) : GraphAreaBase.GetFinalX(this.Source)),
Y = (useCurrentCoords ? GraphAreaBase.GetY(this.Source) : GraphAreaBase.GetFinalY(this.Source))
};
// Get the TopLeft position of the Target Vertex.
var targetPos1 = new Point
{
X = Mouse.GetPosition(this.RootArea).X,
Y = Mouse.GetPosition(this.RootArea).Y
};
var hasEpSource = EdgePointerForSource != null;
var hasEpTarget = EdgePointerForTarget != null;
//if self looped edge
if (IsSelfLooped)
{
PrepareSelfLoopedEdge(sourcePos1);
return;
}
//check if we have some edge route data
var hasRouteInfo = routeInformation != null && routeInformation.Length > 1;
var gEdge = Edge as IGraphXCommonEdge;
Point p1;
Point p2;
//calculate edge source (p1) and target (p2) endpoints based on different settings
if (gEdge?.SourceConnectionPointId != null)
{
var sourceCp = this.Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true);
if (sourceCp == null)
{
throw new GX_ObjectNotFoundException(string.Format("Can't find source vertex VCP by edge source connection point Id({1}) : {0}", this.Source, gEdge.SourceConnectionPointId));
}
if (sourceCp.Shape == VertexShape.None) p1 = sourceCp.RectangularSize.Center();
else
{
var targetCpPos = hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos);
p1 = GeometryHelper.GetEdgeEndpoint(sourceCp.RectangularSize.Center(), sourceCp.RectangularSize, targetCpPos, sourceCp.Shape);
}
}
else
p1 = GeometryHelper.GetEdgeEndpoint(sourcePos, new SysRect(sourcePos1, sourceSize), (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos)), this.Source.VertexShape);
//if (gEdge?.TargetConnectionPointId != null)
//{
// //var targetCp = this.Target.GetConnectionPointById(gEdge.TargetConnectionPointId.Value, true);
// //if (targetCp == null)
// // throw new GX_ObjectNotFoundException(string.Format("Can't find target vertex VCP by edge target connection point Id({1}) : {0}", this.Target, gEdge.TargetConnectionPointId));
// //if (targetCp.Shape == VertexShape.None) p2 = targetCp.RectangularSize.Center();
// //else
// //{
// // var sourceCpPos = gEdge.SourceConnectionPointId.HasValue ? this.Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true).RectangularSize.Center() : hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos);
// // p2 = GeometryHelper.GetEdgeEndpoint(targetCp.RectangularSize.Center(), targetCp.RectangularSize, sourceCpPos, targetCp.Shape);
// //}
//}
//else
p2 = GeometryHelper.GetEdgeEndpoint(
targetPos, new SysRect(targetPos, targetSize), hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos), VertexShape.None);
SourceConnectionPoint = p1;
TargetConnectionPoint = p2;
Linegeometry = new PathGeometry();
PathFigure lineFigure = new PathFigure();
//if we have route and route consist of 2 or more points
if (RootArea != null && hasRouteInfo)
{
//replace start and end points with accurate ones
var routePoints = routeInformation.ToWindows().ToList();
routePoints.Clear();
routePoints.Add(p1);
routePoints.Add(p2);
if (routedEdge.RoutingPoints != null)
routedEdge.RoutingPoints = routePoints.ToArray().ToGraphX();
if (RootArea.EdgeCurvingEnabled)
{
var oPolyLineSegment = GeometryHelper.GetCurveThroughPoints(routePoints.ToArray(), 0.5, RootArea.EdgeCurvingTolerance);
if (hasEpTarget)
{
UpdateTargetEpData(oPolyLineSegment.Points[oPolyLineSegment.Points.Count - 1], oPolyLineSegment.Points[oPolyLineSegment.Points.Count - 2]);
oPolyLineSegment.Points.RemoveAt(oPolyLineSegment.Points.Count - 1);
}
if (hasEpSource)
{
UpdateSourceEpData(oPolyLineSegment.Points.First(), oPolyLineSegment.Points[1]);
oPolyLineSegment.Points.RemoveAt(0);
}
lineFigure = GeometryHelper.GetPathFigureFromPathSegments(routePoints[0], true, true, oPolyLineSegment);
#if WPF
//freeze and create resulting geometry
GeometryHelper.TryFreeze(oPolyLineSegment);
#endif
}
else
{
if (hasEpSource)
routePoints[0] = routePoints[0].Subtract(UpdateSourceEpData(routePoints.First(), routePoints[1]));
if (hasEpTarget)
routePoints[routePoints.Count - 1] = routePoints[routePoints.Count - 1].Subtract(UpdateTargetEpData(p2, routePoints[routePoints.Count - 2]));
// Reverse the path if specified.
if (gEdge.ReversePath)
routePoints.Reverse();
var pcol = new PointCollection();
routePoints.ForEach(a => pcol.Add(a));
lineFigure = new PathFigure { StartPoint = p1, Segments = new PathSegmentCollection { new PolyLineSegment { Points = pcol } }, IsClosed = false };
}
}
else // no route defined
{
bool remainHidden = false;
//check for hide only if prop is not 0
if (HideEdgePointerByEdgeLength != 0d)
{
if (MathHelper.GetDistanceBetweenPoints(p1, p2) <= HideEdgePointerByEdgeLength)
{
EdgePointerForSource?.Hide();
EdgePointerForTarget?.Hide();
remainHidden = true;
}
else
{
EdgePointerForSource?.Show();
EdgePointerForTarget?.Show();
}
}
if (hasEpSource)
p1 = p1.Subtract(UpdateSourceEpData(p1, p2, remainHidden));
if (hasEpTarget)
p2 = p2.Subtract(UpdateTargetEpData(p2, p1, remainHidden));
lineFigure = new PathFigure { StartPoint = gEdge.ReversePath ? p2 : p1, Segments = new PathSegmentCollection { new LineSegment() { Point = gEdge.ReversePath ? p1 : p2 } }, IsClosed = false };
}
((PathGeometry)Linegeometry).Figures.Add(lineFigure);
#if WPF
GeometryHelper.TryFreeze(lineFigure);
GeometryHelper.TryFreeze(Linegeometry);
#endif
if (ShowLabel && EdgeLabelControl != null && _updateLabelPosition)
EdgeLabelControl.UpdatePosition();
if (LinePathObject == null) return;
LinePathObject.Data = Linegeometry;
LinePathObject.StrokeDashArray = StrokeDashArray;
}
/// <summary>
/// Create and apply edge path using calculated ER parameters stored in edge
/// </summary>
@ -663,29 +889,30 @@ namespace GraphX.Controls
public virtual void PrepareEdgePath(bool useCurrentCoords = false, Measure.Point[] externalRoutingPoints = null, bool updateLabel = true)
{
//do not calculate invisible edges
if ((Visibility != Visibility.Visible && !IsHiddenEdgesUpdated) && Source == null || Target == null || ManualDrawing || !IsTemplateLoaded) return;
if ((Visibility != Visibility.Visible && !IsHiddenEdgesUpdated) && this.Source == null || this.Target == null || ManualDrawing || !IsTemplateLoaded) return;
#region Get the inputs
//get the size of the source
var sourceSize = new Size
{
Width = Source.ActualWidth,
Height = Source.ActualHeight
Width = this.Source.ActualWidth,
Height = this.Source.ActualHeight
};
if (CustomHelper.IsInDesignMode(this)) sourceSize = new Size(80, 20);
//get the position center of the source
var sourcePos = new Point
{
X = (useCurrentCoords ? GraphAreaBase.GetX(Source) : GraphAreaBase.GetFinalX(Source)) + sourceSize.Width * .5,
Y = (useCurrentCoords ? GraphAreaBase.GetY(Source) : GraphAreaBase.GetFinalY(Source)) + sourceSize.Height * .5
X = (useCurrentCoords ? GraphAreaBase.GetX(this.Source) : GraphAreaBase.GetFinalX(this.Source)) + sourceSize.Width * .5,
Y = (useCurrentCoords ? GraphAreaBase.GetY(this.Source) : GraphAreaBase.GetFinalY(this.Source)) + sourceSize.Height * .5
};
//get the size of the target
var targetSize = new Size
{
Width = Target.ActualWidth,
Height = Target.ActualHeight
Width = this.Target.ActualWidth,
Height = this.Target.ActualHeight
};
if (CustomHelper.IsInDesignMode(this))
targetSize = new Size(80, 20);
@ -693,8 +920,8 @@ namespace GraphX.Controls
//get the position center of the target
var targetPos = new Point
{
X = (useCurrentCoords ? GraphAreaBase.GetX(Target) : GraphAreaBase.GetFinalX(Target)) + targetSize.Width * .5,
Y = (useCurrentCoords ? GraphAreaBase.GetY(Target) : GraphAreaBase.GetFinalY(Target)) + targetSize.Height * .5
X = (useCurrentCoords ? GraphAreaBase.GetX(this.Target) : GraphAreaBase.GetFinalX(this.Target)) + targetSize.Width * .5,
Y = (useCurrentCoords ? GraphAreaBase.GetY(this.Target) : GraphAreaBase.GetFinalY(this.Target)) + targetSize.Height * .5
};
var routedEdge = Edge as IRoutingInfo;
@ -707,19 +934,20 @@ namespace GraphX.Controls
// Get the TopLeft position of the Source Vertex.
var sourcePos1 = new Point
{
X = (useCurrentCoords ? GraphAreaBase.GetX(Source) : GraphAreaBase.GetFinalX(Source)),
Y = (useCurrentCoords ? GraphAreaBase.GetY(Source) : GraphAreaBase.GetFinalY(Source))
X = (useCurrentCoords ? GraphAreaBase.GetX(this.Source) : GraphAreaBase.GetFinalX(this.Source)),
Y = (useCurrentCoords ? GraphAreaBase.GetY(this.Source) : GraphAreaBase.GetFinalY(this.Source))
};
// Get the TopLeft position of the Target Vertex.
var targetPos1 = new Point
{
X = (useCurrentCoords ? GraphAreaBase.GetX(Target) : GraphAreaBase.GetFinalX(Target)),
Y = (useCurrentCoords ? GraphAreaBase.GetY(Target) : GraphAreaBase.GetFinalY(Target))
X = (useCurrentCoords ? GraphAreaBase.GetX(this.Target) : GraphAreaBase.GetFinalX(this.Target)),
Y = (useCurrentCoords ? GraphAreaBase.GetY(this.Target) : GraphAreaBase.GetFinalY(this.Target))
};
var hasEpSource = EdgePointerForSource != null;
var hasEpTarget = EdgePointerForTarget != null;
#endregion
#endregion Get the inputs
//if self looped edge
if (IsSelfLooped)
@ -734,8 +962,8 @@ namespace GraphX.Controls
//calculate source and target edge attach points
if (RootArea != null && !hasRouteInfo && RootArea.EnableParallelEdges && ParallelEdgeOffset != 0)
{
sourcePos = GetParallelOffset(Source, Target, ParallelEdgeOffset);
targetPos = GetParallelOffset(Target, Source, -ParallelEdgeOffset);
sourcePos = GetParallelOffset(this.Source, this.Target, ParallelEdgeOffset);
targetPos = GetParallelOffset(this.Target, this.Source, -ParallelEdgeOffset);
}
/* Rectangular shapes implementation by bleibold */
@ -747,38 +975,38 @@ namespace GraphX.Controls
//calculate edge source (p1) and target (p2) endpoints based on different settings
if (gEdge?.SourceConnectionPointId != null)
{
var sourceCp = Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true);
var sourceCp = this.Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true);
if (sourceCp == null)
throw new GX_ObjectNotFoundException(string.Format("Can't find source vertex VCP by edge source connection point Id({1}) : {0}", Source, gEdge.SourceConnectionPointId));
throw new GX_ObjectNotFoundException(string.Format("Can't find source vertex VCP by edge source connection point Id({1}) : {0}", this.Source, gEdge.SourceConnectionPointId));
if (sourceCp.Shape == VertexShape.None) p1 = sourceCp.RectangularSize.Center();
else
{
var targetCpPos = gEdge.TargetConnectionPointId.HasValue ? Target.GetConnectionPointById(gEdge.TargetConnectionPointId.Value, true).RectangularSize.Center() : (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos));
var targetCpPos = gEdge.TargetConnectionPointId.HasValue ? this.Target.GetConnectionPointById(gEdge.TargetConnectionPointId.Value, true).RectangularSize.Center() : (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos));
p1 = GeometryHelper.GetEdgeEndpoint(sourceCp.RectangularSize.Center(), sourceCp.RectangularSize, targetCpPos, sourceCp.Shape);
}
}
else
p1 = GeometryHelper.GetEdgeEndpoint(sourcePos, new SysRect(sourcePos1, sourceSize), (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos)), Source.VertexShape);
p1 = GeometryHelper.GetEdgeEndpoint(sourcePos, new SysRect(sourcePos1, sourceSize), (hasRouteInfo ? routeInformation[1].ToWindows() : (targetPos)), this.Source.VertexShape);
if (gEdge?.TargetConnectionPointId != null)
{
var targetCp = Target.GetConnectionPointById(gEdge.TargetConnectionPointId.Value, true);
var targetCp = this.Target.GetConnectionPointById(gEdge.TargetConnectionPointId.Value, true);
if (targetCp == null)
throw new GX_ObjectNotFoundException(string.Format("Can't find target vertex VCP by edge target connection point Id({1}) : {0}", Target, gEdge.TargetConnectionPointId));
throw new GX_ObjectNotFoundException(string.Format("Can't find target vertex VCP by edge target connection point Id({1}) : {0}", this.Target, gEdge.TargetConnectionPointId));
if (targetCp.Shape == VertexShape.None) p2 = targetCp.RectangularSize.Center();
else
{
var sourceCpPos = gEdge.SourceConnectionPointId.HasValue ? Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true).RectangularSize.Center() : hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos);
var sourceCpPos = gEdge.SourceConnectionPointId.HasValue ? this.Source.GetConnectionPointById(gEdge.SourceConnectionPointId.Value, true).RectangularSize.Center() : hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos);
p2 = GeometryHelper.GetEdgeEndpoint(targetCp.RectangularSize.Center(), targetCp.RectangularSize, sourceCpPos, targetCp.Shape);
}
}
else
p2 = GeometryHelper.GetEdgeEndpoint(targetPos, new SysRect(targetPos1, targetSize), hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos), Target.VertexShape);
p2 = GeometryHelper.GetEdgeEndpoint(targetPos, new SysRect(targetPos1, targetSize), hasRouteInfo ? routeInformation[routeInformation.Length - 2].ToWindows() : (sourcePos), this.Target.VertexShape);
SourceConnectionPoint = p1;
TargetConnectionPoint = p2;
Linegeometry = new PathGeometry();
Linegeometry = new PathGeometry();
PathFigure lineFigure;
//if we have route and route consist of 2 or more points
@ -817,21 +1045,20 @@ namespace GraphX.Controls
}
else
{
if (hasEpSource)
if (hasEpSource)
routePoints[0] = routePoints[0].Subtract(UpdateSourceEpData(routePoints.First(), routePoints[1]));
if (hasEpTarget)
routePoints[routePoints.Count - 1] = routePoints[routePoints.Count - 1].Subtract(UpdateTargetEpData(p2, routePoints[routePoints.Count - 2]));
// Reverse the path if specified.
if (gEdge.ReversePath)
routePoints.Reverse();
if (gEdge.ReversePath)
routePoints.Reverse();
var pcol = new PointCollection();
routePoints.ForEach(a=> pcol.Add(a));
routePoints.ForEach(a => pcol.Add(a));
lineFigure = new PathFigure { StartPoint = p1, Segments = new PathSegmentCollection { new PolyLineSegment { Points = pcol } }, IsClosed = false };
}
}
else // no route defined
{
@ -852,12 +1079,12 @@ namespace GraphX.Controls
}
}
if (hasEpSource)
if (hasEpSource)
p1 = p1.Subtract(UpdateSourceEpData(p1, p2, allowUpdateEpDataToUnsuppress));
if (hasEpTarget)
p2 = p2.Subtract(UpdateTargetEpData(p2, p1, allowUpdateEpDataToUnsuppress));
lineFigure = new PathFigure { StartPoint = gEdge.ReversePath ? p2 : p1, Segments = new PathSegmentCollection { new LineSegment() { Point = gEdge.ReversePath ? p1 : p2 } }, IsClosed = false };
lineFigure = new PathFigure { StartPoint = gEdge.ReversePath ? p2 : p1, Segments = new PathSegmentCollection { new LineSegment() { Point = gEdge.ReversePath ? p1 : p2 } }, IsClosed = false };
}
((PathGeometry)Linegeometry).Figures.Add(lineFigure);
#if WPF
@ -876,7 +1103,7 @@ namespace GraphX.Controls
if (HideEdgePointerOnVertexOverlap) EdgePointerForSource.Suppress();
else dir = new Vector(0, 0);
}
else if(allowUnsuppress) EdgePointerForSource.UnSuppress();
else if (allowUnsuppress) EdgePointerForSource.UnSuppress();
var result = EdgePointerForSource.Update(from, dir, EdgePointerForSource.NeedRotation ? -MathHelper.GetAngleBetweenPoints(from, to).ToDegrees() : 0);
return EdgePointerForSource.Visibility == Visibility.Visible ? result : new Point();
}
@ -890,11 +1117,11 @@ namespace GraphX.Controls
else dir = new Vector(0, 0);
}
else if (allowUnsuppress) EdgePointerForTarget.UnSuppress();
var result = EdgePointerForTarget.Update(from, dir, EdgePointerForTarget.NeedRotation ? (-MathHelper.GetAngleBetweenPoints(from, to).ToDegrees()) : 0);
var result = EdgePointerForTarget.Update(from, dir, EdgePointerForTarget.NeedRotation ? (-MathHelper.GetAngleBetweenPoints(from, to).ToDegrees()) : 0);
return EdgePointerForTarget.Visibility == Visibility.Visible ? result : new Point();
}
#endregion
#endregion public PrepareEdgePath()
/// <summary>
/// Searches and returns template part by name if found
@ -906,7 +1133,7 @@ namespace GraphX.Controls
#if WPF
return Template.FindName(name, this);
#elif METRO
return this.FindDescendantByName(name);
return this.FindDescendantByName(name);
#endif
}
@ -931,4 +1158,4 @@ namespace GraphX.Controls
}
}
}
}
}

Просмотреть файл

@ -66,8 +66,8 @@ namespace GraphX.Controls
DependencyProperty.Register(nameof(LogicCore), typeof(IGXLogicCore<TVertex, TEdge, TGraph>), typeof(GraphArea<TVertex, TEdge, TGraph>), new PropertyMetadata(null, logic_core_changed));
private static
#if METRO
async
#if METRO
async
#endif
void logic_core_changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
@ -164,7 +164,7 @@ namespace GraphX.Controls
internal override double EdgeCurvingTolerance => LogicCore?.EdgeCurvingTolerance ?? 0;
/// <summary>
/// Add custom control for
/// Add custom control for
/// </summary>
/// <param name="control"></param>
public virtual void AddCustomChildControl(UIElement control)
@ -302,9 +302,10 @@ namespace GraphX.Controls
/// Returns first vertex that is found under specified coordinates
/// </summary>
/// <param name="position">GraphArea coordinate space position</param>
public virtual VertexControl GetVertexControlAt(Point position)
public override VertexControl GetVertexControlAt(Point position)
{
Measure(new USize(double.PositiveInfinity, double.PositiveInfinity));
return VertexList.Values.FirstOrDefault(a =>
{
var pos = a.GetPosition();
@ -698,7 +699,7 @@ namespace GraphX.Controls
/// </summary>
public Dictionary<TVertex, Size> GetVertexSizes()
{
//measure if needed and get all vertex sizes
//measure if needed and get all vertex sizes
Measure(new USize(double.PositiveInfinity, double.PositiveInfinity));
var vertexSizes = new Dictionary<TVertex, Size>(_vertexlist.Count(a => ((IGraphXVertex)a.Value.Vertex).SkipProcessing != ProcessingOptionEnum.Exclude));
//go through the vertex presenters and get the actual layoutpositions
@ -814,7 +815,7 @@ namespace GraphX.Controls
private CancellationTokenSource _layoutCancellationSource;
/// <summary>
/// Gets or sets if visual graph should be updated if graph is filtered.
/// Gets or sets if visual graph should be updated if graph is filtered.
/// Remove all visuals with no keys in data graph and add all visuals that has keys in data graph.
/// Default value is True.
/// </summary>
@ -896,7 +897,7 @@ namespace GraphX.Controls
if (!localLogicCore.GenerateAlgorithmStorage(vertexSizes, vertexPositions))
return;
//clear routing info
//clear routing info
localLogicCore.Graph.Edges.ForEach(a => a.RoutingPoints = null);
var resultCoords = localLogicCore.Compute(cancellationToken);
@ -971,7 +972,7 @@ namespace GraphX.Controls
/// </summary>
/// <param name="generateAllEdges">Generate all available edges for graph</param>
#if WPF
public virtual void RelayoutGraph(bool generateAllEdges = false)
public override void RelayoutGraph(bool generateAllEdges = false)
{
LogicCore.PushFilters();
_relayoutGraphMain(generateAllEdges);
@ -1192,7 +1193,7 @@ namespace GraphX.Controls
if (LogicCore.Graph == null)
throw new InvalidDataException("GraphArea.GenerateGraph() -> LogicCore.Graph property is null while trying to generate graph!");
LogicCore.PushFilters();
LogicCore.PushFilters();
if (AutoAssignMissingDataId)
AutoresolveIds(false, graph);
if (!LogicCore.IsCustomLayout)
@ -1326,6 +1327,7 @@ namespace GraphX.Controls
protected void ReapplySingleEdgeVisualProperties(EdgeControl item)
{
if (this._edgesDragEnabled != null) DragBehaviour.SetIsDragEnabled(item, this._edgesDragEnabled.Value);
if (_svEdgeDashStyle != null) item.DashStyle = _svEdgeDashStyle.Value;
if (_svShowEdgeArrows != null) item.SetCurrentValue(EdgeControlBase.ShowArrowsProperty, _svShowEdgeArrows.Value);
if (_svShowEdgeLabels != null) item.ShowLabel = _svShowEdgeLabels.Value;
@ -1435,6 +1437,21 @@ namespace GraphX.Controls
}
}
private bool? _edgesDragEnabled;
/// <summary>
/// Sets drag mode for all edges
/// </summary>
/// <param name="isEnabled">Is drag mode enabled</param>
public void SetEdgesDrag(bool isEnabled)
{
_edgesDragEnabled = isEnabled;
foreach (var item in EdgesList)
{
DragBehaviour.SetIsDragEnabled(item.Value, isEnabled);
}
}
private VertexShape? _svVertexShape;// = VertexShape.Rectangle;
/// <summary>
/// Sets math shape for all vertices
@ -1515,7 +1532,7 @@ namespace GraphX.Controls
{
if (LogicCore == null)
throw new GX_InvalidDataException("LogicCore -> Not initialized!");
RemoveAllEdges();
RemoveAllEdges();
AutoresolveEdgeIds();
@ -1785,7 +1802,7 @@ namespace GraphX.Controls
}
/// <summary>
/// Get controls related to specified control
/// Get controls related to specified control
/// </summary>
/// <param name="ctrl">Original control</param>
/// <param name="resultType">Type of resulting related controls</param>
@ -1966,7 +1983,7 @@ namespace GraphX.Controls
/// <summary>
/// Export current graph layout into the JPEG image file. layout will be saved in full size.
/// </summary>
/// <param name="quality">Optional image quality parameter</param>
/// <param name="quality">Optional image quality parameter</param>
public virtual void ExportAsJpeg(int quality = 100)
{
ExportAsImageDialog(ImageType.JPEG, true, PrintHelper.DEFAULT_DPI, quality);
@ -1978,7 +1995,7 @@ namespace GraphX.Controls
/// <param name="itype">Image format</param>
/// <param name="dpi">Optional image DPI parameter</param>
/// <param name="useZoomControlSurface">Use zoom control parent surface to render bitmap (only visible zoom content will be exported)</param>
/// <param name="quality">Optional image quality parameter (for JPEG)</param>
/// <param name="quality">Optional image quality parameter (for JPEG)</param>
public virtual void ExportAsImageDialog(ImageType itype, bool useZoomControlSurface = false, double dpi = PrintHelper.DEFAULT_DPI, int quality = 100)
{
#if WPF
@ -2030,7 +2047,7 @@ namespace GraphX.Controls
}
#endif
/// <summary>
/// Sets GraphArea into printing mode when its size will be recalculated on each measuer and child controls will be arranged accordingly.
/// Sets GraphArea into printing mode when its size will be recalculated on each measuer and child controls will be arranged accordingly.
/// Use with caution. Can spoil normal work while active but is essential to set before printing or grabbing an image.
/// </summary>
/// <param name="value">True or False</param>
@ -2168,7 +2185,7 @@ namespace GraphX.Controls
protected virtual void CreateNewStateStorage()
{
_stateStorage = new StateStorage<TVertex, TEdge, TGraph>(this);
_stateStorage = new StateStorage<TVertex, TEdge, TGraph>(this);
}
#endregion

Просмотреть файл

@ -510,6 +510,10 @@ namespace GraphX
/// <returns></returns>
public abstract VertexControl[] GetAllVertexControls();
public abstract VertexControl GetVertexControlAt(Point position);
public abstract void RelayoutGraph(bool generateAllEdges = false);
// INTERNAL VARIABLES FOR CONTROLS INTEROPERABILITY
internal abstract bool IsEdgeRoutingEnabled { get; }
internal abstract bool EnableParallelEdges { get; }
@ -518,20 +522,20 @@ namespace GraphX
/// <summary>
/// Get controls related to specified control
/// Get controls related to specified control
/// </summary>
/// <param name="ctrl">Original control</param>
/// <param name="resultType">Type of resulting related controls</param>
/// <param name="edgesType">Optional edge controls type</param>
public abstract List<IGraphControl> GetRelatedControls(IGraphControl ctrl, GraphControlType resultType = GraphControlType.VertexAndEdge, EdgesType edgesType = EdgesType.Out);
/// <summary>
/// Get vertex controls related to specified control
/// Get vertex controls related to specified control
/// </summary>
/// <param name="ctrl">Original control</param>
/// <param name="edgesType">Edge types to query for vertices</param>
public abstract List<IGraphControl> GetRelatedVertexControls(IGraphControl ctrl, EdgesType edgesType = EdgesType.All);
/// <summary>
/// Get edge controls related to specified control
/// Get edge controls related to specified control
/// </summary>
/// <param name="ctrl">Original control</param>
/// <param name="edgesType">Edge types to query</param>
@ -641,11 +645,11 @@ namespace GraphX
return DesignerProperties.GetIsInDesignMode(this) ? DesignSize : (IsInPrintMode ? ContentSize.Size : new Size(10, 10));
#elif METRO
return DesignMode.DesignModeEnabled ? DesignSize : new Size(10, 10);
#endif
#endif
}
/// <summary>
/// Overridden measure. It calculates a size where all of
/// Overridden measure. It calculates a size where all of
/// of the vertices are visible.
/// </summary>
/// <param name="constraint">The size constraint.</param>
@ -660,7 +664,7 @@ namespace GraphX
foreach (UIElement child in InternalChildren)
#elif METRO
foreach (UIElement child in Children)
#endif
#endif
{
//measure the child
child.Measure(constraint);

Просмотреть файл

@ -1,14 +1,17 @@
#if WPF
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
#elif METRO
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
#endif
using GraphX.PCL.Common.Enums;
namespace GraphX.Controls
@ -22,11 +25,10 @@ namespace GraphX.Controls
/// </summary>
public int Id { get; set; }
public static readonly DependencyProperty ShapeProperty =
DependencyProperty.Register(nameof(Shape),
typeof(VertexShape),
typeof(StaticVertexConnectionPoint),
DependencyProperty.Register(nameof(Shape),
typeof(VertexShape),
typeof(StaticVertexConnectionPoint),
new PropertyMetadata(VertexShape.Circle));
/// <summary>
@ -38,12 +40,13 @@ namespace GraphX.Controls
set { SetValue(ShapeProperty, value); }
}
private Rect _rectangularSize;
public Rect RectangularSize {
get
{
if(_rectangularSize == Rect.Empty)
public Rect RectangularSize
{
get
{
if (_rectangularSize == Rect.Empty)
UpdateLayout();
return _rectangularSize;
}
@ -79,7 +82,7 @@ namespace GraphX.Controls
return null;
}
#endregion
#endregion Common part
private VertexControl _vertexControl;
protected VertexControl VertexControl => _vertexControl ?? (_vertexControl = GetVertexControl(GetParent()));
@ -101,7 +104,9 @@ namespace GraphX.Controls
{
_vertexControl = null;
}
#if WPF
public DependencyObject GetParent()
{
return VisualParent;
@ -114,6 +119,7 @@ namespace GraphX.Controls
position = new Point(position.X + vPos.X, position.Y + vPos.Y);
RectangularSize = new Rect(position, DesiredSize);
}
#elif METRO
public DependencyObject GetParent()
{
@ -129,4 +135,4 @@ namespace GraphX.Controls
}
#endif
}
}
}

Просмотреть файл

@ -1,10 +1,13 @@
using System;
using System.Linq;
#if WPF
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Controls;
#elif METRO
using MouseEventArgs = Windows.UI.Xaml.Input.PointerRoutedEventArgs;
using MouseButtonEventArgs = Windows.UI.Xaml.Input.PointerRoutedEventArgs;
@ -12,18 +15,19 @@ using Windows.UI.Xaml;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Controls;
#endif
using GraphX.PCL.Common.Enums;
using GraphX.PCL.Common.Exceptions;
using GraphX.Controls.Models;
using GraphX.PCL.Common.Interfaces;
namespace GraphX.Controls
{
/// <summary>
/// Visual vertex control
/// </summary>
#if WPF
[Serializable]
#endif
[TemplateVisualState(GroupName = "CommonStates", Name = "Normal")]
@ -33,9 +37,10 @@ namespace GraphX.Controls
[TemplateVisualState(GroupName = "CommonStates", Name = "Disabled")]
[TemplatePart(Name = "PART_vertexLabel", Type = typeof(IVertexLabelControl))]
[TemplatePart(Name = "PART_vcproot", Type = typeof(Panel))]
public class VertexControl: VertexControlBase
public class VertexControl : VertexControlBase
{
#if WPF
static VertexControl()
{
//override the StyleKey Property
@ -54,16 +59,15 @@ namespace GraphX.Controls
Vertex = vertexData;
EventOptions = new VertexEventOptions(this) { PositionChangeNotification = tracePositionChange };
foreach(var item in Enum.GetValues(typeof(EventType)).Cast<EventType>())
foreach (var item in Enum.GetValues(typeof(EventType)).Cast<EventType>())
UpdateEventhandling(item);
}
#region Position trace feature
private ChangeMonitor _xChangeMonitor;
private ChangeMonitor _yChangeMonitor;
internal void UpdatePositionTraceState()
{
if (EventOptions.PositionChangeNotification)
@ -100,12 +104,12 @@ namespace GraphX.Controls
private void changeMonitor_ChangeDetected(object source, EventArgs args)
{
if(ShowLabel)
if (ShowLabel)
VertexLabelControl?.UpdatePosition();
OnPositionChanged(new Point(), GetPosition());
}
#endregion
#endregion Position trace feature
public T FindDescendant<T>(string name)
{
@ -115,7 +119,7 @@ namespace GraphX.Controls
#region Event tracing
private bool _clickTrack;
private Point _clickTrackPoint;
private Point _clickTrackPoint;
internal void UpdateEventhandling(EventType typ)
{
@ -133,22 +137,27 @@ namespace GraphX.Controls
PreviewMouseMove -= VertexControl_PreviewMouseMove;
}
break;
case EventType.MouseDoubleClick:
if (EventOptions.MouseDoubleClickEnabled) MouseDoubleClick += VertexControl_MouseDoubleClick;
else MouseDoubleClick -= VertexControl_MouseDoubleClick;
break;
case EventType.MouseMove:
if (EventOptions.MouseMoveEnabled) MouseMove += VertexControl_MouseMove;
else MouseMove -= VertexControl_MouseMove;
break;
case EventType.MouseEnter:
if (EventOptions.MouseEnterEnabled) MouseEnter += VertexControl_MouseEnter;
else MouseEnter -= VertexControl_MouseEnter;
break;
case EventType.MouseLeave:
if (EventOptions.MouseLeaveEnabled) MouseLeave += VertexControl_MouseLeave;
else MouseLeave -= VertexControl_MouseLeave;
break;
case EventType.PositionChangeNotify:
UpdatePositionTraceState();
break;
@ -157,18 +166,18 @@ namespace GraphX.Controls
MouseUp += VertexControl_MouseUp;
}
void VertexControl_PreviewMouseMove(object sender, MouseEventArgs e)
private void VertexControl_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (!_clickTrack)
return;
if (!_clickTrack)
return;
var curPoint = RootArea != null ? Mouse.GetPosition(RootArea) : new Point();
if (curPoint != _clickTrackPoint)
_clickTrack = false;
if (curPoint != _clickTrackPoint)
_clickTrack = false;
}
void VertexControl_MouseUp(object sender, MouseButtonEventArgs e)
private void VertexControl_MouseUp(object sender, MouseButtonEventArgs e)
{
if (RootArea != null && Visibility == Visibility.Visible)
{
@ -183,33 +192,35 @@ namespace GraphX.Controls
e.Handled = true;
}
void VertexControl_MouseDoubleClick(object sender, MouseButtonEventArgs e)
private void VertexControl_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (RootArea != null && Visibility == Visibility.Visible)
RootArea.OnVertexDoubleClick(this, e);
//e.Handled = true;
}
void VertexControl_Down(object sender, MouseButtonEventArgs e)
private void VertexControl_Down(object sender, MouseButtonEventArgs e)
{
if (RootArea != null && Visibility == Visibility.Visible)
RootArea.OnVertexSelected(this, e, Keyboard.Modifiers);
if (RootArea != null && Visibility == Visibility.Visible)
RootArea.OnVertexSelected(this, e, Keyboard.Modifiers);
_clickTrack = true;
_clickTrackPoint = RootArea != null ? Mouse.GetPosition(RootArea) : new Point();
_clickTrackPoint = RootArea != null ? Mouse.GetPosition(RootArea) : new Point();
e.Handled = true;
}
#endregion
#endregion Event tracing
#region Click Event
public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(nameof(Click), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(VertexControl));
public event RoutedEventHandler Click
{
add { AddHandler(ClickEvent, value); }
remove { RemoveHandler(ClickEvent, value); }
}
#endregion
#endregion Click Event
#region ChangeMonitor class
@ -259,9 +270,12 @@ namespace GraphX.Controls
}
}
#endregion
#endregion ChangeMonitor class
#elif METRO
#region Attached property tracer
private static readonly DependencyProperty TestXProperty =
DependencyProperty.Register("TestX", typeof(double), typeof(VertexControl), new PropertyMetadata(0, Testxchanged));
@ -281,7 +295,8 @@ namespace GraphX.Controls
if (vc != null)
vc.OnPositionChanged();
}
#endregion
#endregion Attached property tracer
/// <summary>
/// Create vertex visual control
@ -327,18 +342,22 @@ namespace GraphX.Controls
if (EventOptions.MouseClickEnabled) PointerPressed += VertexControl_Down;
else PointerPressed -= VertexControl_Down;
break;
case EventType.MouseDoubleClick:
// if (EventOptions.MouseDoubleClickEnabled) Poi += VertexControl_MouseDoubleClick;
// else MouseDoubleClick -= VertexControl_MouseDoubleClick;
break;
case EventType.MouseMove:
if (EventOptions.MouseMoveEnabled) PointerMoved += VertexControl_MouseMove;
else PointerMoved -= VertexControl_MouseMove;
break;
case EventType.MouseEnter:
if (EventOptions.MouseEnterEnabled) PointerEntered += VertexControl_MouseEnter;
else PointerEntered -= VertexControl_MouseEnter;
break;
case EventType.MouseLeave:
if (EventOptions.MouseLeaveEnabled) PointerExited += VertexControl_MouseLeave;
else PointerExited -= VertexControl_MouseLeave;
@ -355,14 +374,14 @@ namespace GraphX.Controls
}
#endif
/// <summary>
/// Gets the root element which hosts VCPs so you can add them at runtime. Requires Panel-descendant template item defined named PART_vcproot.
/// </summary>
public Panel VCPRoot { get; protected set; }
#if WPF
public
public
#elif METRO
protected
#endif
@ -377,7 +396,7 @@ namespace GraphX.Controls
if (VertexLabelControl != null)
{
if(ShowLabel) VertexLabelControl.Show(); else VertexLabelControl.Hide();
if (ShowLabel) VertexLabelControl.Show(); else VertexLabelControl.Hide();
UpdateLayout();
VertexLabelControl.UpdatePosition();
}
@ -387,31 +406,29 @@ namespace GraphX.Controls
throw new GX_InvalidDataException("Vertex connection points in VertexControl template must have unique Id!");
}
#region Events handling
#region Events handling
void VertexControl_MouseLeave(object sender, MouseEventArgs e)
private void VertexControl_MouseLeave(object sender, MouseEventArgs e)
{
if (RootArea != null && Visibility == Visibility.Visible)
RootArea.OnVertexMouseLeave(this, e);
VisualStateManager.GoToState(this, "PointerLeave", true);
}
void VertexControl_MouseEnter(object sender, MouseEventArgs e)
private void VertexControl_MouseEnter(object sender, MouseEventArgs e)
{
if (RootArea != null && Visibility == Visibility.Visible)
RootArea.OnVertexMouseEnter(this, e);
VisualStateManager.GoToState(this, "PointerOver", true);
}
void VertexControl_MouseMove(object sender, MouseEventArgs e)
private void VertexControl_MouseMove(object sender, MouseEventArgs e)
{
if (RootArea != null)
RootArea.OnVertexMouseMove(this, e);
}
#endregion
#endregion Events handling
/// <summary>
/// Cleans all potential memory-holding code
@ -440,6 +457,6 @@ namespace GraphX.Controls
public T GetDataVertex<T>() where T : IGraphXVertex
{
return (T)Vertex;
}
}
}
}

Просмотреть файл

@ -4,6 +4,8 @@ using System.Linq;
#if WPF
using System.Windows;
using System.Windows.Controls;
using USize = System.Windows.Size;
using Point = System.Windows.Point;
#elif METRO
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
@ -12,6 +14,7 @@ using Windows.Foundation;
using GraphX.Controls.Models;
using GraphX.PCL.Common;
using GraphX.PCL.Common.Enums;
using Rect = GraphX.Measure.Rect;
namespace GraphX.Controls
{
@ -232,6 +235,17 @@ namespace GraphX.Controls
return result;
}
public IVertexConnectionPoint GetConnectionPointAt(Point position)
{
Measure(new USize(double.PositiveInfinity, double.PositiveInfinity));
return VertexConnectionPointsList.FirstOrDefault(a =>
{
var rect = new Rect(a.RectangularSize.X, a.RectangularSize.Y, a.RectangularSize.Width, a.RectangularSize.Height);
return rect.Contains(position.ToGraphX());
});
}
/// <summary>
/// Internal method. Attaches label to control
/// </summary>

Просмотреть файл

@ -1,9 +1,12 @@
using System;
using System.Text.RegularExpressions;
#if WPF
using System.Windows;
using System.Windows.Media;
using System.ComponentModel;
#elif METRO
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
@ -49,6 +52,7 @@ namespace GraphX.Controls
{
return element;
}
var childCount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < childCount; i++)
{
@ -66,6 +70,5 @@ namespace GraphX.Controls
return DesignMode.DesignModeEnabled;
#endif
}
}
}
}

Просмотреть файл

@ -10,11 +10,11 @@
/// Gets or sets source vertex
/// </summary>
new TVertex Source { get; set; }
/// <summary>
/// Gets or sets target vertex
/// </summary>
new TVertex Target { get; set; }
}
/// <summary>
@ -26,33 +26,36 @@
/// Gets or sets source vertex
/// </summary>
new IGraphXVertex Source { get; set; }
/// <summary>
/// Gets or sets target vertex
/// </summary>
new IGraphXVertex Target { get; set; }
}
/// <summary>
/// Core edge data interface
/// </summary>
public interface IGraphXCommonEdge: IIdentifiableGraphDataObject, IRoutingInfo
public interface IGraphXCommonEdge : IIdentifiableGraphDataObject, IRoutingInfo
{
/// <summary>
/// Gets if edge is self-looped
/// </summary>
bool IsSelfLoop { get; }
/// <summary>
/// Optional parameter to bind edge to static vertex connection point
/// </summary>
int? SourceConnectionPointId { get; }
int? SourceConnectionPointId { get; set; }
/// <summary>
/// Optional parameter to bind edge to static vertex connection point
/// </summary>
int? TargetConnectionPointId { get; }
/// <summary>
/// Reverse the calculated routing path points.
/// </summary>
bool ReversePath { get; set; }}
}
int? TargetConnectionPointId { get; set; }
/// <summary>
/// Reverse the calculated routing path points.
/// </summary>
bool ReversePath { get; set; }
}
}