John M. Henreick John M. Henreick - 3 months ago 8
C# Question

How come my label isnt updating its content? WPF .NET C#

Im currently messing around with a timer and a label and a performance counter because I am trying to make a label update its content to what ever the CPU usage is at the moment (and keeps updating at a certain interval)

But for some reason it just shows that my CPU usage is at 0% and its not updating.

What is causing this bug.

public void timerControl()
{

DispatcherTimer dtClockTime = new DispatcherTimer();

dtClockTime.Interval = new TimeSpan(0, 0, 1); //in Hour, Minutes, Second.
dtClockTime.Tick += dtClockTime_Tick;
dtClockTime.IsEnabled = true;
dtClockTime.Start();

}

private void dtClockTime_Tick(object sender, EventArgs e)
{
getCPUInfo();
}

public void getCPUInfo()
{

PerformanceCounter cpuCounter;
cpuCounter = new PerformanceCounter();
cpuCounter.CategoryName = "Processor";
cpuCounter.CounterName = "% Processor Time";
cpuCounter.InstanceName = "_Total";
// Get Current Cpu Usage
string currentCpuUsage =
cpuCounter.NextValue() + "%";
//Print it to the current label
CPUUsage.Content = currentCpuUsage;
}


XAML

<Page x:Class="Hawk_PC.systemPage"
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:local="clr-namespace:Hawk_PC"
mc:Ignorable="d"
Background="#03a3d2"
d:DesignHeight="350" d:DesignWidth="525"
Title="systemPage">


<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Label x:Name="osInfoLabel" Content="osInfoFriendlyName" HorizontalAlignment="Left" Margin="-206,-76,0,0" VerticalAlignment="Top"/>
<Image x:Name="image" HorizontalAlignment="Left" Height="83" Margin="-258,94,0,-119" VerticalAlignment="Top" Width="100" Source="Images/Logo.png"/>
<Label x:Name="osInfo" Content="OS:" HorizontalAlignment="Left" Margin="-234,-76,0,0" VerticalAlignment="Top"/>
<Label x:Name="ramInfo" Content="RAM:" HorizontalAlignment="Left" Margin="-234,-50,0,0" VerticalAlignment="Top"/>
<Label x:Name="hddInfo" Content="HDD Storage:" HorizontalAlignment="Left" Margin="-234,2,0,0" VerticalAlignment="Top"/>
<Label x:Name="systemHealth" Content="System Diagnose:" HorizontalAlignment="Left" Margin="-234,28,0,0" VerticalAlignment="Top"/>
<Label x:Name="cpuInfo" Content="CPU:" HorizontalAlignment="Left" Margin="-234,-24,0,0" VerticalAlignment="Top"/>
<Label x:Name="CPUUsage" Content="Label" HorizontalAlignment="Left" Margin="-190,-24,0,0" VerticalAlignment="Top"/>
<Button x:Name="startScanButton" Click="startScanButton_Click" Background="Transparent" BorderThickness="0" Content="Start Diagnose" HorizontalAlignment="Left" Margin="-258,55,0,-10" VerticalAlignment="Top" Width="137" Height="31"/>


</Grid>
</Page>

Answer

It appears that the method you're using might need a second call to NextValue() and a pause between two calls to get it to work, based on https://blogs.msdn.microsoft.com/bclteam/2006/06/02/how-to-read-performance-counters-ryan-byington/ and comments in this SO post: How to get the CPU Usage in C#?

So, you'd modify your getCPUInfo() method to look something like this:

public void getCPUInfo()
{

    PerformanceCounter cpuCounter;
    cpuCounter = new PerformanceCounter();
    cpuCounter.CategoryName = "Processor";
    cpuCounter.CounterName = "% Processor Time";
    cpuCounter.InstanceName = "_Total";
    // Get Current Cpu Usage
    cpuCounter.NextValue();
    // Sleep
    Thread.Sleep(1000);
    // Get Current Cpu Usage again
    string currentCpuUsage = cpuCounter.NextValue() + "%";
    //Print it to the current label
    CPUUsage.Content = currentCpuUsage;
}

Obviously, I hope that you understand that using Thread.Sleep() in a UI-centric program is a bad thing, so I'd use tasks and delays if you were to go with this approach (I just don't know how familiar you are with TPL, so I didn't want to bring in any further confusion).

Here's the demo of it working:

enter image description here

With that being said, you can solve everything by simply doing something like this:

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Threading;

namespace TestApp
{
    public partial class MainWindow : Window
    {
        PerformanceCounter cpuCounter;
        DispatcherTimer dtClockTime;

        public MainWindow()
        {
            InitializeComponent();
            this.Loaded += MainWindow_Loaded;
        }

        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            InitializeCpuPerformanceCounter();
            InitializeDispatcherTimer();
        }

        void InitializeDispatcherTimer()
        {
            dtClockTime = new DispatcherTimer();
            dtClockTime.Interval = new TimeSpan(0, 0, 1); //in Hour, Minutes, Second.
            dtClockTime.Tick += dtClockTime_Tick;
            dtClockTime.IsEnabled = true;
            dtClockTime.Start();
        }

        void InitializeCpuPerformanceCounter()
        {
            cpuCounter = new PerformanceCounter();
            cpuCounter.CategoryName = "Processor";
            cpuCounter.CounterName = "% Processor Time";
            cpuCounter.InstanceName = "_Total";
        }

        private void dtClockTime_Tick(object sender, EventArgs e)
        {
            getCPUInfo();
        }

        public void getCPUInfo()
        {
            CPUUsage.Content = cpuCounter.NextValue() + "%";
        }
    }
}

Here's a demo:

enter image description here

It's working due to the fact that I am not instantiating a new performance counter on every tick and since the timer has a one second interval, that causes the needed delay between each NextValue() call. I store both the PerformanceCounter and the DispatcherTimer as fields and initialize only once. So, this is a much better approach.

By the way, the XAML code for both tests is incredibly simple:

<Window x:Class="TestApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="200" Width="525">
    <Grid>
        <Label x:Name="CPUUsage" HorizontalAlignment="Center" VerticalAlignment="Center"/>
    </Grid>
</Window>

EDIT:

Per your request in the comments, here's something that would make use of async and await if you were to stay with the original approach. This will prevent the UI from freezing up for one second every other second, as it would do with the Thread.Sleep() version:

public async Task getCPUInfo()
{
    PerformanceCounter cpuCounter;
    cpuCounter = new PerformanceCounter();
    cpuCounter.CategoryName = "Processor";
    cpuCounter.CounterName = "% Processor Time";
    cpuCounter.InstanceName = "_Total";
    // Get Current Cpu Usage
    cpuCounter.NextValue();
    // Delay
    await Task.Delay(1000);
    // Get Current Cpu Usage again
    string currentCpuUsage = cpuCounter.NextValue() + "%";
    //Print it to the current label
    CPUUsage.Content = currentCpuUsage;
}

You may find more information on TPL on MSDN:

https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx

Additionally, I can't recommend Stephen Cleary's blog enough. It's a gem. Here's an article on async and await:

http://blog.stephencleary.com/2012/02/async-and-await.html

He has a lot of other articles as well and his book is top notch (although, if you can't afford it, all the content in that book is throughout his blog -- he's a good guy).

Comments