Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / WPF

WPF Memory Usage Chart

Rate me:
Please Sign up or sign in to vote.
4.91/5 (8 votes)
19 Dec 2011CPOL2 min read 39.4K   3.8K   44   2
A WPF memory usage chart that is based on the WPF Toolkit charting package.

Introduction

This code sample demonstrates how to create a memory usage chart in WPF, which updates in real-time.

Background

The WPF Toolkit provides a nice charting package that is customizable and extensible. This article demonstrates how to create a modular memory monitoring control using this chart and a dispatch timer. The control updates in real time, providing the current .NET run-time memory usage. (Note: This is the total (estimated) amount of managed memory used by your application - it doesn't include unsafe/native code.)

Using the code

You can get the full WPF Toolkit at http://wpf.codeplex.com. In my sample, I have only included the base DLL and the DataVisualizations package.

The first step is to customize the look and feel of the chart. Create a new User Control and reference WPFToolkit and System.Windows.Controls.DataVisualization.Toolkit. When you paste the chart XML below, the WPF designer should automatically start displaying the chart with the default look and feel.

XML
<UserControl x:Class="WpfMemoryChart.MemoryChart"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
         xmlns:Charting="clr-namespace:System.Windows.Controls.
           DataVisualization.Charting;
           assembly=System.Windows.Controls.DataVisualization.Toolkit"
         xmlns:chartingprimitives="clr-namespace:System.Windows.Controls.
           DataVisualization.Charting.Primitives;
           assembly=System.Windows.Controls.DataVisualization.Toolkit"
         mc:Ignorable="d" >
    <Grid>
        <Charting:Chart Name="MemoryChartComponent"></Charting:Chart>  
    </Grid>
</UserControl>

I chose to use AreaSeries, but you can also use line series (i.e., if you want the chart to look more like the Windows Task Manager).

By default, the WPF Toolkit chart displays physical points for each datapoint in the series. You can turn this off by setting the data point style:

XML
<Charting:AreaSeries>
    <Charting:AreaSeries.DataPointStyle>
        <Style TargetType="Charting:AreaDataPoint">
            <Setter Property="Opacity" Value="0" />
            <Setter Property="Background" Value="Navy" />
        </Style>
    </Charting:AreaSeries.DataPointStyle>
</Charting:AreaSeries>
<Charting:Chart.Axes>
    <Charting:LinearAxis Orientation="Y" ShowGridLines="False" 
            Visibility="Hidden" Width="0" />
    <Charting:DateTimeAxis Orientation="X" Visibility="Hidden" 
            Height="0"></Charting:DateTimeAxis>
</Charting:Chart.Axes>

To change colors, remove the chart legend and axis labels. I've overridden the control as follows:

XML
<ControlTemplate TargetType="{x:Type Charting:Chart}">
    <Border Background="{TemplateBinding Background}" 
            BorderBrush="{TemplateBinding BorderBrush}" 
            BorderThickness="{TemplateBinding BorderThickness}" Padding="0">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
            </Grid.RowDefinitions>
            <chartingprimitives:EdgePanel Name="ChartArea" 
                   Style="{TemplateBinding ChartAreaStyle}" 
                   Grid.Row="0" Margin="0">
                <Grid Panel.ZIndex="-1" Style="{TemplateBinding PlotAreaStyle}" />
                <Border Panel.ZIndex="10" BorderBrush="#FF919191" BorderThickness="0" />
            </chartingprimitives:EdgePanel>
        </Grid>
    </Border>
</ControlTemplate>

Once you have the chart looking the way you like in XAML, you can create a dispatch timer to update its bound datasource. In this project, I bind the chart to a RingBuffer<T> implementation originally created by Florian Reischl: http://florianreischl.blogspot.com/2010/01/generic-c-ringbuffer.html.

I modified it slightly so that it implements INotifyPropertyChanged. This was so that it can can fire notification events to any bound WPF component. I first create an object that represents a data point, called MemorySample:

C#
public class MemorySample
{
    public long ByteCount { get; set; }
    public DateTime Timestamp { get; set; }
}

Then we create a RingBuffer with a capacity for 60 of these items (1 minute of memory data). We start a dispatch timer to trigger once per second:

C#
public void InitMemoryWatch()
{
    // keep 60 seconds worth of memory by default
    const int memorySamples = 60;
    MemoryStats = new RingBuffer<MemorySample>(memorySamples);

    var dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
    dispatcherTimer.Tick += DispatcherTimerTick;
    dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
    dispatcherTimer.Start();
}

In the handler, I generate a new memory sample and add it to the RingBuffer:

C#
private void DispatcherTimerTick(object sender, EventArgs e)
{
    LatestMemorySample = new MemorySample
    {
        ByteCount = GC.GetTotalMemory(false),
        Timestamp = DateTime.Now
    };
    _memoryStats.Add(LatestMemorySample);
}

Finally, I create binding properties for all of these data structures:

C#
private RingBuffer<MemorySample> _memoryStats;
public RingBuffer<MemorySample> MemoryStats
{
    get { return _memoryStats; }
    set
    {
        _memoryStats = value;
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs("MemoryStats"));
        }
    }
}
private MemorySample _latestMemorySample;
public MemorySample LatestMemorySample
{
    get { return _latestMemorySample; }
    set
    {
        _latestMemorySample = value;
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs("LatestMemorySample"));
        }
    }
}

Now I bind the chart to these properties:

XML
<Charting:AreaSeries VerticalAlignment="Stretch" 
                    HorizontalAlignment="Stretch" 
                    ItemsSource="{Binding Path=MemoryStats}" 
                    IndependentValuePath="Timestamp" 
                    DependentValuePath="ByteCount">

Warning

The present version of the WPF Toolkit charting package has a big memory leak which is addressed in the following article: http://wpf.codeplex.com/discussions/216609.

You must get a later version after this leak is fixed in order to use the memory chart in a production application (or you can manually patch the WPF Toolkit code yourself as the author describes).

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionthere are two issues in this sample Pin
alex_geng11-Dec-12 15:06
alex_geng11-Dec-12 15:06 
1. you use DispatcherTimer which is runing on same thread with UI to update the view model, this avoid the begininvoke calls but will freeze UI a little bit when the timer tick.

2. the ringbuffer collection has serious performance issue, even you set the capacity only 60 in the code, you can still feel the delay. why not just keep things simple? like below, from my test, this implementation is much faster than the one you are using.

C#
public class RingBuffer<T> : ObservableCollection<T>
{
    public RingBuffer(int capacity)
    {
        // validate capacity
        if (capacity <= 0)
            throw new ArgumentException("Must be greater than zero", "capacity");

        Capacity = capacity;
    }

    public void Enqueue(T item)
    {
        if (this.Count >= Capacity)
            this.RemoveAt(0);
        this.Add(item);
    }

    public int Capacity { get; private set; }
}

AnswerRe: there are two issues in this sample Pin
Ismail Degani3-Nov-13 11:56
Ismail Degani3-Nov-13 11:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.