diff --git a/Documents/AIV.cs b/Documents/AIV.cs index 888f72e..98de494 100644 --- a/Documents/AIV.cs +++ b/Documents/AIV.cs @@ -4,9 +4,9 @@ using System.Reflection; [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("panthernet dev works")] [assembly: AssemblyProduct("GraphX for .NET")] -[assembly: AssemblyCopyright("Copyright www.panthernet.ru © 2016")] +[assembly: AssemblyCopyright("Copyright www.panthernet.ru © 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2.3.7.0")] -[assembly: AssemblyFileVersion("2.3.7.0")] +[assembly: AssemblyVersion("2.3.8.0")] +[assembly: AssemblyFileVersion("2.3.8.0")] diff --git a/Documents/CHANGELOG.txt b/Documents/CHANGELOG.txt index c72629d..79781ce 100644 --- a/Documents/CHANGELOG.txt +++ b/Documents/CHANGELOG.txt @@ -1,4 +1,36 @@ -RELEASE 2.3.7 +RELEASE 2.3.8 +- Implemented multiple labels logic for edges. Now you can display multiple labels across the edge +- Added LabelHorizontalOffset property for labels which offsets labels along the edge given some offset value in total edge length percent + +CODE CHANGES: +- Properties ShowLabel,LabelVerticalOffset,AlignLabelsToEdges moved from EdgeControlBase to EdgeLabelControl class + - Property AlignLabelsToEdges renamed to AlignToEdge +- Property EdgeControlBase.EdgeLabelControl renamed to EdgeLabelControls and now has IList type +- Added new method IList EdgeControlBase.GetLabelSizes() +- Added new method IList EdgeControlBase.GetLabelControls() +- GraphArea.ShowAllEdgesLabels() method now has one-time effect and will not be reapplied after relayout +- GraphArea.ShowAllEdgesLabels() method now has one-time effect and will not be reapplied after relayout +- GraphArea.EdgeLabelFactory is now setup up with default factory generating one AttachableEdgeLabelControl +- Label factory method CreateLabel() now returns IEnumerable to accomodate multiple labels generation during single factory pass + +OBSOLETE AND REMOVED STUFF: +General +- Old style arrows are now not supported +- Old style edge labels (EdgeLabelControl) are now not supported, make sure to remove it from XAML templates and update your code to use AttachableEdgeLabelControl + +EdgeControlBase +- LabelAngle +- GetLabelSize() +- SetCustomLabelSize() + +EdgeControl +- LabelMouseDown event removed + +Other: +- showLabel parameter has been removed from all EdgeControl constructors and factories + + +RELEASE 2.3.7 - Added edge drag functionality to be able to reattach edge to another vertex (thanks to LaborJos) - Fixed SimpleTreeLayout vertex sizes supplement (thanks to edgardozoppi) - Fixed and improved parallel edge handling (thanks to perturbare) diff --git a/Examples/ShowcaseApp.WPF/Pages/Mini/EdgesParallel.xaml.cs b/Examples/ShowcaseApp.WPF/Pages/Mini/EdgesParallel.xaml.cs index e7ce92b..565fc52 100644 --- a/Examples/ShowcaseApp.WPF/Pages/Mini/EdgesParallel.xaml.cs +++ b/Examples/ShowcaseApp.WPF/Pages/Mini/EdgesParallel.xaml.cs @@ -3,6 +3,8 @@ using System.ComponentModel; using System.Linq; using System.Windows; using System.Windows.Controls; +using GraphX.Controls; +using GraphX.Controls.Models; using GraphX.PCL.Common.Enums; using ShowcaseApp.WPF.Annotations; using ShowcaseApp.WPF.Models; @@ -35,6 +37,7 @@ namespace ShowcaseApp.WPF.Pages.Mini cbEnablePE.Checked += CbMathShapeOnChecked; cbEnablePE.Unchecked += CbMathShapeOnChecked; + graphArea.EdgeLabelFactory = new DefaultEdgelabelFactory(); } private void CbMathShapeOnChecked(object sender, RoutedEventArgs routedEventArgs) @@ -88,12 +91,12 @@ namespace ShowcaseApp.WPF.Pages.Mini zoomControl.MaxZoom = 50; //manual edge corrections var eList = graphArea.EdgesList.Values.ToList(); - eList[0].LabelVerticalOffset = 12; - eList[1].LabelVerticalOffset = 12; + eList[0].GetLabelControls().FirstOrDefault().LabelVerticalOffset = 12; + eList[1].GetLabelControls().FirstOrDefault().LabelVerticalOffset = 12; - eList[2].ShowLabel = false; - eList[3].LabelVerticalOffset = 12; - eList[4].LabelVerticalOffset = 12; + eList[2].GetLabelControls().FirstOrDefault().ShowLabel = false; + // eList[3].GetLabelControls().FirstOrDefault().LabelVerticalOffset = 12; + // eList[4].GetLabelControls().FirstOrDefault().LabelVerticalOffset = -12; //PS: to see how parallel edges logic works go to GraphArea::UpdateParallelEdgesData() method diff --git a/Examples/ShowcaseApp.WPF/Templates/Mini/CommonMiniTemplate.xaml b/Examples/ShowcaseApp.WPF/Templates/Mini/CommonMiniTemplate.xaml index fcb796c..e8dc92c 100644 --- a/Examples/ShowcaseApp.WPF/Templates/Mini/CommonMiniTemplate.xaml +++ b/Examples/ShowcaseApp.WPF/Templates/Mini/CommonMiniTemplate.xaml @@ -35,15 +35,6 @@ - - @@ -51,13 +42,21 @@ - \ No newline at end of file diff --git a/Examples/UAP.SimpleGraph/Common/templates.xaml b/Examples/UAP.SimpleGraph/Common/templates.xaml index 75329ca..ffad3f4 100644 --- a/Examples/UAP.SimpleGraph/Common/templates.xaml +++ b/Examples/UAP.SimpleGraph/Common/templates.xaml @@ -12,70 +12,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Examples/UAP.SimpleGraph/Common/templates2.xaml b/Examples/UAP.SimpleGraph/Common/templates2.xaml index c502ae9..66c58c3 100644 --- a/Examples/UAP.SimpleGraph/Common/templates2.xaml +++ b/Examples/UAP.SimpleGraph/Common/templates2.xaml @@ -163,14 +163,11 @@ - - - - diff --git a/Examples/WindowsDesktop_VB.NET_WinForms_Example/Templates/template.xaml b/Examples/WindowsDesktop_VB.NET_WinForms_Example/Templates/template.xaml index c13691a..469aaa1 100644 --- a/Examples/WindowsDesktop_VB.NET_WinForms_Example/Templates/template.xaml +++ b/Examples/WindowsDesktop_VB.NET_WinForms_Example/Templates/template.xaml @@ -57,7 +57,6 @@ - @@ -86,23 +85,18 @@ - \ No newline at end of file diff --git a/GraphX for .NET.sln b/GraphX for .NET.sln index 748d7ce..34ce555 100644 --- a/GraphX for .NET.sln +++ b/GraphX for .NET.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.27130.2020 +VisualStudioVersion = 15.0.27130.2027 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Examples", "Examples", "{D9664367-2C2C-46B2-81A6-26CDCD087B29}" EndProject @@ -156,6 +156,7 @@ Global {4BEBC41E-2710-4613-80B1-198E08D10619}.Release|x86.ActiveCfg = Release|x86 {4BEBC41E-2710-4613-80B1-198E08D10619}.Release|x86.Build.0 = Release|x86 {51C7122B-AAD1-4C49-B2E9-1B09B367952F}.Debug|Any CPU.ActiveCfg = Debug|x86 + {51C7122B-AAD1-4C49-B2E9-1B09B367952F}.Debug|Any CPU.Build.0 = Debug|x86 {51C7122B-AAD1-4C49-B2E9-1B09B367952F}.Debug|ARM.ActiveCfg = Debug|ARM {51C7122B-AAD1-4C49-B2E9-1B09B367952F}.Debug|ARM.Build.0 = Debug|ARM {51C7122B-AAD1-4C49-B2E9-1B09B367952F}.Debug|ARM.Deploy.0 = Debug|ARM @@ -188,7 +189,7 @@ Global {51C7122B-AAD1-4C49-B2E9-1B09B367952F} = {D9664367-2C2C-46B2-81A6-26CDCD087B29} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {9548CBF3-9C1D-4132-B928-475E2AA6A99B} VisualSVNWorkingCopyRoot = . + SolutionGuid = {9548CBF3-9C1D-4132-B928-475E2AA6A99B} EndGlobalSection EndGlobal diff --git a/GraphX.Controls/Controls/EdgeControl.cs b/GraphX.Controls/Controls/EdgeControl.cs index e0baa30..8a59d1a 100644 --- a/GraphX.Controls/Controls/EdgeControl.cs +++ b/GraphX.Controls/Controls/EdgeControl.cs @@ -12,6 +12,7 @@ using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Input; #endif using GraphX.Controls.Models; +using GraphX.PCL.Common; using GraphX.PCL.Common.Enums; using GraphX.PCL.Common.Interfaces; @@ -57,26 +58,6 @@ namespace GraphX.Controls #endregion - public event EdgeLabelEventHandler LabelMouseDown; - protected void OnLabelMouseDown(MouseButtonEventArgs mArgs, ModifierKeys keys) - { - LabelMouseDown?.Invoke(this, new EdgeLabelSelectedEventArgs(EdgeLabelControl, this, mArgs, keys)); - } - - protected override void OnEdgeLabelUpdated() - { - var ctrl = EdgeLabelControl as Control; - if (ctrl == null) return; -#if WPF - MouseButtonEventHandler func = (sender, args) => OnLabelMouseDown(args, Keyboard.Modifiers); - ctrl.MouseDown -= func; - ctrl.MouseDown += func; -#elif METRO - PointerEventHandler func = (sender, args) => OnLabelMouseDown(args, null); - ctrl.PointerPressed -= func; - ctrl.PointerPressed += func; -#endif - } #region public Clean() public override void Clean() @@ -97,11 +78,8 @@ namespace GraphX.Controls Linegeometry = null; LinePathObject = null; SelfLoopIndicator = null; - if (EdgeLabelControl != null) - { - EdgeLabelControl.Dispose(); - EdgeLabelControl = null; - } + EdgeLabelControls.ForEach(l=> l.Dispose()); + EdgeLabelControls.Clear(); if (EdgePointerForSource != null) { @@ -338,13 +316,12 @@ namespace GraphX.Controls { } - public EdgeControl(VertexControl source, VertexControl target, object edge, bool showLabels = false, bool showArrows = true) + public EdgeControl(VertexControl source, VertexControl target, object edge, bool showArrows = true) { DataContext = edge; Source = source; Target = target; Edge = edge; DataContext = edge; this.SetCurrentValue(ShowArrowsProperty, showArrows); - this.SetCurrentValue(ShowLabelProperty, showLabels); IsHiddenEdgesUpdated = true; #if METRO diff --git a/GraphX.Controls/Controls/EdgeControlBase.cs b/GraphX.Controls/Controls/EdgeControlBase.cs index c1eb8ec..cc86f75 100644 --- a/GraphX.Controls/Controls/EdgeControlBase.cs +++ b/GraphX.Controls/Controls/EdgeControlBase.cs @@ -1,11 +1,12 @@ using System; +using System.Collections.Generic; using System.Linq; using GraphX.Controls.Models; using GraphX.PCL.Common.Enums; using GraphX.PCL.Common.Exceptions; using GraphX.PCL.Common.Interfaces; using System.Windows.Input; - +using GraphX.PCL.Common; #if WPF using System.Windows.Controls; @@ -15,7 +16,6 @@ using System.Windows.Shapes; using SysRect = System.Windows.Rect; #elif METRO -using GraphX.PCL.Common; using GraphX.Measure; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; @@ -30,8 +30,7 @@ namespace GraphX.Controls { [TemplatePart(Name = "PART_edgePath", Type = typeof(Path))] [TemplatePart(Name = "PART_SelfLoopedEdge", Type = typeof(FrameworkElement))] - [TemplatePart(Name = "PART_edgeArrowPath", Type = typeof(Path))]//obsolete, present for exception - [TemplatePart(Name = "PART_edgeLabel", Type = typeof(IEdgeLabelControl))] + [TemplatePart(Name = "PART_edgeLabel", Type = typeof(IEdgeLabelControl))] //obsolete, present for exception [TemplatePart(Name = "PART_EdgePointerForSource", Type = typeof(IEdgePointer))] [TemplatePart(Name = "PART_EdgePointerForTarget", Type = typeof(IEdgePointer))] public abstract class EdgeControlBase : Control, IGraphControl, IDisposable @@ -164,7 +163,7 @@ namespace GraphX.Controls typeof(EdgeControlBase), new PropertyMetadata(null)); - private double _labelAngle; + /* private double _labelAngle; /// /// Gets or sets vertex label angle @@ -180,7 +179,7 @@ namespace GraphX.Controls _labelAngle = value; if (EdgeLabelControl != null) EdgeLabelControl.Angle = _labelAngle; } - } + }*/ #region DashStyle @@ -290,50 +289,6 @@ 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), - new PropertyMetadata(false, showlabel_changed)); - - private static void showlabel_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - (d as EdgeControlBase)?.UpdateEdge(); - } - - /// - /// Show edge label.Default value is False. - /// - public bool ShowLabel { get { return (bool)GetValue(ShowLabelProperty); } set { SetValue(ShowLabelProperty, value); } } - - /// - /// Gets or sets if lables should be aligned to edges and be displayed under the same angle - /// - public bool AlignLabelsToEdges - { - get { return _alignLabelsToEdges; } - set - { - _alignLabelsToEdges = value; - if (EdgeLabelControl != null) - { - if (value == false) EdgeLabelControl.Angle = 0; - EdgeLabelControl.UpdatePosition(); - } - } - } - - protected bool _alignLabelsToEdges; - - public static readonly DependencyProperty LabelVerticalOffsetProperty = DependencyProperty.Register("LabelVerticalOffset", - typeof(double), - typeof(EdgeControlBase), - new PropertyMetadata(0d)); - - /// - /// Offset for labels Y axis to display it above/below the edge - /// - public double LabelVerticalOffset { get { return (double)GetValue(LabelVerticalOffsetProperty); } set { SetValue(LabelVerticalOffsetProperty, value); } } - /// /// Gets or Sets that user controls the path geometry object or it is generated automatically /// @@ -349,12 +304,12 @@ namespace GraphX.Controls /// protected Path LinePathObject; - private IEdgeLabelControl _edgeLabelControl; + private IList _edgeLabelControls = new List(); /// /// Templated label control to display labels /// - protected internal IEdgeLabelControl EdgeLabelControl { get { return _edgeLabelControl; } set { _edgeLabelControl = value; OnEdgeLabelUpdated(); } } + protected internal IList EdgeLabelControls { get { return _edgeLabelControls; } set { _edgeLabelControls = value; OnEdgeLabelUpdated(); } } protected IEdgePointer EdgePointerForSource; protected IEdgePointer EdgePointerForTarget; @@ -404,17 +359,29 @@ namespace GraphX.Controls /// public void AttachLabel(IEdgeLabelControl ctrl) { - EdgeLabelControl = ctrl; - UpdateLabelLayout(); + EdgeLabelControls.Add(ctrl); + if(!RootArea.Children.Contains((UIElement)ctrl)) + RootArea.Children.Add((UIElement)ctrl); + ctrl.Show(); + var r = ctrl.GetSize(); + if (r == SysRect.Empty) + { + ctrl.UpdateLayout(); + ctrl.UpdatePosition(); + } } /// /// Internal method. Detaches label from control. /// - public void DetachLabel() - { - (EdgeLabelControl as IAttachableControl)?.Detach(); - EdgeLabelControl = null; + public void DetachLabels(IEdgeLabelControl ctrl = null) + { + EdgeLabelControls.Where(l => l is IAttachableControl).Cast>().ForEach(label => + { + label.Detach(); + RootArea.Children.Remove((UIElement)label); + }); + EdgeLabelControls.Clear(); } /// @@ -422,8 +389,12 @@ namespace GraphX.Controls /// public void UpdateLabel() { - if (_edgeLabelControl != null && ShowLabel) - UpdateLabelLayout(true); + _edgeLabelControls.Where(l=> l.ShowLabel).ForEach(l => + { + l.Show(); + l.UpdateLayout(); + l.UpdatePosition(); + }); } #endregion Properties & Fields @@ -499,7 +470,7 @@ namespace GraphX.Controls internal virtual void InvalidateChildren() { - EdgeLabelControl?.UpdateLayout(); + EdgeLabelControls.ForEach(l=>l.UpdateLayout()); if (LinePathObject != null) { var pos = this.Source.GetPosition(); @@ -530,10 +501,10 @@ namespace GraphX.Controls LinePathObject = GetTemplatePart("PART_edgePath") as Path; if (LinePathObject == null) throw new GX_ObjectNotFoundException("EdgeControlBase Template -> Edge template must contain 'PART_edgePath' Path object to draw route points!"); LinePathObject.Data = Linegeometry; - if (this.FindDescendantByName("PART_edgeArrowPath") != null) - throw new GX_ObsoleteException("PART_edgeArrowPath is obsolete! Please use new DefaultEdgePointer object in your EdgeControlBase template!"); - EdgeLabelControl = EdgeLabelControl ?? GetTemplatePart("PART_edgeLabel") as IEdgeLabelControl; + //EdgeLabelControl = EdgeLabelControl ?? GetTemplatePart("PART_edgeLabel") as IEdgeLabelControl; + if(GetTemplatePart("PART_edgeLabel") != null) + throw new GX_ObsoleteException("PART_edgeLabel is obsolete. Please use attachable labels mechanics!"); EdgePointerForSource = GetTemplatePart("PART_EdgePointerForSource") as IEdgePointer; EdgePointerForTarget = GetTemplatePart("PART_EdgePointerForTarget") as IEdgePointer; @@ -575,8 +546,11 @@ namespace GraphX.Controls if (Visibility == Visibility.Visible || IsHiddenEdgesUpdated) { //first show label to get DesiredSize - if (EdgeLabelControl != null) - if (ShowLabel) EdgeLabelControl.Show(); else EdgeLabelControl.Hide(); + EdgeLabelControls.ForEach(l => + { + if (l.ShowLabel) l.Show(); + else l.Hide(); + }); UpdateEdgeRendering(updateLabel); } } @@ -889,8 +863,8 @@ namespace GraphX.Controls GeometryHelper.TryFreeze(lineFigure); GeometryHelper.TryFreeze(Linegeometry); #endif - if (ShowLabel && EdgeLabelControl != null && _updateLabelPosition) - EdgeLabelControl.UpdatePosition(); + if (_updateLabelPosition) + EdgeLabelControls.Where(l => l.ShowLabel).ForEach(l => l.UpdatePosition()); if (LinePathObject == null) return; LinePathObject.Data = Linegeometry; @@ -1157,8 +1131,8 @@ namespace GraphX.Controls GeometryHelper.TryFreeze(lineFigure); GeometryHelper.TryFreeze(Linegeometry); #endif - if (ShowLabel && EdgeLabelControl != null && _updateLabelPosition && updateLabel) - EdgeLabelControl.UpdatePosition(); + if (_updateLabelPosition && updateLabel) + EdgeLabelControls.Where(l => l.ShowLabel).ForEach(l => l.UpdatePosition()); } private Point UpdateSourceEpData(Point from, Point to, bool allowUnsuppress = true) @@ -1203,25 +1177,22 @@ namespace GraphX.Controls #endif } - public virtual SysRect GetLabelSize() + public virtual IList GetLabelSizes() { - return EdgeLabelControl.GetSize(); + return EdgeLabelControls.Select(l=> l.GetSize()).ToList(); } - public void SetCustomLabelSize(SysRect rect) + /* public void SetCustomLabelSize(SysRect rect) { EdgeLabelControl.SetSize(rect); - } + }*/ - internal virtual void UpdateLabelLayout(bool force = false) + /// + /// Returns all edge controls attached to this entity + /// + public IList GetLabelControls() { - EdgeLabelControl.Show(); - if (EdgeLabelControl.GetSize() == SysRect.Empty || force) - - { - EdgeLabelControl.UpdateLayout(); - EdgeLabelControl.UpdatePosition(); - } + return EdgeLabelControls.ToList(); } } } \ No newline at end of file diff --git a/GraphX.Controls/Controls/EdgeLabels/AttachableEdgeLabelControl.cs b/GraphX.Controls/Controls/EdgeLabels/AttachableEdgeLabelControl.cs index 27ac170..d53e150 100644 --- a/GraphX.Controls/Controls/EdgeLabels/AttachableEdgeLabelControl.cs +++ b/GraphX.Controls/Controls/EdgeLabels/AttachableEdgeLabelControl.cs @@ -57,6 +57,7 @@ namespace GraphX.Controls AttachNode.IsVisibleChanged += AttachNode_IsVisibleChanged; #elif METRO AttachNode = node; + #endif node.AttachLabel(this); } @@ -76,7 +77,7 @@ namespace GraphX.Controls #if WPF void AttachNode_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) { - if(AttachNode.IsVisible && AttachNode.ShowLabel) + if(AttachNode.IsVisible && ShowLabel) base.Show(); else if (!AttachNode.IsVisible) { diff --git a/GraphX.Controls/Controls/EdgeLabels/EdgeLabelControl.cs b/GraphX.Controls/Controls/EdgeLabels/EdgeLabelControl.cs index b873c2f..c9126d4 100644 --- a/GraphX.Controls/Controls/EdgeLabels/EdgeLabelControl.cs +++ b/GraphX.Controls/Controls/EdgeLabels/EdgeLabelControl.cs @@ -31,8 +31,59 @@ namespace GraphX.Controls //hack to fix weird METRO error when it can't find this class [Bindable] #endif - public class EdgeLabelControl : ContentControl, IEdgeLabelControl + public abstract class EdgeLabelControl : ContentControl, IEdgeLabelControl { + + public static readonly DependencyProperty AlignToEdgeProperty = DependencyProperty.Register("AlignToEdge", + typeof(bool), + typeof(EdgeLabelControl), + new PropertyMetadata(false, (o, e) => + { + var ctrl = (EdgeLabelControl) o; + if ((bool)e.NewValue == false) ctrl.Angle = 0; + ctrl.UpdatePosition(); + } )); + + /// + /// Gets or sets if lables should be aligned to edges and be displayed under the same angle + /// + public bool AlignToEdge { get { return (bool)GetValue(AlignToEdgeProperty); } set { SetValue(AlignToEdgeProperty, value); }} + + + public static readonly DependencyProperty LabelVerticalOffsetProperty = DependencyProperty.Register("LabelVerticalOffset", + typeof(double), + typeof(EdgeLabelControl), + new PropertyMetadata(0d)); + + /// + /// Offset for label Y axis to display it above/below the edge + /// + public double LabelVerticalOffset { get { return (double)GetValue(LabelVerticalOffsetProperty); } set { SetValue(LabelVerticalOffsetProperty, value); } } + + public static readonly DependencyProperty LabelHorizontalOffsetProperty = DependencyProperty.Register("LabelHorizontalOffset", + typeof(double), + typeof(EdgeLabelControl), + new PropertyMetadata(0d)); + + /// + /// Offset for label X axis to display it along the edge + /// + public double LabelHorizontalOffset { get { return (double)GetValue(LabelHorizontalOffsetProperty); } set { SetValue(LabelHorizontalOffsetProperty, value); } } + + public static readonly DependencyProperty ShowLabelProperty = DependencyProperty.Register("ShowLabel", + typeof(bool), + typeof(EdgeLabelControl), + new PropertyMetadata(false, showlabel_changed)); + + private static void showlabel_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as EdgeControlBase)?.UpdateEdge(); + } + + /// + /// Show edge label.Default value is False. + /// + public bool ShowLabel { get { return (bool)GetValue(ShowLabelProperty); } set { SetValue(ShowLabelProperty, value); } } public static readonly DependencyProperty DisplayForSelfLoopedEdgesProperty = DependencyProperty.Register("DisplayForSelfLoopedEdges", typeof(bool), @@ -228,12 +279,14 @@ namespace GraphX.Controls var desiredSize = DesiredSize; bool flipAxis = p1.X > p2.X; // Flip axis if source is "after" target + ApplyLabelHorizontalOffset(edgeLength, LabelHorizontalOffset); + // Calculate the center point of the edge var centerPoint = new Point(p1.X + edgeLength * Math.Cos(angleBetweenPoints), p1.Y - edgeLength * Math.Sin(angleBetweenPoints)); - if (EdgeControl.AlignLabelsToEdges) + if (AlignToEdge) { // If we're aligning labels to the edges make sure add the label vertical offset - var yEdgeOffset = EdgeControl.LabelVerticalOffset; + var yEdgeOffset = LabelVerticalOffset; if (FlipOnRotation && flipAxis && !EdgeControl.IsParallel) // If we've flipped axis, move the offset to the other side of the edge yEdgeOffset = -yEdgeOffset; @@ -250,7 +303,19 @@ namespace GraphX.Controls UpdateFinalPosition(centerPoint, desiredSize); + #if METRO + GraphAreaBase.SetX(this, LastKnownRectSize.X, true); + GraphAreaBase.SetY(this, LastKnownRectSize.Y, true); + #else Arrange(LastKnownRectSize); + #endif + } + + protected virtual double ApplyLabelHorizontalOffset(double edgeLength, double offset) + { + if (offset == 0) return edgeLength; + edgeLength += edgeLength / 100 * offset; + return edgeLength; } /// @@ -314,7 +379,7 @@ namespace GraphX.Controls void EdgeLabelControl_LayoutUpdated(object sender, DefaultEventArgs e) { - if (EdgeControl == null || !EdgeControl.ShowLabel) return; + if (EdgeControl == null || !ShowLabel) return; if (LastKnownRectSize == SysRect.Empty || double.IsNaN(LastKnownRectSize.Width) || LastKnownRectSize.Width == 0) { UpdateLayout(); diff --git a/GraphX.Controls/Controls/GraphArea.cs b/GraphX.Controls/Controls/GraphArea.cs index be2848b..c0ca853 100644 --- a/GraphX.Controls/Controls/GraphArea.cs +++ b/GraphX.Controls/Controls/GraphArea.cs @@ -169,7 +169,8 @@ namespace GraphX.Controls /// public virtual void AddCustomChildControl(UIElement control) { - Children.Add(control); + if(!Children.Contains(control)) + Children.Add(control); SetX(control, 0); SetY(control, 0); } @@ -242,7 +243,7 @@ namespace GraphX.Controls { EnableVisualPropsRecovery = true; EnableVisualPropsApply = true; - //EdgeLabelFactory = new DefaultLabelFactory(); + EdgeLabelFactory = new DefaultEdgelabelFactory(); //VertexLabelFactory = new DefaultLabelFactory(); #region Designer Data @@ -434,11 +435,8 @@ namespace GraphX.Controls private void RemoveEdgeInternal(EdgeControlBase ctrl, bool removeEdgeFromDataGraph = false) { - if (ctrl.EdgeLabelControl != null) - { - Children.Remove((UIElement)ctrl.EdgeLabelControl); - ctrl.DetachLabel(); - } + ctrl.DetachLabels(); + Children.Remove(ctrl); if (removeEdgeFromDataGraph && LogicCore?.Graph != null && LogicCore.Graph.ContainsEdge(ctrl.Edge as TEdge)) LogicCore.Graph.RemoveEdge(ctrl.Edge as TEdge); @@ -659,14 +657,18 @@ namespace GraphX.Controls protected virtual void GenerateVertexLabel(VertexControl vertexControl) { - var label = VertexLabelFactory.CreateLabel(vertexControl); - if (!(label is IVertexLabelControl)) + var labels = VertexLabelFactory.CreateLabel(vertexControl); + if (labels == null) return; + if (labels.Any(l=> !(l is IVertexLabelControl))) throw new GX_InvalidDataException("Generated vertex label should implement IVertexLabelControl interface"); - if (_svVertexLabelShow == false || vertexControl.Visibility != Visibility.Visible) - label.Visibility = Visibility.Collapsed; - AddCustomChildControl(label); - label.Measure(new USize(double.MaxValue, double.MaxValue)); - ((IVertexLabelControl)label).UpdatePosition(); + labels.ForEach(l => + { + if (_svVertexLabelShow == false || vertexControl.Visibility != Visibility.Visible) + l.Visibility = Visibility.Collapsed; + AddCustomChildControl(l); + l.Measure(new USize(double.MaxValue, double.MaxValue)); + ((IVertexLabelControl)l).UpdatePosition(); + }); } protected virtual void GenerateEdgeLabels() @@ -681,14 +683,17 @@ namespace GraphX.Controls protected virtual void GenerateEdgeLabel(EdgeControl edgeControl) { - var label = EdgeLabelFactory.CreateLabel(edgeControl); - if (!(label is IEdgeLabelControl)) + var labels = EdgeLabelFactory.CreateLabel(edgeControl); + if (labels == null) return; + if (labels.Any(a=> !(a is IEdgeLabelControl))) throw new GX_InvalidDataException("Generated edge label should implement IEdgeLabelControl interface"); - if (_svShowEdgeLabels == false || edgeControl.Visibility != Visibility.Visible) - label.Visibility = Visibility.Collapsed; - AddCustomChildControl(label); - label.Measure(new USize(double.MaxValue, double.MaxValue)); - ((IEdgeLabelControl)label).UpdatePosition(); + + labels.ForEach(l => + { + AddCustomChildControl(l); + l.Measure(new USize(double.MaxValue, double.MaxValue)); + ((IEdgeLabelControl)l).UpdatePosition(); + }); } #endregion @@ -1330,8 +1335,8 @@ namespace GraphX.Controls 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; - if (_svAlignEdgeLabels != null) item.AlignLabelsToEdges = _svAlignEdgeLabels.Value; + //if (_svShowEdgeLabels != null) item.ShowLabel = _svShowEdgeLabels.Value; + //if (_svAlignEdgeLabels != null) item.AlignLabelsToEdges = _svAlignEdgeLabels.Value; if (_svUpdateLabelPosition != null) item.UpdateLabelPosition = _svUpdateLabelPosition.Value; if (_svEdgeHlEnabled != null) HighlightBehaviour.SetIsHighlightEnabled(item, _svEdgeHlEnabled.Value); if (_svEdgeHlObjectType != null) HighlightBehaviour.SetHighlightControl(item, _svEdgeHlObjectType.Value); @@ -1371,16 +1376,16 @@ namespace GraphX.Controls item.SetCurrentValue(EdgeControlBase.ShowArrowsProperty, isEnabled); } - private bool? _svShowEdgeLabels; + //private bool? _svShowEdgeLabels; /// /// Show or hide all edges labels /// /// Boolean value public void ShowAllEdgesLabels(bool isEnabled = true) { - _svShowEdgeLabels = isEnabled; + //_svShowEdgeLabels = isEnabled; foreach (var item in _edgeslist.Values) - item.SetCurrentValue(EdgeControlBase.ShowLabelProperty, isEnabled); + item.EdgeLabelControls.Cast().ForEach(l=> l.SetCurrentValue(EdgeLabelControl.ShowLabelProperty, isEnabled)); #if WPF InvalidateVisual(); #endif @@ -1401,18 +1406,16 @@ namespace GraphX.Controls #endif } - private bool? _svAlignEdgeLabels; + //private bool? _svAlignEdgeLabels; /// /// Aligns all labels with edges or displays them horizontaly /// /// Boolean value public void AlignAllEdgesLabels(bool isEnabled = true) { - _svAlignEdgeLabels = isEnabled; + //_svAlignEdgeLabels = isEnabled; foreach (var item in _edgeslist.Values) - { - item.AlignLabelsToEdges = isEnabled; - } + item.EdgeLabelControls.ForEach(l=> l.AlignToEdge = isEnabled); #if WPF InvalidateVisual(); #endif @@ -1541,11 +1544,9 @@ namespace GraphX.Controls if (item.Source == null || item.Target == null) continue; if (!_vertexlist.ContainsKey(item.Source) || !_vertexlist.ContainsKey(item.Target)) continue; var edgectrl = ControlFactory.CreateEdgeControl(_vertexlist[item.Source], _vertexlist[item.Target], - item, _svShowEdgeLabels ?? false, _svShowEdgeArrows ?? true, defaultVisibility); + item, _svShowEdgeArrows ?? true, defaultVisibility); InternalInsertEdge(item, edgectrl); //setup path - if (_svShowEdgeLabels == true) - edgectrl.SetCurrentValue(EdgeControlBase.ShowLabelProperty, true); } if (LogicCore.EnableParallelEdges) @@ -1659,7 +1660,7 @@ namespace GraphX.Controls foreach (var item in inlist) { if (gotSelfLoop) continue; - var ctrl = ControlFactory.CreateEdgeControl(_vertexlist[item.Source], vc, item, _svShowEdgeLabels ?? false, _svShowEdgeArrows ?? true, + var ctrl = ControlFactory.CreateEdgeControl(_vertexlist[item.Source], vc, item, _svShowEdgeArrows ?? true, defaultVisibility); InsertEdge(item, ctrl); ctrl.PrepareEdgePath(); @@ -1669,7 +1670,7 @@ namespace GraphX.Controls foreach (var item in outlist) { if (gotSelfLoop) continue; - var ctrl = ControlFactory.CreateEdgeControl(vc, _vertexlist[item.Target], item, _svShowEdgeLabels ?? false, _svShowEdgeArrows ?? true, + var ctrl = ControlFactory.CreateEdgeControl(vc, _vertexlist[item.Target], item, _svShowEdgeArrows ?? true, defaultVisibility); InsertEdge(item, ctrl); ctrl.PrepareEdgePath(); @@ -1866,7 +1867,7 @@ namespace GraphX.Controls foreach (var item in EdgesList) { // item.Key.RoutingPoints = new Point[] { new Point(0, 123), new Point(12, 12), new Point(10, 234.5) }; - dlist.Add(new GraphSerializationData { Position = new Measure.Point(), Data = item.Key, IsVisible = item.Value.Visibility == Visibility.Visible, HasLabel = item.Value.EdgeLabelControl != null }); + dlist.Add(new GraphSerializationData { Position = new Measure.Point(), Data = item.Key, IsVisible = item.Value.Visibility == Visibility.Visible, HasLabel = item.Value.EdgeLabelControls.Count > 0 }); if (item.Key.ID == -1) throw new GX_InvalidDataException("ExtractSerializationData() -> All edge datas must have positive unique ID!"); } return dlist; @@ -1916,7 +1917,7 @@ namespace GraphX.Controls if (datasource == null || datatarget == null) throw new GX_SerializationException("DeserializeFromFile() -> Serialization logic is broken! Vertex not found. All vertices must be processed before edges!"); - var ecc = ControlFactory.CreateEdgeControl(_vertexlist[datasource], _vertexlist[datatarget], edgedata, false, true, item.IsVisible ? Visibility.Visible : Visibility.Collapsed); + var ecc = ControlFactory.CreateEdgeControl(_vertexlist[datasource], _vertexlist[datatarget], edgedata, true, item.IsVisible ? Visibility.Visible : Visibility.Collapsed); InsertEdge(edgedata, ecc); LogicCore.Graph.AddEdge(edgedata); if (item.HasLabel) @@ -2214,9 +2215,19 @@ namespace GraphX.Controls UIElement element = null; if (vResult?.VertexLabelControl != null) element = (UIElement)vResult.VertexLabelControl; - if (eResult?.EdgeLabelControl != null) - element = (UIElement)eResult.EdgeLabelControl; - if (element != null && Children.Contains(element)) + if (eResult?.EdgeLabelControls.Count > 0) + { + eResult.EdgeLabelControls.ForEach(l => + { + if (Children.Contains((UIElement)l)) + { + Children.Remove((UIElement)l); + if (toFront) Children.Add((UIElement)l); + else Children.Insert(0, (UIElement)l); + } + }); + } + else if (element != null && Children.Contains(element)) { Children.Remove(element); if (toFront) Children.Add(element); diff --git a/GraphX.Controls/Controls/Misc/IEdgeLabelControl.cs b/GraphX.Controls/Controls/Misc/IEdgeLabelControl.cs index 966f1cc..506d0f6 100644 --- a/GraphX.Controls/Controls/Misc/IEdgeLabelControl.cs +++ b/GraphX.Controls/Controls/Misc/IEdgeLabelControl.cs @@ -9,6 +9,22 @@ namespace GraphX.Controls { public interface IEdgeLabelControl: IDisposable { + /// + /// Gets or sets if label should be aligned with the edge + /// + bool AlignToEdge { get; set; } + /// + /// Gets or sets if label is visible + /// + bool ShowLabel { get; set; } + /// + /// Gets or sets label vertical offset + /// + double LabelVerticalOffset { get; set; } + /// + /// Gets or sets label horizontal offset + /// + double LabelHorizontalOffset { get; set; } /// /// Gets or sets label drawing angle in degrees /// diff --git a/GraphX.Controls/Models/DefaultLabelFactory.cs b/GraphX.Controls/Models/DefaultLabelFactory.cs index c34cf7d..ce59783 100644 --- a/GraphX.Controls/Models/DefaultLabelFactory.cs +++ b/GraphX.Controls/Models/DefaultLabelFactory.cs @@ -1,4 +1,5 @@ -using System.Windows; +using System.Collections.Generic; +using System.Windows; #if METRO using Windows.UI.Xaml; #endif @@ -16,12 +17,12 @@ namespace GraphX.Controls.Models /// Returns newly generated label for parent control. Attachable labels will be auto attached if derived from IAttachableControl /// /// Parent control - public virtual TLabel CreateLabel(TCtrl control) + public virtual IEnumerable CreateLabel(TCtrl control) { var label = new TLabel(); var aLabel = label as IAttachableControl; aLabel?.Attach(control); - return label; + return new List {label}; } } diff --git a/GraphX.Controls/Models/GraphControlFactory.cs b/GraphX.Controls/Models/GraphControlFactory.cs index 0797fac..61aabd5 100644 --- a/GraphX.Controls/Models/GraphControlFactory.cs +++ b/GraphX.Controls/Models/GraphControlFactory.cs @@ -16,9 +16,9 @@ namespace GraphX.Controls.Models FactoryRootArea = graphArea; } - public virtual EdgeControl CreateEdgeControl(VertexControl source, VertexControl target, object edge, bool showLabels = false, bool showArrows = true, Visibility visibility = Visibility.Visible) + public virtual EdgeControl CreateEdgeControl(VertexControl source, VertexControl target, object edge, bool showArrows = true, Visibility visibility = Visibility.Visible) { - var edgectrl = new EdgeControl(source, target, edge, showLabels, showArrows) { RootArea = FactoryRootArea}; + var edgectrl = new EdgeControl(source, target, edge, showArrows) { RootArea = FactoryRootArea}; edgectrl.SetCurrentValue(UIElement.VisibilityProperty, visibility); return edgectrl; diff --git a/GraphX.Controls/Models/Interfaces/IGraphControlFactory.cs b/GraphX.Controls/Models/Interfaces/IGraphControlFactory.cs index 6e57677..b357671 100644 --- a/GraphX.Controls/Models/Interfaces/IGraphControlFactory.cs +++ b/GraphX.Controls/Models/Interfaces/IGraphControlFactory.cs @@ -8,7 +8,7 @@ namespace GraphX.Controls.Models { public interface IGraphControlFactory { - EdgeControl CreateEdgeControl(VertexControl source, VertexControl target, object edge, bool showLabels = false, bool showArrows = true, Visibility visibility = Visibility.Visible); + EdgeControl CreateEdgeControl(VertexControl source, VertexControl target, object edge, bool showArrows = true, Visibility visibility = Visibility.Visible); VertexControl CreateVertexControl(object vertexData); /// /// Root graph area for the factory diff --git a/GraphX.Controls/Models/Interfaces/ILabelFactory.cs b/GraphX.Controls/Models/Interfaces/ILabelFactory.cs index b2a4c59..be28800 100644 --- a/GraphX.Controls/Models/Interfaces/ILabelFactory.cs +++ b/GraphX.Controls/Models/Interfaces/ILabelFactory.cs @@ -1,4 +1,5 @@ -using System.Windows; +using System.Collections.Generic; +using System.Windows; #if METRO using Windows.UI.Xaml; #endif @@ -15,6 +16,6 @@ namespace GraphX.Controls.Models /// Returns newly generated label for parent control. Attachable labels will be auto attached if derived from IAttachableControl /// /// Parent control - TResult CreateLabel(TCtrl control); + IEnumerable CreateLabel(TCtrl control); } } \ No newline at end of file diff --git a/GraphX.Controls/Themes/Generic.xaml b/GraphX.Controls/Themes/Generic.xaml index 6279939..98def49 100644 --- a/GraphX.Controls/Themes/Generic.xaml +++ b/GraphX.Controls/Themes/Generic.xaml @@ -503,7 +503,6 @@ Height="10"/> - + --> @@ -529,23 +527,10 @@ - - - -