Tuesday, December 29, 2015

SignalR Implementation in XAML Page

XAML Page:

using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using Microsoft.AspNet.SignalR.Client;

namespace SignalRDemo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        HubConnection _connection;
        IHubProxy _hub;

        // Keep track of when fake "drag and drop" mode is enabled.
        private bool isDragging = false;

        // Store the location where the user clicked the control.
        private double clickOffsetX, clickOffsetY;

        private void window_Loaded(object sender, RoutedEventArgs e)
        {
            Start();
        }

        void label_MouseDown(object sender, MouseButtonEventArgs e)
        {
            isDragging = true;
            var position = e.GetPosition(window);

            var label = sender as Label;
            var l = (double)label.GetValue(Canvas.LeftProperty);
            var t = (double)label.GetValue(Canvas.TopProperty);

            clickOffsetX = l - position.X;
            clickOffsetY = t - position.Y;
        }

        void label_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDragging == true)
            {
                // The control coordinates are converted into form coordinates
                // by adding the label position offset.
                // The offset where the user clicked in the control is also
                // accounted for. Otherwise, it looks like the top-left corner
                // of the label is attached to the mouse.

                var label = sender as Label;
                var position = e.GetPosition(window);

                var x = position.X + clickOffsetX;
                var y = position.Y + clickOffsetY;

                SetShapePosition(label, x, y);

                _hub.Invoke("MoveShape", int.Parse(label.Name.Replace("shape", "")), x, y);
            }
        }

        void label_MouseUp(object sender, MouseButtonEventArgs e)
        {
            isDragging = false;
        }

        private void Start()
        {
            _connection = new HubConnection("http://localhost/SignalRChat/myhubs/hubs");

            _hub = _connection.CreateHubProxy("SignalRHub");

            _connection.Start().Wait();

            _hub.On<int>("usersConnected", (count) =>
            {
                this.Dispatcher.Invoke(() =>
                    UpdateUserCount(count)
                );
            });

            _hub.On<List<Common.Shape>>("shapeList", (list) =>
            {
                this.Dispatcher.Invoke(() =>
                {
                    canvas.Children.Clear();

                    list.ForEach(q =>
                    {
                        var label = new Label();

                        label.Name = string.Format("shape{0}", q.Id);
                        label.Content = q.Name;
                        label.Height = 100;
                        label.Width = 100;
                        label.Padding = new Thickness(10, 10, 0, 0);
                        label.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString(q.Color));
                        SetShapePosition(label, q.X, q.Y);
                        label.FontSize = 20;

                        label.MouseDown += label_MouseDown;
                        label.MouseMove += label_MouseMove;
                        label.MouseUp += label_MouseUp;

                        canvas.Children.Add(label);
                    });
                });
            });

            _hub.On<Common.Shape>("shapeMoved", (shape) =>
            {
                this.Dispatcher.Invoke(() =>
                    UpdatePosition(shape)
                );
            });

            _hub.Invoke("GetShapeList");
        }

        private void SetShapePosition(Label label, double x, double y)
        {
            if (label != null)
            {
                label.SetValue(Canvas.LeftProperty, x);
                label.SetValue(Canvas.TopProperty, y);
            }
        }

        private void UpdateUserCount(int count)
        {
            lblCount.Content = count;
        }

        private void UpdatePosition(Common.Shape shape)
        {
            var label = UIHelper.FindChild<Label>(Application.Current.MainWindow, string.Format("shape{0}", shape.Id));
            SetShapePosition(label, shape.X, shape.Y);
        }
    }

    public class UIHelper
    {
        /// <summary>
        /// Finds a Child of a given item in the visual tree. 
        /// </summary>
        /// <param name="parent">A direct parent of the queried item.</param>
        /// <typeparam name="T">The type of the queried item.</typeparam>
        /// <param name="childName">x:Name or Name of child. </param>
        /// <returns>The first parent item that matches the submitted type parameter. 
        /// If not matching item can be found, 
        /// a null parent is being returned.</returns>
        public static T FindChild<T>(DependencyObject parent, string childName)
           where T : DependencyObject
        {
            // Confirm parent and childName are valid. 
            if (parent == null) return null;

            T foundChild = null;

            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                // If the child is not of the request child type child
                T childType = child as T;
                if (childType == null)
                {
                    // recursively drill down the tree
                    foundChild = FindChild<T>(child, childName);

                    // If the child is found, break so we do not overwrite the found child. 
                    if (foundChild != null) break;
                }
                else if (!string.IsNullOrEmpty(childName))
                {
                    var frameworkElement = child as FrameworkElement;
                    // If the child's name is set for search
                    if (frameworkElement != null && frameworkElement.Name == childName)
                    {
                        // if the child's name is of the request name
                        foundChild = (T)child;
                        break;
                    }
                }
                else
                {
                    // child element found.
                    foundChild = (T)child;
                    break;
                }
            }

            return foundChild;
        }
    }

}

