diff --git a/Documents/AIV.cs b/Documents/AIV.cs index c72594d7..ea6f964b 100644 --- a/Documents/AIV.cs +++ b/Documents/AIV.cs @@ -8,5 +8,5 @@ [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -[assembly: AssemblyVersion("2.3.0.0")] -[assembly: AssemblyFileVersion("2.3.0.0")] +[assembly: AssemblyVersion("2.3.1.0")] +[assembly: AssemblyFileVersion("2.3.1.0")] diff --git a/Documents/CHANGELOG.txt b/Documents/CHANGELOG.txt index 3a72c295..0bd4c244 100644 --- a/Documents/CHANGELOG.txt +++ b/Documents/CHANGELOG.txt @@ -1,15 +1,23 @@ -WIP 2.3.1 +RELEASE 2.3.1 DETAILED CHANGELOG: - Added default templates for attached vertex and edge labels + - Added HighlightBehavior::HighlightedEdgeType attached property that indicates In or Out edge is currently highlighted. Default value is None. + - Added GraphArea::GetRelatedVertexControls() and GraphArea::GetRelatedEdgeControls() methods to fetch corresponding objects faster and easily + - Added new printing logic. Now you can use GraphArea::PrintDialog() and GraphArea::PrintVisibleAreaDialog() methods with extended optional parameters + - Added new image export logic. Now GraphArea::ExportToImage() method allows graph image export in original size via optional parameter + - Added GraphArea::SetPrintMode() method which is used internally by print and image export methods but can be useful to override due to complex GraphArea print preparation logic. - Fixed excessive rendering issue when ZoomControl viewfinder is hidden. Should significantly increase performance of the viewfinder in this case. - Fixed showcase app edge example graph + - Fixed ZoomControl viewbox logic to correctly react on zoom control background change - Improved attachable labels logic. Simplified base classes, added checks for mandatory base classes - Improved attachable labels customization possibilities by making several methods virtual BREAKING CHANGES: - Made DefaultLabelFactory class abstract and it is now accepts only one generic param (output object type) + - GraphAreaBase now has new abstract members needed to be overriden in derived controls: GetRelatedVertexControls(), GetRelatedEdgeControls(), SetPrintMode() - +KNOWN ISSUES: + - Graph image export may throw OutOfMemoryError for large graphs RELEASE 2.3.0 diff --git a/Documents/TODO.txt b/Documents/TODO.txt index 4ebb706b..00e5fcea 100644 --- a/Documents/TODO.txt +++ b/Documents/TODO.txt @@ -1,15 +1,15 @@ TODO -1. CUT source ep edge also 3. Detect bidirectional edge and correctly display it w/o parallelization -4. CHeck printing Add edge labels - labels on path http://www.codeproject.com/Articles/30090/Text-On-A-Path-in-WPF -Use DrawVisual for edges at least... performance - http://en.wikipedia.org/wiki/Automatic_label_placement NOTES -1. If custom object 1st time position is incorrect - CHECK if it has Arrange() specified in LayoutUpdated event! \ No newline at end of file +1. If custom object 1st time position is incorrect - CHECK if it has Arrange() specified in LayoutUpdated event! + + +perf +http://blogs.msdn.com/b/llobo/archive/2009/11/10/new-wpf-features-cached-composition.aspx?wa=wsignin1.0 \ No newline at end of file diff --git a/Examples/ShowcaseApp.WPF/Pages/GeneralGraph.xaml.cs b/Examples/ShowcaseApp.WPF/Pages/GeneralGraph.xaml.cs index 7e297123..c280b42f 100644 --- a/Examples/ShowcaseApp.WPF/Pages/GeneralGraph.xaml.cs +++ b/Examples/ShowcaseApp.WPF/Pages/GeneralGraph.xaml.cs @@ -184,7 +184,7 @@ private void gg_saveAsPngImage_Click(object sender, RoutedEventArgs e) private void gg_printlay_Click(object sender, RoutedEventArgs e) { - gg_Area.PrintDialog("GraphX layout printing"); + gg_Area.PrintDialog(false, "GraphX layout printing"); } private void gg_vertexCount_PreviewTextInput(object sender, TextCompositionEventArgs e) diff --git a/GraphX.Controls/Behaviours/HighlightBehaviour.cs b/GraphX.Controls/Behaviours/HighlightBehaviour.cs index cfc4d5cb..f62c2b5c 100644 --- a/GraphX.Controls/Behaviours/HighlightBehaviour.cs +++ b/GraphX.Controls/Behaviours/HighlightBehaviour.cs @@ -1,4 +1,6 @@ -#if WPF + +using System.Linq; +#if WPF using System.Windows; using System.Windows.Controls; using System.Windows.Input; @@ -20,6 +22,17 @@ public static class HighlightBehaviour public static readonly DependencyProperty IsHighlightEnabledProperty = DependencyProperty.RegisterAttached("IsHighlightEnabled", typeof(bool), typeof(HighlightBehaviour), new PropertyMetadata(false, OnIsHighlightEnabledPropertyChanged)); public static readonly DependencyProperty HighlightControlProperty = DependencyProperty.RegisterAttached("HighlightControl", typeof(GraphControlType), typeof(HighlightBehaviour), new PropertyMetadata(GraphControlType.VertexAndEdge)); public static readonly DependencyProperty HighlightEdgesProperty = DependencyProperty.RegisterAttached("HighlightEdges", typeof(EdgesType), typeof(HighlightBehaviour), new PropertyMetadata(EdgesType.Out)); + public static readonly DependencyProperty HighlightedEdgeTypeProperty = DependencyProperty.RegisterAttached("HighlightedEdgeType", typeof(HighlightedEdgeType), typeof(HighlightBehaviour), new PropertyMetadata(HighlightedEdgeType.None)); + + public static HighlightedEdgeType GetHighlightedEdgeType(DependencyObject obj) + { + return (HighlightedEdgeType)obj.GetValue(HighlightedEdgeTypeProperty); + } + + public static void SetHighlightedEdgeType(DependencyObject obj, HighlightedEdgeType value) + { + obj.SetValue(HighlightedEdgeTypeProperty, value); + } public static bool GetIsHighlightEnabled(DependencyObject obj) { @@ -116,10 +129,17 @@ static void element_MouseLeave(object sender, PointerRoutedEventArgs e) var type = GetHighlightControl(sender as DependencyObject); var edgesType = GetHighlightEdges(sender as DependencyObject); SetHighlighted(sender as DependencyObject, false); - foreach (var item in ctrl.RootArea.GetRelatedControls(ctrl, type, edgesType)) - { - SetHighlighted(item as Control, false); - } + + if (type == GraphControlType.Vertex || type == GraphControlType.VertexAndEdge) + foreach (var item in ctrl.RootArea.GetRelatedVertexControls(ctrl, edgesType).Cast()) + SetHighlighted(item, false); + + if (type == GraphControlType.Edge || type == GraphControlType.VertexAndEdge) + foreach (var item in ctrl.RootArea.GetRelatedEdgeControls(ctrl, edgesType).Cast()) + { + SetHighlighted(item, false); + SetHighlightedEdgeType(item, HighlightedEdgeType.None); + } } #if WPF @@ -135,9 +155,27 @@ static void element_MouseEnter(object sender, PointerRoutedEventArgs e) var type = GetHighlightControl(sender as DependencyObject); var edgesType = GetHighlightEdges(sender as DependencyObject); SetHighlighted(sender as DependencyObject, true); - foreach (var item in ctrl.RootArea.GetRelatedControls(ctrl, type, edgesType)) + + //highlight related vertices + if(type == GraphControlType.Vertex || type == GraphControlType.VertexAndEdge) + foreach (var item in ctrl.RootArea.GetRelatedVertexControls(ctrl, edgesType).Cast()) + SetHighlighted(item, true); + //highlight related edges + if (type == GraphControlType.Edge || type == GraphControlType.VertexAndEdge) { - SetHighlighted(item as Control, true); + //separetely get in and out edges to set direction flag + if (edgesType == EdgesType.In || edgesType == EdgesType.All) + foreach (var item in ctrl.RootArea.GetRelatedEdgeControls(ctrl, EdgesType.In).Cast()) + { + SetHighlighted(item, true); + SetHighlightedEdgeType(item, HighlightedEdgeType.In); + } + if (edgesType == EdgesType.Out || edgesType == EdgesType.All) + foreach (var item in ctrl.RootArea.GetRelatedEdgeControls(ctrl, EdgesType.Out).Cast()) + { + SetHighlighted(item, true); + SetHighlightedEdgeType(item, HighlightedEdgeType.Out); + } } } #endregion @@ -148,5 +186,12 @@ public enum HighlightType Edge, VertexAndEdge } + + public enum HighlightedEdgeType + { + In, + Out, + None + } } } \ No newline at end of file diff --git a/GraphX.Controls/Controls/GraphArea.cs b/GraphX.Controls/Controls/GraphArea.cs index 4520bd98..8cc819f4 100644 --- a/GraphX.Controls/Controls/GraphArea.cs +++ b/GraphX.Controls/Controls/GraphArea.cs @@ -11,6 +11,7 @@ using Point = System.Windows.Point; using USize = System.Windows.Size; using GraphX.PCL.Logic.Helpers; +using Brushes = System.Windows.Media.Brushes; #elif METRO using Point = Windows.Foundation.Point; using USize = Windows.Foundation.Size; @@ -28,6 +29,7 @@ using GraphX.PCL.Common.Interfaces; using GraphX.PCL.Common.Models; using GraphX.Controls.Models; +using GraphX.PCL.Logic.Algorithms; using GraphX.PCL.Logic.Helpers; using QuickGraph; using Rect = GraphX.Measure.Rect; @@ -35,7 +37,7 @@ namespace GraphX.Controls { - public class GraphArea : GraphAreaBase, IDisposable + public class GraphArea : GraphAreaBase, IDisposable where TVertex : class, IGraphXVertex where TEdge : class, IGraphXEdge where TGraph : class, IMutableBidirectionalGraph @@ -68,15 +70,15 @@ public class GraphArea : GraphAreaBase, IDisposable public static readonly DependencyProperty LogicCoreProperty = DependencyProperty.Register("LogicCore", typeof(IGXLogicCore), typeof(GraphArea), new PropertyMetadata(null, logic_core_changed)); - private static + private static #if METRO async #endif - void logic_core_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) + void logic_core_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) { var graph = d as GraphArea; - if(graph == null) return; - switch(graph.LogicCoreChangeAction) + if (graph == null || graph.Parent == null) return; + switch (graph.LogicCoreChangeAction) { #if WPF case LogicCoreChangedAction.GenerateGraph: @@ -222,7 +224,7 @@ public IEnumerable GetChildControls(Func condition = null) /// public IDictionary EdgesList { - get { return _edgeslist; } + get { return _edgeslist; } } /// /// Gets vertex controls read only collection. To modify collection use AddVertex() RemoveVertex() methods. @@ -254,41 +256,41 @@ public GraphArea() #endif ControlFactory = new GraphControlFactory(this); StateStorage = new StateStorage(this); - } + } else - { - /* Width = DesignSize.Width; - Height = DesignSize.Height; - var vc = new VertexDataExample(1, "Vertex 1"); - var ctrl = ControlFactory.CreateVertexControl(vc); - SetX(ctrl, 0); SetY(ctrl, 0, true); + { + /* Width = DesignSize.Width; + Height = DesignSize.Height; + var vc = new VertexDataExample(1, "Vertex 1"); + var ctrl = ControlFactory.CreateVertexControl(vc); + SetX(ctrl, 0); SetY(ctrl, 0, true); - var vc2 = new VertexDataExample(2, "Vertex 2"); - var ctrl2 = ControlFactory.CreateVertexControl(vc2); - SetX(ctrl2, 200); SetY(ctrl2, 0, true); + var vc2 = new VertexDataExample(2, "Vertex 2"); + var ctrl2 = ControlFactory.CreateVertexControl(vc2); + SetX(ctrl2, 200); SetY(ctrl2, 0, true); - var vc3 = new VertexDataExample(1, "Vertex 3"); - var ctrl3 = ControlFactory.CreateVertexControl(vc3); - SetX(ctrl3, 100); SetY(ctrl3, 100, true); + var vc3 = new VertexDataExample(1, "Vertex 3"); + var ctrl3 = ControlFactory.CreateVertexControl(vc3); + SetX(ctrl3, 100); SetY(ctrl3, 100, true); - UpdateLayout(); - var edge = new EdgeDataExample(vc, vc2, 1) { Text = "One" }; - var edgectrl = ControlFactory.CreateEdgeControl(ctrl, ctrl2, edge); + UpdateLayout(); + var edge = new EdgeDataExample(vc, vc2, 1) { Text = "One" }; + var edgectrl = ControlFactory.CreateEdgeControl(ctrl, ctrl2, edge); - base.Children.Add(edgectrl); + base.Children.Add(edgectrl); - edge = new EdgeDataExample(vc2, vc3, 1) { Text = "Two" }; - edgectrl = ControlFactory.CreateEdgeControl(ctrl2, ctrl3, edge); - base.Children.Add(edgectrl); + edge = new EdgeDataExample(vc2, vc3, 1) { Text = "Two" }; + edgectrl = ControlFactory.CreateEdgeControl(ctrl2, ctrl3, edge); + base.Children.Add(edgectrl); - edge = new EdgeDataExample(vc3, vc, 1) { Text = "Three" }; - edgectrl = ControlFactory.CreateEdgeControl(ctrl3, ctrl, edge); - base.Children.Add(edgectrl); + edge = new EdgeDataExample(vc3, vc, 1) { Text = "Three" }; + edgectrl = ControlFactory.CreateEdgeControl(ctrl3, ctrl, edge); + base.Children.Add(edgectrl); - base.Children.Add(ctrl); - base.Children.Add(ctrl2); - base.Children.Add(ctrl3);*/ + base.Children.Add(ctrl); + base.Children.Add(ctrl2); + base.Children.Add(ctrl3);*/ } #endregion } @@ -309,7 +311,7 @@ public virtual VertexControl GetVertexControlAt(Point position) return rect.Contains(position.ToGraphX()); }); } - + /// /// Returns all existing VertexControls added into the layout as new Array @@ -395,12 +397,12 @@ private void RemoveVertexInternal(VertexControl ctrl, bool removeVertexFromDataG { if (ctrl.VertexLabelControl != null) { - Children.Remove((UIElement) ctrl.VertexLabelControl); + Children.Remove((UIElement)ctrl.VertexLabelControl); ctrl.DetachLabel(); } Children.Remove(ctrl); if (removeVertexFromDataGraph && LogicCore != null && LogicCore.Graph != null && LogicCore.Graph.ContainsVertex(ctrl.Vertex as TVertex)) - LogicCore.Graph.RemoveVertex(ctrl.Vertex as TVertex); + LogicCore.Graph.RemoveVertex(ctrl.Vertex as TVertex); ctrl.Clean(); } @@ -432,7 +434,7 @@ private void RemoveEdgeInternal(EdgeControl ctrl, bool removeEdgeFromDataGraph = { if (ctrl.EdgeLabelControl != null) { - Children.Remove((UIElement) ctrl.EdgeLabelControl); + Children.Remove((UIElement)ctrl.EdgeLabelControl); ctrl.DetachLabel(); } Children.Remove(ctrl); @@ -498,7 +500,7 @@ public void AddVertex(TVertex vertexData, VertexControl vertexControl, bool gene /// Generate vertex label for this control using VertexLabelFactory public void AddVertexAndData(TVertex vertexData, VertexControl vertexControl, bool generateLabel = false) { - if(LogicCore == null || LogicCore.Graph == null) + if (LogicCore == null || LogicCore.Graph == null) throw new GX_InvalidDataException("LogicCore or its graph hasn't been assigned. Can't add data vertex!"); LogicCore.Graph.AddVertex(vertexData); AddVertex(vertexData, vertexControl, generateLabel); @@ -614,7 +616,7 @@ protected void InternalInsertEdge(TEdge edgeData, EdgeControl edgeControl, int n #region Automatic data ID storage and resolving private int _dataIdCounter = 1; private int _edgeDataIdCounter = 1; - + protected virtual int GetNextUniqueId(bool isVertex) { if (isVertex) @@ -636,7 +638,7 @@ protected virtual int GetNextUniqueId(bool isVertex) protected readonly HashSet DataIdsCollection = new HashSet(); protected readonly HashSet EdgeDataIdsCollection = new HashSet(); - + #endregion @@ -647,7 +649,7 @@ protected virtual int GetNextUniqueId(bool isVertex) protected virtual void GenerateVertexLabels() { if (VertexLabelFactory == null) return; - base.Children.OfType().Cast().ToList().ForEach(a => base.Children.Remove(a)); + base.Children.OfType().Cast().ToList().ForEach(a => base.Children.Remove(a)); VertexList.ForEach(a => { GenerateVertexLabel(a.Value); @@ -695,7 +697,7 @@ protected virtual void GenerateEdgeLabel(EdgeControl edgeControl) /// Get vertex control sizes /// public Dictionary GetVertexSizes() - { + { //measure if needed and get all vertex sizes Measure(new USize(double.PositiveInfinity, double.PositiveInfinity)); var vertexSizes = new Dictionary(_vertexlist.Count(a => ((IGraphXVertex)a.Value.Vertex).SkipProcessing != ProcessingOptionEnum.Exclude)); @@ -712,7 +714,7 @@ public Dictionary GetVertexSizesAndPositions(out IDictionary ((IGraphXVertex) a.Value.Vertex).SkipProcessing != ProcessingOptionEnum.Exclude); + var count = _vertexlist.Count(a => ((IGraphXVertex)a.Value.Vertex).SkipProcessing != ProcessingOptionEnum.Exclude); var vertexSizes = new Dictionary(count); vertexPositions = new Dictionary(count); //go through the vertex presenters and get the actual layoutpositions @@ -751,12 +753,12 @@ public virtual void PreloadGraph(Dictionary positions = null, bo if (LogicCore.Graph == null) throw new GX_InvalidDataException("LogicCore.Graph -> Not initialized!"); - if(autoresolveIds) + if (autoresolveIds) AutoresolveIds(false); PreloadVertexes(); - if(positions != null) + if (positions != null) { foreach (var item in positions) { @@ -809,7 +811,7 @@ public virtual void PreloadVertexes(TGraph graph = null, bool dataContextToDataI #region RelayoutGraph() private Task _layoutTask; - private CancellationTokenSource _layoutCancellationSource; + private CancellationTokenSource _layoutCancellationSource; #if WPF @@ -821,23 +823,23 @@ protected virtual Task _relayoutGraph(CancellationToken cancellationToken, bool return Task.Run(async () => { #endif - Dictionary vertexSizes = null; - IDictionary vertexPositions = null; - IGXLogicCore localLogicCore = null; + Dictionary vertexSizes = null; + IDictionary vertexPositions = null; + IGXLogicCore localLogicCore = null; #if WPF - RunOnDispatcherThread(() => + RunOnDispatcherThread(() => #elif METRO await DispatcherHelper.CheckBeginInvokeOnUi(() => #endif - { - if (LogicCore == null) return; + { + if (LogicCore == null) return; - UpdateLayout(); //update layout so we can get actual control sizes + UpdateLayout(); //update layout so we can get actual control sizes - if (LogicCore.AreVertexSizesNeeded()) - vertexSizes = GetVertexSizesAndPositions(out vertexPositions); - else vertexPositions = GetVertexPositions(); + if (LogicCore.AreVertexSizesNeeded()) + vertexSizes = GetVertexSizesAndPositions(out vertexPositions); + else vertexPositions = GetVertexPositions(); #if METRO //TODO may be wrong. Fix for vertexControl pos not NaN by default as in WPF @@ -846,60 +848,60 @@ await DispatcherHelper.CheckBeginInvokeOnUi(() => vertexPositions.Clear(); #endif - localLogicCore = LogicCore; - }); + localLogicCore = LogicCore; + }); - if (localLogicCore == null) - throw new GX_InvalidDataException("LogicCore -> Not initialized!"); + if (localLogicCore == null) + throw new GX_InvalidDataException("LogicCore -> Not initialized!"); - if (!localLogicCore.GenerateAlgorithmStorage(vertexSizes, vertexPositions)) - return; + if (!localLogicCore.GenerateAlgorithmStorage(vertexSizes, vertexPositions)) + return; - //clear routing info - localLogicCore.Graph.Edges.ForEach(a => a.RoutingPoints = null); + //clear routing info + localLogicCore.Graph.Edges.ForEach(a => a.RoutingPoints = null); - var resultCoords = localLogicCore.Compute(cancellationToken); + var resultCoords = localLogicCore.Compute(cancellationToken); #if WPF - RunOnDispatcherThread(() => + RunOnDispatcherThread(() => #elif METRO await DispatcherHelper.CheckBeginInvokeOnUi(() => #endif + { + if (MoveAnimation != null) { - if (MoveAnimation != null) - { - MoveAnimation.CleanupBaseData(); - MoveAnimation.Cleanup(); - } - //setup vertex positions from result data - foreach (var item in resultCoords) - { - if (!_vertexlist.ContainsKey(item.Key)) continue; - var vc = _vertexlist[item.Key]; + MoveAnimation.CleanupBaseData(); + MoveAnimation.Cleanup(); + } + //setup vertex positions from result data + foreach (var item in resultCoords) + { + if (!_vertexlist.ContainsKey(item.Key)) continue; + var vc = _vertexlist[item.Key]; - SetFinalX(vc, item.Value.X); - SetFinalY(vc, item.Value.Y); + SetFinalX(vc, item.Value.X); + SetFinalY(vc, item.Value.Y); - if (MoveAnimation == null || double.IsNaN(GetX(vc))) - vc.SetPosition(item.Value.X, item.Value.Y, false); - else MoveAnimation.AddVertexData(vc, item.Value); - vc.SetCurrentValue(GraphAreaBase.PositioningCompleteProperty, true); // Style can show vertexes with layout positions assigned - } - if (MoveAnimation != null) - { - if (MoveAnimation.VertexStorage.Count > 0) - MoveAnimation.RunVertexAnimation(); + if (MoveAnimation == null || double.IsNaN(GetX(vc))) + vc.SetPosition(item.Value.X, item.Value.Y, false); + else MoveAnimation.AddVertexData(vc, item.Value); + vc.SetCurrentValue(GraphAreaBase.PositioningCompleteProperty, true); // Style can show vertexes with layout positions assigned + } + if (MoveAnimation != null) + { + if (MoveAnimation.VertexStorage.Count > 0) + MoveAnimation.RunVertexAnimation(); - foreach (var item in _edgeslist.Values) - MoveAnimation.AddEdgeData(item); - if (MoveAnimation.EdgeStorage.Count > 0) - MoveAnimation.RunEdgeAnimation(); - } + foreach (var item in _edgeslist.Values) + MoveAnimation.AddEdgeData(item); + if (MoveAnimation.EdgeStorage.Count > 0) + MoveAnimation.RunEdgeAnimation(); + } #if WPF - SetCurrentValue(LogicCoreProperty, localLogicCore); - UpdateLayout(); //update all changes - }); + SetCurrentValue(LogicCoreProperty, localLogicCore); + UpdateLayout(); //update all changes + }); #elif METRO MeasureOverride(new Windows.Foundation.Size(double.PositiveInfinity, double.PositiveInfinity)); @@ -955,7 +957,7 @@ protected virtual void _relayoutGraphMain(bool generateAllEdges = false, bool st { _relayoutGraph(CancellationToken.None); _finishUpRelayoutGraph(generateAllEdges, standalone); - } + } } protected virtual void _finishUpRelayoutGraph(bool generateAllEdges, bool standalone) @@ -1086,7 +1088,7 @@ private void Await(Task task) /// /// Cancel all undergoing async calculations /// -#if WPF +#if WPF public void CancelRelayout() #elif METRO public async Task CancelRelayoutAsync() @@ -1170,7 +1172,7 @@ public virtual void GenerateGraph(TGraph graph, bool generateAllEdges = true, bo { if (AutoAssignMissingDataId) AutoresolveIds(false, graph); - if(!LogicCore.IsCustomLayout) + if (!LogicCore.IsCustomLayout) PreloadVertexes(graph, dataContextToDataItem); _relayoutGraphMain(generateAllEdges, false); } @@ -1205,17 +1207,17 @@ protected virtual void AutoresolveIds(bool includeEdgeIds, TGraph graph = null) DataIdsCollection.Clear(); _dataIdCounter = 1; - // First, rebuild data ID collection for all vertices and edges that already have assigned IDs. - foreach (var item in graph.Vertices.Where(a => a.ID != -1)) - { - bool added = DataIdsCollection.Add(item.ID); - Debug.Assert(added, string.Format("Duplicate ID '{0}' found while adding a vertex ID during rebuild of data ID collection.", item.ID)); + // First, rebuild data ID collection for all vertices and edges that already have assigned IDs. + foreach (var item in graph.Vertices.Where(a => a.ID != -1)) + { + bool added = DataIdsCollection.Add(item.ID); + Debug.Assert(added, string.Format("Duplicate ID '{0}' found while adding a vertex ID during rebuild of data ID collection.", item.ID)); } - // Generate unique IDs for all vertices and edges that don't already have a unique ID. - foreach (var item in graph.Vertices.Where(a => a.ID == -1)) - item.ID = GetNextUniqueId(true); - if(includeEdgeIds) + // Generate unique IDs for all vertices and edges that don't already have a unique ID. + foreach (var item in graph.Vertices.Where(a => a.ID == -1)) + item.ID = GetNextUniqueId(true); + if (includeEdgeIds) AutoresolveEdgeIds(graph); } @@ -1237,7 +1239,7 @@ protected virtual void AutoresolveEdgeIds(TGraph graph = null) item.ID = GetNextUniqueId(false); } - #endregion + #endregion #region Methods for EDGE and VERTEX properties set @@ -1491,7 +1493,7 @@ protected virtual void GenerateAllEdgesInternal(Visibility defaultVisibility = V /// Ensures that layout is properly updated before edges calculation. If you are sure that it is already updated you can set this param to False to increase performance. public virtual void GenerateAllEdges(Visibility defaultVisibility = Visibility.Visible, bool updateLayout = true) { - if(updateLayout) UpdateLayout(); + if (updateLayout) UpdateLayout(); GenerateAllEdgesInternal(defaultVisibility); } @@ -1509,9 +1511,9 @@ public virtual void UpdateParallelEdgesData() foreach (var item in EdgesList) { if (usedIds.Contains(item.Key.ID) || !item.Value.CanBeParallel) continue; - var list = new List {item.Value}; + var list = new List { item.Value }; //that list will contain checks for edges that goes form target to source - var cList = new List {false}; + var cList = new List { false }; foreach (var edge in EdgesList) { //skip the same edge @@ -1607,22 +1609,22 @@ public override void GenerateEdgesForVertex(VertexControl vc, EdgesType edgeType if (inlist != null) foreach (var item in inlist) { - if(gotSelfLoop) continue; + if (gotSelfLoop) continue; var ctrl = ControlFactory.CreateEdgeControl(_vertexlist[item.Source], vc, item, _svShowEdgeLabels ?? false, _svShowEdgeArrows ?? true, - defaultVisibility); + defaultVisibility); InsertEdge(item, ctrl); ctrl.PrepareEdgePath(); - if(item.Source == item.Target) gotSelfLoop = true; + if (item.Source == item.Target) gotSelfLoop = true; } if (outlist != null) foreach (var item in outlist) { - if(gotSelfLoop) continue; + if (gotSelfLoop) continue; var ctrl = ControlFactory.CreateEdgeControl(vc, _vertexlist[item.Target], item, _svShowEdgeLabels ?? false, _svShowEdgeArrows ?? true, defaultVisibility); InsertEdge(item, ctrl); ctrl.PrepareEdgePath(); - if(item.Source == item.Target) gotSelfLoop = true; + if (item.Source == item.Target) gotSelfLoop = true; } } #endregion @@ -1635,7 +1637,7 @@ public virtual void UpdateAllEdges(bool performFullUpdate = false) { if (LogicCore == null) throw new GX_InvalidDataException("LogicCore -> Not initialized!"); - + if (LogicCore.EnableParallelEdges) UpdateParallelEdgesData(); @@ -1646,11 +1648,88 @@ public virtual void UpdateAllEdges(bool performFullUpdate = false) } } - + #endregion #region GetRelatedControls + + public override List GetRelatedVertexControls(IGraphControl ctrl, EdgesType edgesType = EdgesType.All) + { + if (ctrl == null) + throw new GX_InvalidDataException("Supplied ctrl value is null!"); + if (LogicCore == null) + throw new GX_InvalidDataException("LogicCore -> Not initialized!"); + if (LogicCore.Graph == null) + { + Debug.WriteLine("LogicCore.Graph property not set while using GetRelatedVertexControls method!"); + return null; + } + + var list = new List(); + var vc = ctrl as VertexControl; + if (vc != null) + { + var vData = vc.Vertex as TVertex; + var vList = new List(); + switch (edgesType) + { + case EdgesType.All: + vList = LogicCore.Graph.GetNeighbours(vData).ToList(); + break; + case EdgesType.In: + vList = LogicCore.Graph.GetInNeighbours(vData).ToList(); + break; + case EdgesType.Out: + vList = LogicCore.Graph.GetOutNeighbours(vData).ToList(); + break; + } + list.AddRange(VertexList.Where(a => vList.Contains(a.Key)).Select(a => a.Value)); + } + var ec = ctrl as EdgeControl; + if (ec == null) return list; + var edge = (TEdge)ec.Edge; + if (_vertexlist.ContainsKey(edge.Target)) list.Add(_vertexlist[edge.Target]); + if (_vertexlist.ContainsKey(edge.Source)) list.Add(_vertexlist[edge.Source]); + return list; + } + + public override List GetRelatedEdgeControls(IGraphControl ctrl, EdgesType edgesType = EdgesType.All) + { + if (ctrl == null) + throw new GX_InvalidDataException("Supplied ctrl value is null!"); + if (LogicCore == null) + throw new GX_InvalidDataException("LogicCore -> Not initialized!"); + if (LogicCore.Graph == null) + { + Debug.WriteLine("LogicCore.Graph property not set while using GetRelatedEdgeControls method!"); + return null; + } + + var list = new List(); + if (ctrl is EdgeControl) return list; + var vc = ctrl as VertexControl; + if (vc != null) + { + var vData = vc.Vertex as TVertex; + var eList = new List(); + switch (edgesType) + { + case EdgesType.All: + eList = LogicCore.Graph.GetAllEdges(vData).ToList(); + break; + case EdgesType.In: + eList = LogicCore.Graph.GetInEdges(vData).ToList(); + break; + case EdgesType.Out: + eList = LogicCore.Graph.GetOutEdges(vData).ToList(); + break; + } + list.AddRange(EdgesList.Where(a => eList.Contains(a.Key)).Select(a => a.Value)); + } + return list; + } + /// /// Get controls related to specified control /// @@ -1659,9 +1738,11 @@ public virtual void UpdateAllEdges(bool performFullUpdate = false) /// Optional edge controls type public override List GetRelatedControls(IGraphControl ctrl, GraphControlType resultType = GraphControlType.VertexAndEdge, EdgesType edgesType = EdgesType.Out) { + if (ctrl == null) + throw new GX_InvalidDataException("Supplied ctrl value is null!"); if (LogicCore == null) throw new GX_InvalidDataException("LogicCore -> Not initialized!"); - if(LogicCore.Graph == null) + if (LogicCore.Graph == null) { Debug.WriteLine("LogicCore.Graph property not set while using GetRelatedControls method!"); return null; @@ -1678,7 +1759,7 @@ public override List GetRelatedControls(IGraphControl ctrl, Graph { IEnumerable inEdges; LogicCore.Graph.TryGetInEdges(vc.Vertex as TVertex, out inEdges); - edgesInList = inEdges == null? null : inEdges.ToList(); + edgesInList = inEdges == null ? null : inEdges.ToList(); } if (edgesType == EdgesType.Out || edgesType == EdgesType.All) @@ -1748,7 +1829,7 @@ public virtual List ExtractSerializationData() /// The serialization data /// Occurs when LogicCore isn't set /// Occurs when edge source or target isn't set - public virtual void RebuildFromSerializationData(IEnumerable data) + public virtual void RebuildFromSerializationData(IEnumerable data) { if (LogicCore == null) throw new GX_InvalidDataException("LogicCore -> Not initialized!"); @@ -1767,8 +1848,8 @@ public virtual void RebuildFromSerializationData(IEnumerable a.Data is TEdge); foreach (var item in elist) @@ -1787,11 +1868,11 @@ public virtual void RebuildFromSerializationData(IEnumerable vPositions; - var vSizes = GetVertexSizesAndPositions(out vPositions); + var vSizes = GetVertexSizesAndPositions(out vPositions); LogicCore.GenerateAlgorithmStorage(vSizes, vPositions); } @@ -1839,7 +1920,7 @@ public virtual void ExportAsJpeg(int quality = 100) /// Optional image DPI parameter /// Use zoom control parent surface to render bitmap (only visible zoom content will be exported) /// Optional image quality parameter (for JPEG) - public virtual void ExportAsImage(ImageType itype, bool useZoomControlSurface = true, double dpi = PrintHelper.DEFAULT_DPI, int quality = 100) + public virtual void ExportAsImage(ImageType itype, bool useZoomControlSurface = false, double dpi = PrintHelper.DEFAULT_DPI, int quality = 100) { #if WPF string fileExt; @@ -1862,7 +1943,7 @@ public virtual void ExportAsImage(ImageType itype, bool useZoomControlSurface = var dlg = new SaveFileDialog { Filter = String.Format("{0} Image File ({1})|{1}", fileType, fileExt), Title = String.Format("Exporting graph as {0} image...", fileType) }; if (dlg.ShowDialog() == true) { - PrintHelper.ExportToImage(this, new Uri(dlg.FileName), itype, true, dpi, quality); + PrintHelper.ExportToImage(this, new Uri(dlg.FileName), itype, useZoomControlSurface, dpi, quality); } #endif } @@ -1870,16 +1951,81 @@ public virtual void ExportAsImage(ImageType itype, bool useZoomControlSurface = #if WPF public Bitmap ExportToBitmap(double dpi = PrintHelper.DEFAULT_DPI) { - return PrintHelper.RenderTargetBitmapToBitmap(PrintHelper.RenderTargetBitmap(this, true, dpi)); + return PrintHelper.RenderTargetBitmapToBitmap(PrintHelper.RenderTargetBitmap(this, true, dpi)); + } + + private USize _oldSizeExpansion; + + /// + /// Print whole visual graph + /// + /// Fit to configured print page + /// Optional header description + /// Optional side margin + public virtual void PrintDialog(bool fitPage, string description = "", int margin = 0) + { + PrintHelper.PrintExtended(this, description, margin, fitPage); } #endif + /// + /// 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. + /// + /// True or False + /// Offset child controls to fit into GraphArea control size + /// Optional print area margin around the graph + public override void SetPrintMode(bool value, bool offsetControls = true, int margin = 0) + { +#if WPF + if (IsInPrintMode == value) return; + IsInPrintMode = value; + + if (IsInPrintMode) + { + //set parent background + if (Parent is ZoomControl) + Background = ((ZoomControl)Parent).Background; + //set margin + _oldSizeExpansion = SideExpansionSize; + SideExpansionSize = margin == 0 ? new USize(0, 0) : new USize(margin, margin); + } + else + { + //reset margin + SideExpansionSize = _oldSizeExpansion; + //clear background + if (Parent is ZoomControl) + Background = Brushes.Transparent; + } + if (offsetControls) + { + //var offset = ContentSize.TopLeft; + var offset = new Point(ContentSize.TopLeft.X - (margin == 0 ? 0 : margin * .5), ContentSize.TopLeft.Y - (margin == 0 ? 0 : margin * .5)); + + foreach (UIElement child in Children) + { + //skip edge controls + if (child is EdgeControl) continue; + //get current position + var pos = new Point(GetX(child), GetY(child)); + //skip children with unset coordinates + if (double.IsNaN(pos.X) || double.IsInfinity(pos.X)) continue; + //adjust coordinates + SetX(child, pos.X - (IsInPrintMode ? offset.X : -offset.X)); + SetY(child, pos.Y - (IsInPrintMode ? offset.Y : -offset.Y), true); + } + } + InvalidateMeasure(); + UpdateLayout(); +#endif + } /// - /// Print current visual graph layout + /// Print current visual graph layout as visible in ZoomControl (if wrapped in) /// /// Optional header description - public virtual void PrintDialog(string description = "", double zoomModifier = 1d) + public virtual void PrintVisibleAreaDialog(string description = "") { #if WPF UIElement vis = this; @@ -1891,22 +2037,9 @@ public virtual void PrintDialog(string description = "", double zoomModifier = 1 { var frameworkElement = Parent as FrameworkElement; if (frameworkElement != null && frameworkElement.Parent is IZoomControl) - vis = ((IZoomControl) frameworkElement.Parent).PresenterVisual; + vis = ((IZoomControl)frameworkElement.Parent).PresenterVisual; } - - /* if (vis != this && zoomModifier != 1d) - { - var p = zoomControl; - var oW = p.Width; - var oH = p.Height; - var oZoom = p.Zoom; - p.Width = p.ActualWidth*zoomModifier; - p.Height = p.ActualHeight*zoomModifier; - PrintHelper.ShowPrintPreview(vis, description); - p.Width = oW; - p.Height = oH; - }else*/ - PrintHelper.ShowPrintPreview(vis, description); + PrintHelper.ShowPrintPreview(vis, description); #endif } @@ -1937,7 +2070,7 @@ public void Dispose() /// /// Runs when base dispose is done /// - protected virtual void OnDispose() {} + protected virtual void OnDispose() { } /// /// Clear graph visual layout (all edges, vertices and their states storage if any) and (optionally) LogicCore @@ -1949,14 +2082,14 @@ public virtual void ClearLayout(bool removeCustomObjects = true, bool clearState { RemoveAllEdges(); RemoveAllVertices(); - if(removeCustomObjects) + if (removeCustomObjects) base.Children.Clear(); StateStorage = new StateStorage(this); if (clearLogicCore && LogicCore != null) LogicCore.Clear(); - if(clearLogicCore || clearStates) + if (clearLogicCore || clearStates) if (StateStorage != null) StateStorage.Dispose(); } diff --git a/GraphX.Controls/Controls/GraphAreaBase.cs b/GraphX.Controls/Controls/GraphAreaBase.cs index 60525b4b..a5c50d5b 100644 --- a/GraphX.Controls/Controls/GraphAreaBase.cs +++ b/GraphX.Controls/Controls/GraphAreaBase.cs @@ -24,6 +24,13 @@ namespace GraphX { public abstract class GraphAreaBase : Canvas, ITrackableContent { + /// + /// Gets or Sets if GraphArea is in print mode when its size is recalculated on each Measure pass + /// + protected bool IsInPrintMode; + + public abstract void SetPrintMode(bool value, bool offsetControls = true, int margin = 0); + /// /// Automaticaly assign unique Id (if missing) for vertex and edge data classes provided as Graph in GenerateGraph() method or by Addvertex() or AddEdge() methods /// @@ -519,6 +526,19 @@ internal virtual void ComputeEdgeRoutesByVertex(VertexControl vc, bool vertexDat /// Type of resulting related controls /// Optional edge controls type public abstract List GetRelatedControls(IGraphControl ctrl, GraphControlType resultType = GraphControlType.VertexAndEdge, EdgesType edgesType = EdgesType.Out); + /// + /// Get vertex controls related to specified control + /// + /// Original control + /// Edge types to query for vertices + public abstract List GetRelatedVertexControls(IGraphControl ctrl, EdgesType edgesType = EdgesType.All); + /// + /// Get edge controls related to specified control + /// + /// Original control + /// Edge types to query + public abstract List GetRelatedEdgeControls(IGraphControl ctrl, EdgesType edgesType = EdgesType.All); + /// /// Generates and displays edges for specified vertex @@ -625,7 +645,7 @@ protected override Size ArrangeOverride(Size arrangeSize) } #if WPF - return DesignerProperties.GetIsInDesignMode(this) ? DesignSize : new Size(10, 10); + return DesignerProperties.GetIsInDesignMode(this) ? DesignSize : (IsInPrintMode ? ContentSize.Size : new Size(10, 10)); #elif METRO return DesignMode.DesignModeEnabled ? DesignSize : new Size(10, 10); #endif @@ -698,7 +718,7 @@ protected override Size MeasureOverride(Size constraint) if (oldSize != newSize) OnContentSizeChanged(oldSize, newSize); #if WPF - return DesignerProperties.GetIsInDesignMode(this) ? DesignSize : new Size(10, 10); + return DesignerProperties.GetIsInDesignMode(this) ? DesignSize : (IsInPrintMode ? ContentSize.Size : new Size(10, 10)); #elif METRO return DesignMode.DesignModeEnabled ? DesignSize : new Size(10, 10); #endif diff --git a/GraphX.Controls/Controls/ZoomControl/ZoomControl.cs b/GraphX.Controls/Controls/ZoomControl/ZoomControl.cs index 5f0c5e68..e0715a2b 100644 --- a/GraphX.Controls/Controls/ZoomControl/ZoomControl.cs +++ b/GraphX.Controls/Controls/ZoomControl/ZoomControl.cs @@ -198,7 +198,7 @@ private void AttachToVisualTree() { // create VisualBrush for the view finder display panel CreateVisualBrushForViewFinder(Content as Visual); - _viewFinderDisplay.Background = this.Background; + //rem due to fail in template bg binding //_viewFinderDisplay.Background = this.Background; // hook up event handlers for dragging and resizing the viewport _viewFinderDisplay.MouseMove += ViewFinderDisplayMouseMove; diff --git a/GraphX.Controls/GraphX.WPF.Controls.csproj b/GraphX.Controls/GraphX.WPF.Controls.csproj index 1cf7747b..ef2321cc 100644 --- a/GraphX.Controls/GraphX.WPF.Controls.csproj +++ b/GraphX.Controls/GraphX.WPF.Controls.csproj @@ -82,9 +82,11 @@ ..\packages\QuickGraphPCL.3.6.61114.2\lib\net40-client\QuickGraph.Serialization.dll True + + diff --git a/GraphX.Controls/Models/GraphControlFactory.cs b/GraphX.Controls/Models/GraphControlFactory.cs index efbb5718..0797faca 100644 --- a/GraphX.Controls/Models/GraphControlFactory.cs +++ b/GraphX.Controls/Models/GraphControlFactory.cs @@ -16,7 +16,7 @@ public GraphControlFactory(GraphAreaBase graphArea) FactoryRootArea = graphArea; } - public 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 showLabels = false, bool showArrows = true, Visibility visibility = Visibility.Visible) { var edgectrl = new EdgeControl(source, target, edge, showLabels, showArrows) { RootArea = FactoryRootArea}; edgectrl.SetCurrentValue(UIElement.VisibilityProperty, visibility); @@ -24,7 +24,7 @@ public EdgeControl CreateEdgeControl(VertexControl source, VertexControl target, } - public VertexControl CreateVertexControl(object vertexData) + public virtual VertexControl CreateVertexControl(object vertexData) { return new VertexControl(vertexData) {RootArea = FactoryRootArea}; } diff --git a/GraphX.Controls/PrintHelper.cs b/GraphX.Controls/PrintHelper.cs index 9993b48a..70ae7e5f 100644 --- a/GraphX.Controls/PrintHelper.cs +++ b/GraphX.Controls/PrintHelper.cs @@ -32,6 +32,8 @@ internal static class PrintHelper /// public static void ExportToImage(GraphAreaBase surface, Uri path, ImageType itype, bool useZoomControlSurface = false, double imgdpi = DEFAULT_DPI, int imgQuality = 100) { + if(!useZoomControlSurface) + surface.SetPrintMode(true, true, 100); //Create a render bitmap and push the surface to it UIElement vis = surface; if (useZoomControlSurface) @@ -48,8 +50,6 @@ public static void ExportToImage(GraphAreaBase surface, Uri path, ImageType ityp } var renderBitmap = new RenderTargetBitmap( - //(int)surface.ActualWidth, - //(int)surface.ActualHeight, (int)(vis.DesiredSize.Width * (imgdpi / DEFAULT_DPI) + 100), (int)(vis.DesiredSize.Height * (imgdpi / DEFAULT_DPI) + 100), imgdpi, @@ -85,6 +85,13 @@ public static void ExportToImage(GraphAreaBase surface, Uri path, ImageType ityp //Save the data to the stream encoder.Save(outStream); } + //due to mem leak in wpf :( + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + + if (!useZoomControlSurface) + surface.SetPrintMode(false, true, 100); } @@ -104,6 +111,93 @@ public static void ShowPrintPreview(Visual surface, string description = "") } } + + public static void PrintExtended(GraphAreaBase visual, string description, int margin = 0, bool fitPage = false) + { + var pd = new PrintDialog(); + if (pd.ShowDialog() == true) + { + visual.SetPrintMode(true, true, margin); + + //store original scale + var originalScale = visual.LayoutTransform; + //get selected printer capabilities + var capabilities = pd.PrintQueue.GetPrintCapabilities(pd.PrintTicket); + + //get scale of the print wrt to screen of WPF visual + var scale = Math.Min(capabilities.PageImageableArea.ExtentWidth / (visual.ActualWidth + margin), capabilities.PageImageableArea.ExtentHeight / + (visual.ActualHeight + margin)); + + //Transform the Visual to scale + var group = new TransformGroup(); + if (fitPage) + group.Children.Add(new ScaleTransform(scale, scale)); + visual.LayoutTransform = group; + + if (fitPage) + { + //get the size of the printer page + var sz = new System.Windows.Size(capabilities.PageImageableArea.ExtentWidth, capabilities.PageImageableArea.ExtentHeight); + //update the layout of the visual to the printer page size. + visual.Measure(sz); + visual.Arrange(new Rect(new System.Windows.Point(capabilities.PageImageableArea.OriginWidth, capabilities.PageImageableArea.OriginHeight), sz)); + } + //now print the visual to printer to fit on the one page. + pd.PrintVisual(visual, description); + + //apply the original transform. + visual.LayoutTransform = originalScale; + visual.SetPrintMode(false, true, margin); + } + } + + /* /// + /// experimental method, not working + /// + /// + public void PrintExp(PocGraphLayout controlToPrint) + { + var fixedDoc = new FixedDocument(); + var pageContent = new PageContent(); + var fixedPage = new FixedPage(); + + zoomControl.Content = null; + + //Create first page of document + fixedPage.Children.Add(controlToPrint); + FixedPage.SetLeft(controlToPrint, 0); + FixedPage.SetTop(controlToPrint, 0); + controlToPrint.InvalidateMeasure(); + controlToPrint.UpdateLayout(); + fixedPage.UpdateLayout(); + + pageContent.Child = fixedPage; + fixedDoc.Pages.Add(pageContent); + var dlg = new SaveFileDialog + { + FileName = "print", + DefaultExt = ".xps", + Filter = "XPS Documents (.xps)|*.xps" + }; + // Show save file dialog box + bool? result = dlg.ShowDialog(); + + // Process save file dialog box results + if (result == true) + { + // Save document + string filename = dlg.FileName; + + var xpsd = new XpsDocument(filename, FileAccess.ReadWrite); + var xw = XpsDocument.CreateXpsDocumentWriter(xpsd); + xw.Write(fixedDoc); + xpsd.Close(); + } + fixedPage.Children.Clear(); + zoomControl.Content = graphLayout; + } + */ + public static Bitmap RenderTargetBitmapToBitmap(RenderTargetBitmap source) { using (MemoryStream outStream = new MemoryStream()) diff --git a/GraphX.Controls/Themes/Generic.xaml b/GraphX.Controls/Themes/Generic.xaml index fc623df4..c63ca580 100644 --- a/GraphX.Controls/Themes/Generic.xaml +++ b/GraphX.Controls/Themes/Generic.xaml @@ -328,7 +328,8 @@ - + + /// Get all edges associated with the vertex + /// + /// Vertex data type + /// Edge data type + /// Graph + /// Vertex + public static IEnumerable GetAllEdges(this IBidirectionalGraph graph, TVertex vertex) + where TEdge : IEdge + { + var result = new List(); + IEnumerable edges; + graph.TryGetOutEdges(vertex, out edges); + if (edges != null) + result.AddRange(edges); + graph.TryGetInEdges(vertex, out edges); + if (edges != null) + result.AddRange(edges); + return result; + } + + public static IEnumerable GetInEdges(this IBidirectionalGraph graph, TVertex vertex) + where TEdge : IEdge + { + var result = new List(); + IEnumerable edges; + graph.TryGetInEdges(vertex, out edges); + if (edges != null) + result.AddRange(edges); + return result; + } + + public static IEnumerable GetOutEdges(this IBidirectionalGraph graph, TVertex vertex) +where TEdge : IEdge + { + var result = new List(); + IEnumerable edges; + graph.TryGetOutEdges(vertex, out edges); + if (edges != null) + result.AddRange(edges); + return result; + } + /// /// Returns with the adjacent vertices of the vertex. /// @@ -18,9 +61,9 @@ public static class GraphXExtensions public static IEnumerable GetNeighbours(this IBidirectionalGraph g, TVertex vertex) where TEdge : IEdge { - return ((from e in g.InEdges(vertex) select e.Source) - .Concat( - (from e in g.OutEdges(vertex) select e.Target))).Distinct(); + return (from e in g.InEdges(vertex) select e.Source) + .Concat(from e in g.OutEdges(vertex) select e.Target) + .Distinct(); } @@ -31,6 +74,13 @@ public static IEnumerable GetOutNeighbours(this IVertex select e.Target).Distinct(); } + public static IEnumerable GetInNeighbours(this IBidirectionalGraph g, TVertex vertex) + where TEdge : IEdge + { + return (from e in g.InEdges(vertex) + select e.Source).Distinct(); + } + /// /// If the graph g is directed, then returns every edges which source is one of the vertices in the set1 diff --git a/GraphX.PCL.Logic/Helpers/TypeExtensions.cs b/GraphX.PCL.Logic/Helpers/TypeExtensions.cs index 0a7e08e7..33ad324a 100644 --- a/GraphX.PCL.Logic/Helpers/TypeExtensions.cs +++ b/GraphX.PCL.Logic/Helpers/TypeExtensions.cs @@ -11,26 +11,5 @@ public static void ForEach(this IEnumerable list, Action func) foreach (var item in list) func(item); } - - /// - /// Get all edges associated with the vertex - /// - /// Vertex data type - /// Edge data type - /// Graph - /// Vertex - public static IEnumerable GetAllEdges(this IBidirectionalGraph graph, TVertex vertex) - where TEdge: IEdge - { - var result = new List(); - IEnumerable edges; - graph.TryGetOutEdges(vertex, out edges); - if(edges != null) - result.AddRange(edges); - graph.TryGetInEdges(vertex, out edges); - if(edges != null) - result.AddRange(edges); - return result; - } } } diff --git a/README.md b/README.md index f2632334..ca8674dc 100644 --- a/README.md +++ b/README.md @@ -27,13 +27,13 @@ Main GraphX libraries can be used in both C# and VB for .NET using WPF, WinForms * Windows Desktop (WPF & WinForms on Windows XP SP3 and later) * Microsoft Silverlight 5 (WIP, only logic core is available) - * Windows Metro 8.1 and later (BETA) + * Windows Metro 8.1 and later * Windows Phone 8 (WIP, only logic core is available) Features: Performance oriented, isolated library design optimized for: - Large amount of graph vertices rendering + Large amount of templated graph vertices rendering Isolated visual and logic libraries design and modular coding approach Constantly improving MVVM support .NET multiplatform support @@ -50,7 +50,7 @@ Main GraphX libraries can be used in both C# and VB for .NET using WPF, WinForms Advanced graph vertex features: Easy vertex drag and highlight support including on-the-fly edge routing updates Filtering feature provides selective vertex rendering leaving supplied graph untouched - Customizable vertex labels support that allows to set text, positionand angle + Customizable vertex labels support that allows to set text, position and angle Support for different vertex math shapes for proper edge connections rendering Support for different vertex and edge animations including the ability to easily create custom animations Easy support for user-defined external layout, overlap removal and edge routing algorithms