Merge pull request #10 from jorgensigvardsson/PCL

Store app async API
This commit is contained in:
Alexander Smirnov 2015-04-14 01:22:41 +03:00
Родитель f960c70910 e2b8e963df
Коммит 34b3831d0c
5 изменённых файлов: 121 добавлений и 73 удалений

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

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
@ -51,15 +52,32 @@ namespace METRO.SimpleGraph
zc.ZoomToFill(); zc.ZoomToFill();
} }
void butGenerate_Click(object sender, RoutedEventArgs e) private async void butGenerate_Click(object sender, RoutedEventArgs e)
{ {
GraphAreaExample_Setup(); GraphAreaExample_Setup();
graph.GenerateGraph(true);
try
{
await graph.GenerateGraphAsync(true);
}
catch (OperationCanceledException)
{
// User may have canceled
}
} }
void butRelayout_Click(object sender, RoutedEventArgs e) async void butRelayout_Click(object sender, RoutedEventArgs e)
{ {
graph.RelayoutGraph(); try
{
var t0 = DateTime.Now;
await graph.RelayoutGraphAsync();
Debug.WriteLine("Time elapsed: {0}", DateTime.Now - t0);
}
catch (OperationCanceledException)
{
// User may have canceled
}
} }
void cboxEdgeRouting_SelectionChanged(object sender, SelectionChangedEventArgs e) void cboxEdgeRouting_SelectionChanged(object sender, SelectionChangedEventArgs e)
@ -80,12 +98,19 @@ namespace METRO.SimpleGraph
graph.LogicCore.DefaultLayoutAlgorithm = (LayoutAlgorithmTypeEnum) cboxLayout.SelectedItem; graph.LogicCore.DefaultLayoutAlgorithm = (LayoutAlgorithmTypeEnum) cboxLayout.SelectedItem;
} }
void MainPage_Loaded(object sender, RoutedEventArgs e) async void MainPage_Loaded(object sender, RoutedEventArgs e)
{ {
InitialSetup(); InitialSetup();
GraphAreaExample_Setup(); GraphAreaExample_Setup();
graph.GenerateGraph(true); try
{
await graph.GenerateGraphAsync(true);
}
catch (OperationCanceledException)
{
// User may have canceled
}
//graph.RelayoutGraph(true); //graph.RelayoutGraph(true);
//zc.ZoomToFill(); //zc.ZoomToFill();

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

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using GraphX; using GraphX;
using GraphX.GraphSharp.Algorithms.EdgeRouting; using GraphX.GraphSharp.Algorithms.EdgeRouting;
using GraphX.Measure; using GraphX.Measure;
@ -18,7 +19,7 @@ namespace InteractiveGraph.Models
_curveOffset = prms != null ? prms.VerticalCurveOffset : 20; _curveOffset = prms != null ? prms.VerticalCurveOffset : 20;
} }
public override void Compute() public override void Compute(CancellationToken cancellationToken)
{ {
EdgeRoutes.Clear(); EdgeRoutes.Clear();
foreach (var edge in _graph.Edges) foreach (var edge in _graph.Edges)

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

@ -831,6 +831,7 @@ namespace GraphX
// Wait, but don't block the dispatcher, because the background task might be trying to execute on the UI thread. // Wait, but don't block the dispatcher, because the background task might be trying to execute on the UI thread.
Await(_layoutTask); Await(_layoutTask);
_layoutCancellationSource.Dispose();
_layoutCancellationSource = null; _layoutCancellationSource = null;
_layoutTask = null; _layoutTask = null;
} }

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

