asynchronous loading of raddiagram controls

3 posts, 0 answers
  1. Arabinda
    Arabinda avatar
    2 posts
    Member since:
    Jan 2012

    Posted 15 Aug 2012 Link to this post

    Hello,

    I am looking for some sample code for raddiagramcontrols loading asynchronously using a background worker. Please share if you have any

  2. Arabinda
    Arabinda avatar
    2 posts
    Member since:
    Jan 2012

    Posted 15 Aug 2012 Link to this post

    Any help?
  3. UI for WPF is Visual Studio 2017 Ready
  4. Francois Vanderseypen
    Francois Vanderseypen avatar
    46 posts
    Member since:
    Oct 2009

    Posted 16 Aug 2012 Link to this post

    The background worker is the old-fashioned way to go, nowadays the TPL (task parallel library, see this extensive overview) has largely replaced the threading constructs. Here is a helper class which eases the pain of using TPL a bit;

    /// <summary>
    /// A class used by Tasks to report progress or completion updates back to the UI.
    /// </summary>
        public sealed class ProgressReporter
        {
            /// <summary>
            /// The underlying scheduler for the UI's synchronization context.
            /// </summary>
            private readonly TaskScheduler scheduler;
     
            /// <summary>
            /// Initializes a new instance of the <see cref="ProgressReporter"/> class.
            /// This should be run on a UI thread.
            /// </summary>
            public ProgressReporter()
            {
                this.scheduler = TaskScheduler.FromCurrentSynchronizationContext();
            }
     
            /// <summary>
            /// Gets the task scheduler which executes tasks on the UI thread.
            /// </summary>
            public TaskScheduler Scheduler
            {
                get { return this.scheduler; }
            }
     
            /// <summary>
            /// Reports the progress to the UI thread. This method should be called from the task.
            /// Note that the progress update is asynchronous with respect to the reporting Task.
            /// For a synchronous progress update, wait on the returned <see cref="Task"/>.
            /// </summary>
            /// <param name="action">The action to perform in the context of the UI thread.
            /// Note that this action is run asynchronously on the UI thread.</param>
            /// <returns>The task queued to the UI thread.</returns>
            public Task ReportProgressAsync(Action action)
            {
                return Task.Factory.StartNew(action, CancellationToken.None, TaskCreationOptions.None, this.scheduler);
            }
     
            /// <summary>
            /// Reports the progress to the UI thread, and waits for the UI thread to process
            /// the update before returning. This method should be called from the task.
            /// </summary>
            /// <param name="action">The action to perform in the context of the UI thread.</param>
            public void ReportProgress(Action action)
            {
                this.ReportProgressAsync(action).Wait();
            }
     
            /// <summary>
            /// Registers a UI thread handler for when the specified task finishes execution,
            /// whether it finishes with success, failiure, or cancellation.
            /// </summary>
            /// <param name="task">The task to monitor for completion.</param>
            /// <param name="action">The action to take when the task has completed, in the context of the UI thread.</param>
            /// <returns>The continuation created to handle completion. This is normally ignored.</returns>
            public Task RegisterContinuation(Task task, Action action)
            {
                return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.None, this.scheduler);
            }
     
            /// <summary>
            /// Registers a UI thread handler for when the specified task finishes execution,
            /// whether it finishes with success, failiure, or cancellation.
            /// </summary>
            /// <typeparam name="TResult">The type of the task result.</typeparam>
            /// <param name="task">The task to monitor for completion.</param>
            /// <param name="action">The action to take when the task has completed, in the context of the UI thread.</param>
            /// <returns>The continuation created to handle completion. This is normally ignored.</returns>
            public Task RegisterContinuation<TResult>(Task<TResult> task, Action action)
            {
                return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.None, this.scheduler);
            }
     
            /// <summary>
            /// Registers a UI thread handler for when the specified task successfully finishes execution.
            /// </summary>
            /// <param name="task">The task to monitor for successful completion.</param>
            /// <param name="action">The action to take when the task has successfully completed, in the context of the UI thread.</param>
            /// <returns>The continuation created to handle successful completion. This is normally ignored.</returns>
            public Task RegisterSucceededHandler(Task task, Action action)
            {
                return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, this.scheduler);
            }
     
            /// <summary>
            /// Registers a UI thread handler for when the specified task successfully finishes execution
            /// and returns a result.
            /// </summary>
            /// <typeparam name="TResult">The type of the task result.</typeparam>
            /// <param name="task">The task to monitor for successful completion.</param>
            /// <param name="action">The action to take when the task has successfully completed, in the context of the UI thread.
            /// The argument to the action is the return value of the task.</param>
            /// <returns>The continuation created to handle successful completion. This is normally ignored.</returns>
            public Task RegisterSucceededHandler<TResult>(Task<TResult> task, Action<TResult> action)
            {
                return task.ContinueWith(t => action(t.Result), CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, this.Scheduler);
            }
     
            /// <summary>
            /// Registers a UI thread handler for when the specified task becomes faulted.
            /// </summary>
            /// <param name="task">The task to monitor for faulting.</param>
            /// <param name="action">The action to take when the task has faulted, in the context of the UI thread.</param>
            /// <returns>The continuation created to handle faulting. This is normally ignored.</returns>
            public Task RegisterFaultedHandler(Task task, Action<Exception> action)
            {
                return task.ContinueWith(t => action(t.Exception), CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, this.Scheduler);
            }
     
            /// <summary>
            /// Registers a UI thread handler for when the specified task becomes faulted.
            /// </summary>
            /// <typeparam name="TResult">The type of the task result.</typeparam>
            /// <param name="task">The task to monitor for faulting.</param>
            /// <param name="action">The action to take when the task has faulted, in the context of the UI thread.</param>
            /// <returns>The continuation created to handle faulting. This is normally ignored.</returns>
            public Task RegisterFaultedHandler<TResult>(Task<TResult> task, Action<Exception> action)
            {
                return task.ContinueWith(t => action(t.Exception), CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, this.Scheduler);
            }
     
            /// <summary>
            /// Registers a UI thread handler for when the specified task is cancelled.
            /// </summary>
            /// <param name="task">The task to monitor for cancellation.</param>
            /// <param name="action">The action to take when the task is cancelled, in the context of the UI thread.</param>
            /// <returns>The continuation created to handle cancellation. This is normally ignored.</returns>
            public Task RegisterCancelledHandler(Task task, Action action)
            {
                return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, this.Scheduler);
            }
     
            /// <summary>
            /// Registers a UI thread handler for when the specified task is cancelled.
            /// </summary>
            /// <typeparam name="TResult">The type of the task result.</typeparam>
            /// <param name="task">The task to monitor for cancellation.</param>
            /// <param name="action">The action to take when the task is cancelled, in the context of the UI thread.</param>
            /// <returns>The continuation created to handle cancellation. This is normally ignored.</returns>
            public Task RegisterCancelledHandler<TResult>(Task<TResult> task, Action action)
            {
                return task.ContinueWith(_ => action(), CancellationToken.None, TaskContinuationOptions.OnlyOnCanceled, this.Scheduler);
            }
        }

    With this helper class you can now go in any direction you wish and it all depends a bit on your business context.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows;
     
    using Telerik.Windows.Controls;
    using Telerik.Windows.Controls.Diagrams;
    using Telerik.Windows.Diagrams.Core;
     
    using Edge = Telerik.Windows.Diagrams.Core.Edge<object, object>;
    using Graph = Telerik.Windows.Diagrams.Core.GraphBase<Telerik.Windows.Diagrams.Core.Node<object, object>, Telerik.Windows.Diagrams.Core.Edge<object, object>>;
    namespace AsyncDiagram
    {
        /// <summary>
        /// Interaction logic for MainWindow.xaml
        /// </summary>
        public partial class MainWindow
        {
            static Random rand = new Random(Environment.TickCount);
     
            private static int counter = 1000;
            public MainWindow()
            {
                InitializeComponent();
                Loaded += OnLoaded;
            }
     
            private void OnLoaded(object sender, RoutedEventArgs routedEventArgs)
            {
                var graph = GraphExtensions.CreateRandomGraph(50);
                CreateDiagram(diagram, graph);
                diagram.Layout();
                this.StartListening();
     
            }
            public static Graph CreateDiagram(Telerik.Windows.Controls.RadDiagram diagram, Graph g)
            {
                if (g == null) throw new ArgumentNullException("g");
                diagram.Clear();
     
                var dic = new Dictionary<int, RadDiagramShape>();
                foreach (var node in g.Nodes)
                {
                    var shape = CreateShape(node.Id);
                    diagram.AddShape(shape);
                    dic.Add(node.Id, shape);
                }
                foreach (
                    var con in
                        g.Links.Select(link => diagram.AddConnection(dic[link.Source.Id], dic[link.Sink.Id]) as RadDiagramConnection)) con.TargetCapType = CapType.Arrow1Filled;
                return g;
            }
            private static RadDiagramShape CreateShape(int id)
            {
                return new RadDiagramShape { Width = 30, Height = 30, Content = id, Tag = id, Geometry = ShapeFactory.GetShapeGeometry(CommonShapeType.EllipseShape) };
            }
            private void StartListening()
            {
                var reporter = new ProgressReporter();
                var task = Task.Factory.StartNew(
                    () =>
                    {
                        while (true)
                        {
                            // here you should listen to whatever service returns data and
                            // eventually also break the loop via a button click.
                            // In any case, this TPL task will not block in any way the UI.
                            Thread.Sleep(1500);
                            reporter.ReportProgress(
                                () =>
                                {
                                    // change the diagram according to the input
                                    var randomConnection = this.diagram.Connections[rand.Next(this.diagram.Connections.Count)];
                                    var randomShape = this.diagram.Shapes[rand.Next(this.diagram.Shapes.Count)];
                                    var value = Math.Round(rand.NextDouble() * 100, 2);
                                    // let's say you differentiate between three choices, but it's really up to your business context here
                                    if (value < 40)
                                    {
                                        // remove all connections first
                                        var cons = diagram.Connections.Where(c => c.Source == randomShape || c.Target == randomShape).ToArray();
                                        cons.ForEach(c => diagram.RemoveConnection(c));
                                        diagram.RemoveShape(randomShape);
                                    }
                                    else
                                        if (value > 80)
                                        {
                                            var newShape = CreateShape(counter++);
                                            var newCon = diagram.AddConnection(newShape, randomShape);
                                            newCon.TargetCapType = CapType.Arrow1Filled;
                                            diagram.Layout();
                                        }
                                        else
                                        {
                                            var p = randomShape.Position;
                                            randomShape.Position = new Point(p.X + rand.NextDouble() * 400, p.Y + rand.NextDouble() * 200);
                                        }
                                });
                        }
                    });
            }
     
        }
    }

    I've used above a utility class contained within the Extensions library to generate a random diagram first, but you are free to start however you wish. The crucial bits sits in the StartListening method wherein the TPL helper class is called. Note that you don't need to access the Dispatcher.Invoke, which would be the case if you would use the background worker thread or a more basic threading solution.

    Finally, this piece of code would also simply/change if you would use the await/async of .Net 4.5

    Hope this helps you out, Fr.
Back to Top