SignalR Hub Page :

using Microsoft.AspNet.SignalR;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Common;

namespace SignalRChat
{
    public class SignalRHub : Hub
    {
        public SignalRHub()
        {

        }

        #region Moveable Shape
        public async Task GetShapeList() // this method will be called from the client, when the user drag/move the shape
        {
            await Clients.Caller.shapeList(ShapeHandler.Instance.ShapeList); // this method will send the coord x, y to the other users but the user draging the shape
        }

        public async Task MoveShape(int id, double x, double y) // this method will be called from the client, when the user drag/move the shape
        {
            var shape = ShapeHandler.Instance.GetShape(id, x, y);

            if (shape.X != x || shape.Y != y) await Clients.Caller.shapeMoved(shape);

            await Clients.Others.shapeMoved(shape); // this method will send the coord x, y to the other users but the user draging the shape
        }

        public override Task OnConnected() //override OnConnect, OnReconnected and OnDisconnected to know if a user is connected or disconnected
        {
            ShapeHandler.Instance.ConnectedIds.Add(Context.ConnectionId); //add a connection id to the list
            Clients.All.usersConnected(ShapeHandler.Instance.ConnectedIds.Count()); //this will send to ALL the clients the number of users connected
            return base.OnConnected();
        }

        public override Task OnReconnected()
        {
            ShapeHandler.Instance.ConnectedIds.Add(Context.ConnectionId);
            Clients.All.usersConnected(ShapeHandler.Instance.ConnectedIds.Count());
            return base.OnConnected();
        }

        public override Task OnDisconnected(bool stopCalled)
        {
            ShapeHandler.Instance.ConnectedIds.Remove(Context.ConnectionId);
            Clients.All.usersConnected(ShapeHandler.Instance.ConnectedIds.Count());
            return base.OnDisconnected(stopCalled);
        }
        #endregion
    }
}

SignalR Implementation in HTML Page

Html Page :

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <label id="serverDate"></label>
    <button id="initTimer">Start Timer</button>

    <!--Script references. -->
    <script src="Scripts/jquery-1.6.4.min.js"></script>
    <script src="Scripts/jquery.signalR-2.0.0.js"></script>

    <!--Reference the autogenerated SignalR hub script. -->
    <script src="signalR/hubs"></script>
    <script type="text/javascript">
        $(function () {
            // Declare a proxy to reference the hub.
            var hub = $.connection.demoHub;

            hub.client.serverDateTimeInfo = function (time) {
                $("#serverDate").text(time);
            };

            $.connection.hub.start().done(function () {
                alert('Server connected');

                hub.server.getServerDatetime();

                $("#initTimer").click(function () {
                    hub.server.initTimer();
                });
            });
        });
    </script>
</body>
</html>

SignalR Hub Page :

using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Timers;
using System.Web;

namespace SignalRChat
{
    public class DemoHub :Hub
    {
        public void GetServerDatetime()
        {
            SendNotification();
        }

        private void SendNotification()
        {
            try
            {
                Clients.Caller.serverDateTimeInfo(DateTime.Now.ToString());
            }
            catch { }
        }

        public void InitTimer()
        {
            Timer timer = new Timer(1000);
            timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
            timer.AutoReset = true;
            timer.Enabled = true;
        }

        public void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            SendNotification();
        }
        
    }
}