BKStart a conversation ↗

Automatically Scaling Telerik Radial Gauge

Took a little time out tonight from the web application I’m working on as I want to create a little dashboard prototype that has to be desktop based as a result of where it will be used.

I’ve got a server JBPM (Java business process management... pretty similar to WF4) component that is writing log files, these log files are divided into subdirectories for each JBPM workflow that gets executed.

The idea is to provide a quick view for the counts of server logs at a glance with a gauge (I'll be putting this gauge into a template for use in a listbox, but as a first step I’ve just displayed the details of the files in the first workflow folder).

Here’s what it looks like.

The text is bound to the directory name, the yellow/orange radial bar is the count of all the log files in all subdirectories, and the needle is the count of files in the EchoLoader directory.

Here’s the XAML.

<Window x:Class="Datagenic__Monitor.MainWindow"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                Title="MainWindow" Height="350" Width="525">
    <Window.Background>
        <ImageBrush ImageSource="/Datagenic-%20Monitor;component/Images/background.png" />
    </Window.Background>
    <Grid>
        <telerik:RadialGauge>
            <telerik:RadialScale x:Name="radialScale" Min="0" Max="{Binding Path=MaxScale}" MajorTicks="10"
                            MiddleTicks="1" MinorTicks="3">

                <telerik:RadialScale.MajorTick>
                    <telerik:MajorTickProperties />
                </telerik:RadialScale.MajorTick>
                <telerik:RadialScale.MiddleTick>
                    <telerik:MiddleTickProperties Length="0.07" />
                </telerik:RadialScale.MiddleTick>
                <telerik:RadialScale.MinorTick>
                    <telerik:MinorTickProperties Length="0.05" />
                </telerik:RadialScale.MinorTick>

                <telerik:RadialScale.Label>
                    <telerik:LabelProperties FontSize="10" />
                </telerik:RadialScale.Label>

                <telerik:IndicatorList>
                    <telerik:RadialBar x:Name="gauge1_radialBar" IsAnimated="True"
                               Value="{Binding Path=TotalLogCount}" />

                    <telerik:Needle x:Name="gauge1_needle" IsAnimated="true"
                                Value="{Binding Path=WFExecutions[0].LogCount}" />
                </telerik:IndicatorList>
            </telerik:RadialScale>
        </telerik:RadialGauge>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="0.60*" />
                <RowDefinition Height="0.40*" />
            </Grid.RowDefinitions>

            <TextBlock Grid.Row="1" VerticalAlignment="Top" HorizontalAlignment="Center"
                            Foreground="GhostWhite" FontFamily="CourierNew"
                                   Text="{Binding Path=WFExecutions[0].FolderName}" />
        </Grid>

    </Grid>
</Window>

Here’s the code.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;

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

            this.DataContext = _wfExecutions;
        }

        private WorkflowsExecutions _wfExecutions = new WorkflowsExecutions();
    }


    class WorkflowsExecutions :NotifyPropertyChangedBase
    {
        public WorkflowsExecutions()
        {
            this.WFExecutions = new List<WorkflowExecutions>();

            // Get the individual folders that corresponds to the logs
            var executionFolders = System.IO.Directory.EnumerateDirectories(_executionFolder);
            if (executionFolders != null)
                executionFolders.ToList().ForEach(f =>
                    {
                        var we = new WorkflowExecutions(f);
                        we.PropertyChanged += ItemPropChanged;
                        this.WFExecutions.Add(we);
                    });
        }

        void ItemPropChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "LogCount")
            {
                FirePropertyChanged("TotalLogCount");
                if (TotalLogCount > MaxScale)
                    FirePropertyChanged("MaxScale");
            }
        }

        public int TotalLogCount
        {
            get
            {
                return this.WFExecutions.Sum(we => we.LogCount);
            }
            set { }
        }

        public int MaxScale
        {
            get
            {
                int max = (int)(this.TotalLogCount * 1.5);
                max = max + (10 - max % 10);
                return Math.Max(100, max);
            }
            set { }
        }

        public List<WorkflowExecutions> WFExecutions { get; set; }


        private string _executionFolder = Properties.Settings.Default.ExecutionLogFolder;



        //event PropertyChangedEventHandler PropertyChanged = (s, e) => { };
    }

    class WorkflowExecutions : NotifyPropertyChangedBase
    {
        public WorkflowExecutions(string folder)
        {
            this.FolderName = System.IO.Path.GetFileName(folder);
            _watcher = new FileSystemWatcher(folder);
            _watcher.Deleted += (s, e) => Update();
            _watcher.Created += (s, e) => Update();
            _watcher.EnableRaisingEvents = true;
            Update();
        }


        public string FolderName { get; set; }


        public int LogCount
        {
            get { return _logCount; }
            set
            {
                _logCount = value;
                base.FirePropertyChanged("LogCount");
            }
        }

        private void Update()
        {
            var files = System.IO.Directory.EnumerateFiles(_watcher.Path);
            this.LogCount = files.Count();
        }

        private int _logCount = 0;
        private FileSystemWatcher _watcher;

    }
}

The interesting part is the auto scaling, if the TotalLogFile count passes the max scale level the view will get the property changed notification and update its bindings. (Note: I half expect the Telerik gauge to auto scale, if I spend a few minutes to figure out how…)