John M. Henreick
John M. Henreick

Reputation: 53

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>

Upvotes: 1

Views: 119

Answers (2)

G K
G K

Reputation: 2501

Replace the code in getCPUInfo() method with the below code,

    /// <summary>
    /// 
    /// </summary>
    public void getCPUInfo()
    {
        PerformanceCounter cpuCounter;
        cpuCounter = new PerformanceCounter();
        cpuCounter.CategoryName = "Processor";
        cpuCounter.CounterName = "% Processor Time";
        cpuCounter.InstanceName = "_Total";
        // Get Current Cpu Usage

        cpuCounter.NextValue(); // Added
        System.Threading.Thread.Sleep(1000); // Added
        // now matches task manager reading 
        float secondValue = cpuCounter.NextValue(); // Added

        string currentCpuUsage = secondValue + "%";
        //Print it to the current label
        CPUUsage.Content = currentCpuUsage;
    }

And hope your problem will be resolved.

Upvotes: 1

B.K.
B.K.

Reputation: 10152

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).

Upvotes: 2

Related Questions