Real-Time Visitor Count in Sitefinity with SignalR

In the previous post we looked at how to install and use SignalR in Sitefinity with a really simple popup notification sample. This time we'll look at how we can leverage SignalR to create a real-time count of readers visiting a blog post. Be sure you've reviewed the previous post as we'll be extending the existing Notify Hub and JavaScript for this example.

Modifying the Hub Class

The first thing we need to do is add a static int property to count the visitors to the page. We also need to expose a public method to trigger the Hub so that it can push out the update to all connected clients (not just the latest visitor). The actual counting of visitors will be achieved by overriding the OnConnected and OnDisconnected methods to that the total is incremented or decremented respectively as clients connect (load the page) and disconnect (close their browser or navigate away from the page). The complete code sample is below:

using System; 
using System.Linq; 
using Microsoft.AspNet.SignalR; 
using Microsoft.AspNet.SignalR.Hubs;  
namespace SitefinityWebApp.App_Custom.SignalrHubs { 
    [HubName("notice")] 
    public class NoticeHub : Hub { 
        private static int visitorCount = 0;  
        public override System.Threading.Tasks.Task OnConnected() { 
            visitorCount++; RefreshCount(); 
            return base.OnConnected(); 
        }  
        
        public override System.Threading.Tasks.Task OnDisconnected() { 
            visitorCount--; RefreshCount(); 
            return base.OnDisconnected(); 
        }  
        
        public void RefreshCount() { 
            var visitorText = string.Format("{0} user{1} currently reading this post.", visitorCount, visitorCount == 1 ? "" : "s"); 
            Clients.All.refreshVisitorCount(visitorText); 
        } 
    } 
}

Update the Blog Details Template

Because we already added all the JavaScript and CSS references last time, we simply need to update the Sitefinity Blog Details template with an area to display the viewer count text, as well as the script to connect to the hub. Ideally, you'd probably want to put this in a separate file or part of your JavaScript framework, but to keep things simple I'll just put it inline. Here's a copy of the updated Blog Details template.

<%@ Control Language="C#" %> 
<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %> 
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI" Assembly="Telerik.Sitefinity" %> 
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.ContentUI" Assembly="Telerik.Sitefinity" %> 
<%@ Register TagPrefix="sf" Namespace="Telerik.Sitefinity.Web.UI.PublicControls.BrowseAndEdit" Assembly="Telerik.Sitefinity" %> 
<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Modules.Comments.Web.UI.Frontend" TagPrefix="comments" %> 
<%@ Import Namespace="Telerik.Sitefinity" %> 
<%@ Import Namespace="Telerik.Sitefinity.Web.UI" %> 
<%@ Import Namespace="Telerik.Sitefinity.Modules.Comments" %> 
<script type="text/javascript">

	$.connection.hub.logging = true; 
    $.connection.hub.start(); 
    $.connection.notice.client.refreshVisitorCount = function onRefreshCount(msg) { 
        $(".count").text(msg); 
    }; 
</script> 

<sf:SitefinityLabel id="title" runat="server" WrapperTagName="div" HideIfNoText="true" HideIfNoTextMode="Server" /> 
<telerik:RadListView ID="SingleItemContainer" ItemPlaceholderID="ItemContainer" AllowPaging="False" runat="server" EnableEmbeddedSkins="false" EnableEmbeddedBaseStylesheet="false"> 
    <layouttemplate> 
        <%-- <div class="sfpostLinksWrp"> 
    <sf:MasterViewHyperLink class="sfpostBack sfback" Text="<%$ Resources:BlogResources, allposts %>" runat="server" /> 
        </div> 
    --%> 
    <asp:PlaceHolder ID="ItemContainer" runat="server" /> 
    </layouttemplate> 