@ -1,3 +1,4 @@
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Windows.Foundation; using Windows.Foundation;
using Windows.UI.Xaml; using Windows.UI.Xaml;
@ -32,7 +33,7 @@ namespace GraphX
public static readonly DependencyProperty LogicCoreProperty = public static readonly DependencyProperty LogicCoreProperty =
DependencyProperty.Register("LogicCore", typeof(IGXLogicCore<TVertex, TEdge, TGraph>), typeof(GraphArea<TVertex, TEdge, TGraph>), new PropertyMetadata(null, logic_core_changed)); DependencyProperty.Register("LogicCore", typeof(IGXLogicCore<TVertex, TEdge, TGraph>), typeof(GraphArea<TVertex, TEdge, TGraph>), new PropertyMetadata(null, logic_core_changed));
private static void logic_core_changed(DependencyObject d, DependencyPropertyChangedEventArgs e) private static async void logic_core_changed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{ {
//automaticaly assign default file service provider //automaticaly assign default file service provider
if(e.NewValue != null && ((IGXLogicCore<TVertex, TEdge, TGraph>)e.NewValue).FileServiceProvider == null) if(e.NewValue != null && ((IGXLogicCore<TVertex, TEdge, TGraph>)e.NewValue).FileServiceProvider == null)
@ -43,16 +44,16 @@ namespace GraphX
switch (graph.LogicCoreChangeAction) switch (graph.LogicCoreChangeAction)
{ {
case LogicCoreChangedAction.GenerateGraph: case LogicCoreChangedAction.GenerateGraph:
graph.GenerateGraph(); await graph.GenerateGraphAsync();
break; break;
case LogicCoreChangedAction.GenerateGraphWithEdges: case LogicCoreChangedAction.GenerateGraphWithEdges:
graph.GenerateGraph(true); await graph.GenerateGraphAsync(true);
break; break;
case LogicCoreChangedAction.RelayoutGraph: case LogicCoreChangedAction.RelayoutGraph:
graph.RelayoutGraph(); await graph.RelayoutGraphAsync();
break; break;
case LogicCoreChangedAction.RelayoutGraphWithEdges: case LogicCoreChangedAction.RelayoutGraphWithEdges:
graph.RelayoutGraph(true); await graph.RelayoutGraphAsync(true);
break; break;
default: default:
break; break;
@ -520,21 +521,9 @@ namespace GraphX
#endregion #endregion
#region RelayoutGraph() #region RelayoutGraph()
private Task _relayoutGraph(CancellationToken cancellationToken, bool generateAllEdges = false, bool standalone = true)
private bool IsCalculating;
private object calcLocker = new object();
private async Task _relayoutGraph(bool generateAllEdges = false, bool standalone = true)
{ {
//lock to GraphArea instance and check if calculation is already running return Task.Run(async () =>
lock (calcLocker)
{
//do not allow new calculation to begin until previous isn't finished
if (IsCalculating) return;
IsCalculating = true;
}
try
{ {
Dictionary<TVertex, Measure.Size> vertexSizes = null; Dictionary<TVertex, Measure.Size> vertexSizes = null;
IExternalLayout<TVertex> alg = null; //layout algorithm IExternalLayout<TVertex> alg = null; //layout algorithm
@ -543,12 +532,12 @@ namespace GraphX
IExternalEdgeRouting<TVertex, TEdge> eralg = null; IExternalEdgeRouting<TVertex, TEdge> eralg = null;
var result = false; var result = false;
await DispatcherHelper.CheckBeginInvokeOnUi(new Action(() => await DispatcherHelper.CheckBeginInvokeOnUi(() =>
{ {
if (LogicCore == null) if (LogicCore == null)
throw new GX_InvalidDataException("LogicCore -> Not initialized!"); throw new GX_InvalidDataException("LogicCore -> Not initialized!");
if (LogicCore.Graph == null) if (LogicCore.Graph == null)
throw new GX_InvalidDataException("LogicCore -> Graph property is not set!"); throw new GX_InvalidDataException("LogicCore -> Graph property is not set!");
if (_vertexlist.Count == 0) if (_vertexlist.Count == 0)
return; // no vertexes == no edges return; // no vertexes == no edges
@ -571,13 +560,13 @@ namespace GraphX
//setup Edge Routing algorithm //setup Edge Routing algorithm
eralg = LogicCore.GenerateEdgeRoutingAlgorithm(DesiredSize.ToGraphX()); eralg = LogicCore.GenerateEdgeRoutingAlgorithm(DesiredSize.ToGraphX());
result = true; result = true;
})); });
if (!result) return; if (!result) return;
IDictionary<TVertex, Measure.Point> resultCoords; IDictionary<TVertex, Measure.Point> resultCoords;
if (alg != null) if (alg != null)
{ {
alg.Compute(); alg.Compute(cancellationToken);
OnLayoutCalculationFinished(); OnLayoutCalculationFinished();
//if (Worker != null) Worker.ReportProgress(33, 0); //if (Worker != null) Worker.ReportProgress(33, 0);
//result data storage //result data storage
@ -594,13 +583,13 @@ namespace GraphX
{ {
//generate rectangle data from sizes //generate rectangle data from sizes
var coords = resultCoords; var coords = resultCoords;
UpdateLayout();
await DispatcherHelper.CheckBeginInvokeOnUi(() => await DispatcherHelper.CheckBeginInvokeOnUi(() =>
{ {
UpdateLayout();
rectangles = GetVertexSizeRectangles(coords, vertexSizes, true); rectangles = GetVertexSizeRectangles(coords, vertexSizes, true);
}); });
overlap.Rectangles = rectangles; overlap.Rectangles = rectangles;
overlap.Compute(); overlap.Compute(cancellationToken);
OnOverlapRemovalCalculationFinished(); OnOverlapRemovalCalculationFinished();
resultCoords = new Dictionary<TVertex, Measure.Point>(); resultCoords = new Dictionary<TVertex, Measure.Point>();
foreach (var res in overlap.Rectangles) foreach (var res in overlap.Rectangles)
@ -637,13 +626,14 @@ namespace GraphX
if (MoveAnimation.VertexStorage.Count > 0) if (MoveAnimation.VertexStorage.Count > 0)
MoveAnimation.RunVertexAnimation(); MoveAnimation.RunVertexAnimation();
foreach (var item in _edgeslist.Values) foreach (var item in _edgeslist.Values)
MoveAnimation.AddEdgeData(item); MoveAnimation.AddEdgeData(item);
if (MoveAnimation.EdgeStorage.Count > 0) if (MoveAnimation.EdgeStorage.Count > 0)
MoveAnimation.RunEdgeAnimation(); MoveAnimation.RunEdgeAnimation();
} }
UpdateLayout(); //need to update before edge routing UpdateLayout(); //need to update before edge routing
}); });
@ -653,18 +643,19 @@ namespace GraphX
await DispatcherHelper.CheckBeginInvokeOnUi(() => await DispatcherHelper.CheckBeginInvokeOnUi(() =>
{ {
//var size = Parent is ZoomControl ? (Parent as ZoomControl).Presenter.ContentSize : DesiredSize; //var size = Parent is ZoomControl ? (Parent as ZoomControl).Presenter.ContentSize : DesiredSize;
eralg.AreaRectangle = ContentSize.ToGraphX(); // new Rect(TopLeft.X, TopLeft.Y, size.Width, size.Height); eralg.AreaRectangle = ContentSize.ToGraphX();
// new Rect(TopLeft.X, TopLeft.Y, size.Width, size.Height);
rectangles = GetVertexSizeRectangles(resultCoords, vertexSizes); rectangles = GetVertexSizeRectangles(resultCoords, vertexSizes);
}); });
eralg.VertexPositions = resultCoords; eralg.VertexPositions = resultCoords;
eralg.VertexSizes = rectangles; eralg.VertexSizes = rectangles;
eralg.Compute(); eralg.Compute(cancellationToken);
OnEdgeRoutingCalculationFinished(); OnEdgeRoutingCalculationFinished();
if (eralg.EdgeRoutes != null) if (eralg.EdgeRoutes != null)
foreach (var item in eralg.EdgeRoutes) foreach (var item in eralg.EdgeRoutes)
item.Key.RoutingPoints = item.Value; item.Key.RoutingPoints = item.Value;
//if (Worker != null) Worker.ReportProgress(99, 1); //if (Worker != null) Worker.ReportProgress(99, 1);
} }
await DispatcherHelper.CheckBeginInvokeOnUi(() => await DispatcherHelper.CheckBeginInvokeOnUi(() =>
{ {
@ -688,64 +679,99 @@ namespace GraphX
} }
else OnRelayoutFinished(); else OnRelayoutFinished();
}); });
} }, cancellationToken);
finally
{
IsCalculating = false;
}
} }
/// <summary> /// <summary>
/// Relayout graph using the same vertexes /// Relayout graph using the same vertexes
/// </summary> /// </summary>
/// <param name="generateAllEdges">Generate all available edges for graph</param> /// <param name="generateAllEdges">Generate all available edges for graph</param>
public void RelayoutGraph(bool generateAllEdges = false) public Task RelayoutGraphAsync(bool generateAllEdges = false)
{ {
_relayoutGraphMain(generateAllEdges); return RelayoutGraphAsync(CancellationToken.None, generateAllEdges);
} }
private void _relayoutGraphMain(bool generateAllEdges = false, bool standalone = true) public Task RelayoutGraphAsync(CancellationToken cancellationToken, bool generateAllEdges = false)
{ {
if (LogicCore == null) return _relayoutGraphMainAsync(cancellationToken, generateAllEdges, standalone: true);
throw new GX_InvalidDataException("LogicCore -> Not initialized!"); }
if (LogicCore.AsyncAlgorithmCompute) private async Task _relayoutGraphMainAsync(CancellationToken externalCancellationToken, bool generateAllEdges = false, bool standalone = true)
{
await CancelRelayoutAsync();
_layoutCancellationSource = new CancellationTokenSource();
if (externalCancellationToken != CancellationToken.None)
_linkedLayoutCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(_layoutCancellationSource.Token, externalCancellationToken);
_layoutTask = _relayoutGraph((_linkedLayoutCancellationSource ?? _layoutCancellationSource).Token, generateAllEdges, standalone);
await _layoutTask;
}
private Task _layoutTask = null;
private CancellationTokenSource _layoutCancellationSource;
private CancellationTokenSource _linkedLayoutCancellationSource;
public async Task CancelRelayoutAsync()
{
if (_layoutTask != null)
{ {
_relayoutGraph(generateAllEdges, standalone); _layoutCancellationSource.Cancel();
return; try
} {
_relayoutGraph(generateAllEdges, standalone).Wait(); await _layoutTask;
} }
catch (OperationCanceledException)
{
// This is expected, so just ignore it
}
_layoutTask = null;
_layoutCancellationSource.Dispose();
_layoutCancellationSource = null;
if (_linkedLayoutCancellationSource != null)
{
_linkedLayoutCancellationSource.Dispose();
_linkedLayoutCancellationSource = null;
}
}
}
#endregion #endregion
/// <summary> /// <summary>
/// Generate visual graph /// Generate visual graph asynchronously
/// </summary> /// </summary>
/// <param name="graph">Data graph</param> /// <param name="graph">Data graph</param>
/// <param name="generateAllEdges">Generate all available edges for graph</param> /// <param name="generateAllEdges">Generate all available edges for graph</param>
/// <param name="dataContextToDataItem">Sets visual edge and vertex controls DataContext property to vertex data item of the control (Allows prop binding in xaml templates)</param> /// <param name="dataContextToDataItem">Sets visual edge and vertex controls DataContext property to vertex data item of the control (Allows prop binding in xaml templates)</param>
public void GenerateGraph(TGraph graph, bool generateAllEdges = false, bool dataContextToDataItem = true) public Task GenerateGraphAsync(TGraph graph, bool generateAllEdges = false, bool dataContextToDataItem = true)
{
return GenerateGraphAsync(graph, CancellationToken.None, generateAllEdges, dataContextToDataItem);
}
public Task GenerateGraphAsync(TGraph graph, CancellationToken cancellationToken, bool generateAllEdges = false, bool dataContextToDataItem = true)
{ {
if (AutoAssignMissingDataId) if (AutoAssignMissingDataId)
AutoresolveIds(graph); AutoresolveIds(graph);
if(!LogicCore.IsCustomLayout) if (!LogicCore.IsCustomLayout)
PreloadVertexes(graph, dataContextToDataItem); PreloadVertexes(graph, dataContextToDataItem);
_relayoutGraphMain(generateAllEdges, false); return _relayoutGraphMainAsync(cancellationToken, generateAllEdges, false);
} }
/// <summary> /// <summary>
/// Generate visual graph using Graph property (it must be set before this method is called) /// Generate visual graph asynchronously using Graph property (it must be set before this method is called)
/// </summary> /// </summary>
/// <param name="generateAllEdges">Generate all available edges for graph</param> /// <param name="generateAllEdges">Generate all available edges for graph</param>
/// <param name="dataContextToDataItem">Sets visual edge and vertex controls DataContext property to vertex data item of the control (Allows prop binding in xaml templates)</param> /// <param name="dataContextToDataItem">Sets visual edge and vertex controls DataContext property to vertex data item of the control (Allows prop binding in xaml templates)</param>
public void GenerateGraph(bool generateAllEdges = false, bool dataContextToDataItem = true) public Task GenerateGraphAsync(bool generateAllEdges = false, bool dataContextToDataItem = true)
{ {
if (LogicCore == null) if (LogicCore == null)
throw new GX_InvalidDataException("LogicCore -> Not initialized! (Is NULL)"); throw new GX_InvalidDataException("LogicCore -> Not initialized! (Is NULL)");
if (LogicCore.Graph == null) if (LogicCore.Graph == null)
throw new InvalidDataException("GraphArea.GenerateGraph() -> LogicCore.Graph property is null while trying to generate graph!"); throw new InvalidDataException("GraphArea.GenerateGraph() -> LogicCore.Graph property is null while trying to generate graph!");
GenerateGraph(LogicCore.Graph, generateAllEdges, dataContextToDataItem); return GenerateGraphAsync(LogicCore.Graph, generateAllEdges, dataContextToDataItem);
} }
private void AutoresolveIds(TGraph graph = null) private void AutoresolveIds(TGraph graph = null)
@ -1025,7 +1051,7 @@ namespace GraphX
} }
var orAlgo = LogicCore.AlgorithmFactory.CreateFSAA(sizes, 15f, 15f); var orAlgo = LogicCore.AlgorithmFactory.CreateFSAA(sizes, 15f, 15f);
orAlgo.Compute(); orAlgo.Compute(CancellationToken.None);
foreach (var item in orAlgo.Rectangles) foreach (var item in orAlgo.Rectangles)
{ {
if (item.Key.IsVertex) if (item.Key.IsVertex)
@ -1047,7 +1073,7 @@ namespace GraphX
{ {
LogicCore.AlgorithmStorage.EdgeRouting.VertexSizes = GetVertexSizeRectangles(); LogicCore.AlgorithmStorage.EdgeRouting.VertexSizes = GetVertexSizeRectangles();
LogicCore.AlgorithmStorage.EdgeRouting.VertexPositions = GetVertexPositions(); LogicCore.AlgorithmStorage.EdgeRouting.VertexPositions = GetVertexPositions();
LogicCore.AlgorithmStorage.EdgeRouting.Compute(); LogicCore.AlgorithmStorage.EdgeRouting.Compute(CancellationToken.None);
if (LogicCore.AlgorithmStorage.EdgeRouting.EdgeRoutes != null) if (LogicCore.AlgorithmStorage.EdgeRouting.EdgeRoutes != null)
foreach (var item in LogicCore.AlgorithmStorage.EdgeRouting.EdgeRoutes) foreach (var item in LogicCore.AlgorithmStorage.EdgeRouting.EdgeRoutes)
item.Key.RoutingPoints = item.Value; item.Key.RoutingPoints = item.Value;

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

@ -1,4 +1,5 @@
using System; using System;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Windows.UI.Core; using Windows.UI.Core;
using Windows.UI.Xaml; using Windows.UI.Xaml;
@ -7,20 +8,14 @@ namespace GraphX
{ {
public static class DispatcherHelper public static class DispatcherHelper
{ {
public static CoreDispatcher UiDispatcher { get; private set; }
public static async Task CheckBeginInvokeOnUi(Action action) public static async Task CheckBeginInvokeOnUi(Action action)
{ {
if (UiDispatcher.HasThreadAccess) var dispatcher = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher;
action();
else await UiDispatcher.RunAsync(CoreDispatcherPriority.Normal,
() => action());
}
static DispatcherHelper() if (dispatcher.HasThreadAccess)
{ action();
if (UiDispatcher != null) return; else await dispatcher.RunAsync(CoreDispatcherPriority.Normal,
UiDispatcher = Window.Current.Dispatcher; () => action());
} }
} }
} }