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.
![image image](/blog/image.axd?picture=image_thumb_31.png)
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
![image image](/blog/image.axd?picture=image_thumb_32.png)
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
1: <Window x:Class="Datagenic__Monitor.MainWindow"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
5: Title="MainWindow" Height="350" Width="525">
6: <Window.Background>
7: <ImageBrush ImageSource="/Datagenic-%20Monitor;component/Images/background.png" />
8: </Window.Background>
9: <Grid>
10: <telerik:RadialGauge>
11: <telerik:RadialScale x:Name="radialScale" Min="0" Max="{Binding Path=MaxScale}" MajorTicks="10"
12: MiddleTicks="1" MinorTicks="3">
13:
14: <telerik:RadialScale.MajorTick>
15: <telerik:MajorTickProperties />
16: </telerik:RadialScale.MajorTick>
17: <telerik:RadialScale.MiddleTick>
18: <telerik:MiddleTickProperties Length="0.07" />
19: </telerik:RadialScale.MiddleTick>
20: <telerik:RadialScale.MinorTick>
21: <telerik:MinorTickProperties Length="0.05" />
22: </telerik:RadialScale.MinorTick>
23:
24: <telerik:RadialScale.Label>
25: <telerik:LabelProperties FontSize="10" />
26: </telerik:RadialScale.Label>
27:
28: <telerik:IndicatorList>
29: <telerik:RadialBar x:Name="gauge1_radialBar" IsAnimated="True"
Value="{Binding Path=TotalLogCount}" />
30:
31: <telerik:Needle x:Name="gauge1_needle" IsAnimated="true"
Value="{Binding Path=WFExecutions[0].LogCount}" />
32: </telerik:IndicatorList>
33: </telerik:RadialScale>
34: </telerik:RadialGauge>
35: <Grid>
36: <Grid.RowDefinitions>
37: <RowDefinition Height="0.60*" />
38: <RowDefinition Height="0.40*" />
39: </Grid.RowDefinitions>
40:
41: <TextBlock Grid.Row="1" VerticalAlignment="Top" HorizontalAlignment="Center"
42: Foreground="GhostWhite" FontFamily="CourierNew"
Text="{Binding Path=WFExecutions[0].FolderName}" />
43: </Grid>
44:
45: </Grid>
46: </Window>
Here’s the code
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using System.Windows;
6: using System.Windows.Controls;
7: using System.Windows.Data;
8: using System.Windows.Documents;
9: using System.Windows.Input;
10: using System.Windows.Media;
11: using System.Windows.Media.Imaging;
12: using System.Windows.Navigation;
13: using System.Windows.Shapes;
14: using System.Collections.ObjectModel;
15: using System.ComponentModel;
16: using System.IO;
17:
18: namespace Datagenic__Monitor
19: {
20: /// <summary>
21: /// Interaction logic for MainWindow.xaml
22: /// </summary>
23: public partial class MainWindow : Window
24: {
25: public MainWindow()
26: {
27: InitializeComponent();
28:
29: this.DataContext = _wfExecutions;
30: }
31:
32: private WorkflowsExecutions _wfExecutions = new WorkflowsExecutions();
33: }
34:
35:
36: class WorkflowsExecutions :NotifyPropertyChangedBase
37: {
38: public WorkflowsExecutions()
39: {
40: this.WFExecutions = new List<WorkflowExecutions>();
41:
42: // Get the individual folders that corresponds to the logs
43: var executionFolders = System.IO.Directory.EnumerateDirectories(_executionFolder);
44: if (executionFolders != null)
45: executionFolders.ToList().ForEach(f =>
46: {
47: var we = new WorkflowExecutions(f);
48: we.PropertyChanged += ItemPropChanged;
49: this.WFExecutions.Add(we);
50: });
51: }
52:
53: void ItemPropChanged(object sender, PropertyChangedEventArgs e)
54: {
55: if (e.PropertyName == "LogCount")
56: {
57: FirePropertyChanged("TotalLogCount");
58: if (TotalLogCount > MaxScale)
59: FirePropertyChanged("MaxScale");
60: }
61: }
62:
63: public int TotalLogCount
64: {
65: get
66: {
67: return this.WFExecutions.Sum(we => we.LogCount);
68: }
69: set { }
70: }
71:
72: public int MaxScale
73: {
74: get
75: {
76: int max = (int)(this.TotalLogCount * 1.5);
77: max = max + (10 - max % 10);
78: return Math.Max(100, max);
79: }
80: set { }
81: }
82:
83: public List<WorkflowExecutions> WFExecutions { get; set; }
84:
85:
86: private string _executionFolder = Properties.Settings.Default.ExecutionLogFolder;
87:
88:
89:
90: //event PropertyChangedEventHandler PropertyChanged = (s, e) => { };
91: }
92:
93: class WorkflowExecutions : NotifyPropertyChangedBase
94: {
95: public WorkflowExecutions(string folder)
96: {
97: this.FolderName = System.IO.Path.GetFileName(folder);
98: _watcher = new FileSystemWatcher(folder);
99: _watcher.Deleted += (s, e) => Update();
100: _watcher.Created += (s, e) => Update();
101: _watcher.EnableRaisingEvents = true;
102: Update();
103: }
104:
105:
106: public string FolderName { get; set; }
107:
108:
109: public int LogCount
110: {
111: get { return _logCount; }
112: set
113: {
114: _logCount = value;
115: base.FirePropertyChanged("LogCount");
116: }
117: }
118:
119: private void Update()
120: {
121: var files = System.IO.Directory.EnumerateFiles(_watcher.Path);
122: this.LogCount = files.Count();
123: }
124:
125: private int _logCount = 0;
126: private FileSystemWatcher _watcher;
127:
128: }
129: }
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 it bindings. (Note: I half expect telerik gauge to auto scale, if i spend a few minutes to figure out how…
)