<itemtemplate> 
    <div class="sfpostDetails sfdetails" data-sf-provider='<%# Eval("Provider.Name")%>' data-sf-id='<%# Eval("Id")%>' data-sf-type="Telerik.Sitefinity.Blogs.Model.BlogPost"> 
        <sf:FieldListView ID="PostTitle" runat="server" Text="{0}" Properties="Title" WrapperTagName="h1" WrapperTagCssClass="sfpostTitle sftitle" EditableFieldType="ShortText" /> 
        <div class="sfpostAuthorAndDate sfmetainfo"> 
            <asp:Literal ID="Literal2" Text="<%$ Resources:Labels, By %>" runat="server" /> 
            <sf:PersonProfileView ID="PersonProfileView1" runat="server" /> 
            <sf:FieldListView ID="PostDate" runat="server" Format=" | {PublicationDate.ToLocal():MMM dd, yyyy}" /> <comments:CommentsCountControl ID="CommentsCountControl1" runat="server" ThreadKey='<%# ControlUtilities.GetLocalizedKey(Eval("Id"), null, CommentsBehaviorUtilities.GetLocalizedKeySuffix(Container.DataItem.GetType().FullName)) %>' AllowComments='<%# Eval("AllowComments") %>' ThreadType='<%# Container.DataItem.GetType().FullName %>' NavigateUrl="#commentsWidget" DisplayMode="ShortText"/> | 
            <div id="blogCounter">Active Readers: <span class="count" /> 
                <comments:CommentsAverageRatingControl ID="CommentsAverageRatingControl1" runat="server" ThreadKey='<%# ControlUtilities.GetLocalizedKey(Eval("Id"), null, CommentsBehaviorUtilities.GetLocalizedKeySuffix(Container.DataItem.GetType().FullName)) %>' ThreadType='<%# Container.DataItem.GetType().FullName %>' NavigateUrl="#commentsWidget" DisplayMode="FullText"/> 
            </div> 
            <sf:FieldListView ID="PostContent" runat="server" Text="{0}" Properties="Content" WrapperTagName="div" WrapperTagCssClass="sfpostContent sfcontent" EditableFieldType="LongText" /> 
            <asp:PlaceHolder ID="socialOptionsContainer" runat="server"> 
            </asp:PlaceHolder> 
            <comments:CommentsWidget ID="CommentsWidget1" runat="server" ThreadKey='<%# ControlUtilities.GetLocalizedKey(Eval("Id"), null, CommentsBehaviorUtilities.GetLocalizedKeySuffix(Container.DataItem.GetType().FullName)) %>' AllowComments='<%# Eval("AllowComments") %>' ThreadTitle='<%# Eval("Title") %>' ThreadType='<%# Container.DataItem.GetType().FullName %>' GroupKey='<%# ControlUtilities.GetUniqueProviderKey("Telerik.Sitefinity.Modules.Blogs.BlogsManager", Eval("Provider.Name").ToString()) %>' DataSource='<%# Eval("Provider.Name")%>' /> </div> </itemtemplate> </telerik:RadListView>

That's all there is to it! Loading the page initially fires the event on connect to trigger the count, which in turn pushes out that message to all connected clients. As users come and go, the count is automatically updated in real time to all visitors on the page. Here are some screenshots of it in action.

Sitefinity-Visitor-Count-With-SignalR

Sitefinity-Visitor-Count-With-SignalR-Decrease-Count

Sitefinity-Visitor-Count-With-SignalR-Increase-Count

Wrapping Up

Thanks to SignalR we were able to add new, interactive functionality to our blog with just a few lines of code. So much can be done with this library, so I encourage you to try it out for yourself. I'm also very interested to hear if and how you are making use of SignalR, so please sound off about your projects in the comments! As always, I hope this was helpful!

Enjoyed this post and/or found it useful?
SelArom Dot Net Profile Image
SelAromDotNet

Josh loves all things Microsoft and Windows, and develops solutions for Web, Desktop and Mobile using the .NET Framework, Azure, UWP and everything else in the Microsoft Stack.

His other passion is music, and in his spare time Josh spins and produces electronic music under the name DJ SelArom.



Scroll to top