我在WPF用户界面中遇到更新问题。抛出异常的代码的相同部分在另一个函数中工作得很好。现在,出于某种原因,我无法弄清楚它不仅会抛出异常,而且也不会像我想要的那样更新。我希望OnCPUDetEvent()
函数中的UI元素根据我设置的计时器进行更新。
这是我的代码:
using System;
using System.Collections.Generic;
using System.Management.Instrumentation;
using System.Management;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Runtime.Serialization;
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;
namespace HWDetCS
{
/// <summary>
/// Interaction logic for CPUBase.xaml
/// </summary>
public partial class CPUBase : Page
{
// lists are better than arrays, fite me!
public List<string> names = new List<string>();
public List<string> values = new List<string>();
public int i = 0;
// Set up a timer to be enabled later
public Timer CPUDetRefreshTimer;
public CPUBase()
{
// Auto generated stuff, don't touch!
InitializeComponent();
// Actually run all the detection stuff
CPUDet();
// Start up the Timer, and get it ready
CPUDetRefreshTimer = new Timer();
CPUDetRefreshTimer.AutoReset = true;
CPUDetRefreshTimer.Interval = 500;
CPUDetRefreshTimer.Enabled = true;
CPUDetRefreshTimer.Elapsed += OnCPUDetEvent;
}
// This thing does all the work
public void CPUDet()
{
// Get the CPU Management class, this makes it the CPU we get info off of rather than nothing, because if it wasnt set to the CPU, it would error and break and cry a lot... dont change it.
ManagementClass CPUClass = new ManagementClass("Win32_Processor");
CPUClass.Options.UseAmendedQualifiers = true;
// Set up a data collection to get the data off of, this and the next thing SHOULD NEVER BE IN A LOOP! IT WILL BREAK YOUR CPU LIKE A BALLOON!
PropertyDataCollection dataCollection = CPUClass.Properties;
// Get the instance of the class, for some reason this is required to work, dont touch AND DONT PUT IT IN A LOOP WHY CANT YOU LISTEN!?
ManagementObjectCollection instanceCollection = CPUClass.GetInstances();
// This is a loop, its very fragile, dont touch it, it gets the list of data we are collecting
foreach (PropertyData property in dataCollection)
{
// adds the names into one nice readable-ish list!
names.Add(property.Name);
// loop through all the instances and grabs the actual data off of it
foreach (ManagementObject instance in instanceCollection)
{
// makes sure we dont get null reference errors, I HATE THOSE SO MUCH! I KNOW ITS NULL JUST SHUT UP!
if (instance.Properties[property.Name.ToString()].Value == null)
{
// if its null, dont add the actual property data, INSTEAD, add a string that says null so we know not to mess with it
values.Add("null");
}
else
{
// otherwise, go right ahead
values.Add(instance.Properties[property.Name.ToString()].Value.ToString());
}
}
// counting....
i++;
}
// Debug stuff, dont release uncommented!
// TODO: COMMENT THIS OUT!
for (int x = 0; x < names.Count - 1; x++)
{
Console.WriteLine(x.ToString());
Console.WriteLine(names[x]);
Console.WriteLine(values[x]);
}
// Get the name
CPUNameText.Content = values[29];
// Get the manufacturer
CPUManuText.Content = values[27];
// Get the number of CORES (NOT THREADS!)
CPUCoreCountText.Content = values[30];
// Get the Family
CPUFamilyText.Content = values[18];
}
public void OnCPUDetEvent(Object obj, ElapsedEventArgs args)
{
//Console.WriteLine("Event Fire!");
// Get the current clock speed
CPUClockSpeedText.Content = values[10] + "MHz";
// Get the current Voltage
CPUCVoltageText.Content = (Convert.ToDouble(values[11]) / 10).ToString() + " Volts";
}
}
}
以下是实际UI页面的XAML:
<Page x:Class="HWDetCS.CPUBase"
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:HWDetCS"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="CPUBase" MinWidth="1280" MinHeight="720">
<Grid Background="Gray">
<DockPanel>
<UniformGrid Rows="6" Columns="2">
<Label x:Name="CPUNameLabel" Content="CPU Name:" FontSize="36" FontWeight="Bold" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
<Label x:Name="CPUNameText" Content="Label" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="24" FontWeight="Bold" Foreground="#FF007ACC"/>
<Label x:Name="CPUManuLabel" Content="CPU Manufacturer:" FontSize="36" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontWeight="Bold" Foreground="#FF007ACC"/>
<Label x:Name="CPUManuText" Content="Label" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="24" FontWeight="Bold"/>
<Label x:Name="CPUClockSpeedLabel" Content="CPU Clock Speed:" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="36" FontWeight="Bold"/>
<Label x:Name="CPUClockSpeedText" Content="Label" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="24" FontWeight="Bold" Foreground="#FF007ACC"/>
<Label x:Name="CPUCoreCountLabel" Content="CPU Core Count:" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="36" FontWeight="Bold"/>
<Label x:Name="CPUCoreCountText" Content="Label" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="24" FontWeight="Bold"/>
<Label x:Name="CPUFamilyLabel" Content="CPU Family:" FontSize="36" FontWeight="Bold" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center"/>
<Label x:Name="CPUFamilyText" Content="Label" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="24" FontWeight="Bold"/>
<Label x:Name="CPUCVoltageLabel" Content="CPU Current Voltage:" FontSize="36" FontWeight="Bold" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" Foreground="#FF007ACC"/>
<Label x:Name="CPUCVoltageText" Content="Label" Foreground="#FF007ACC" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" FontSize="24" FontWeight="Bold"/>
</UniformGrid>
</DockPanel>
</Grid>
</Page>
答案 0 :(得分:1)
您收到的错误是“拒绝访问”错误的结果。您的函数在与GUI本身不同的线程中开始生命,因此不允许更改GUI元素。
此链接中描述了A method running on a non-UI thread updates the UI
上面的链接提供了远离问题的方法。但是,没有单一的解决方案可以解决您的问题。以下只是其中之一。它会逐步进行您需要应用的更改。 (编辑:我在下一个答案中添加了另一个解决方案)
将using System.ComponentModel;
添加到标头中。这是使用具有更改属性的绑定。如果需要,添加引用dll文件。
现在为您的班级使用此标题:
public partial class CPUBase : Page,INotifyPropertyChanged
下一步是添加通知程序。该代码用于.NET v4.0,请参考INotifyPropertyChanged Interface如何用于其他版本。
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
现在创建两个属性字段来保存您的更改值。有关其他版本,请参阅以上链接。
string speed;
public string Speed
{
get
{
return speed;
}
set
{
speed= value;
NotifyPropertyChanged(nameof(Speed));
}
}
string volt;
public string Volt
{
get
{
return volt;
}
set
{
volt = value;
NotifyPropertyChanged(nameof(Volt));
}
}
其余的现在要容易得多。只需更改函数内的这些属性(额外i
即表明它正在运行,因为值不会立即显示更改)(编辑:添加有效,但我不认为代码有效 values
,我试图降低CPU频率,结果没有变化)
Speed = values[10] + "MHz"+i;
Volt = (Convert.ToDouble(values[11]) / 10).ToString() + " Volts"+i;
i++;
现在为您提供页面名称,并通过绑定到这些属性来更新标签。
<Page x:Name="CPUBaseMain ...
...
<Label x:Name="CPUClockSpeedText" Content="{Binding Speed, ElementName=CPUBaseMain}" ...
<Label x:Name="CPUCVoltageText" Content="{Binding Volt, ElementName=CPUBaseMain}" ...
答案 1 :(得分:0)
在我之前的回答中,我提到这个问题没有单一的解决方案。这是另一个比前一个简单得多的一个。请记住,每种不同的解决方案都有其优点和缺点。
此解决方案使用Threading
类本身的Windows
方法。子进程使用调度程序方法与GUI进行通信,否则系统不允许这样做。调度程序用于在外部方法中获取/设置GUI元素的属性。缺点是您需要为每个单独/独立工作编写新的调度程序行。
解决方案非常简单:
首先,为Threading
类的Windows
方法添加命名空间。
using System.Windows.Threading;
然后,创建一个Action
。为此,只需将与GUI相关的getter / setter代码片段包装在新的Action
定义中。此操作可以在您的方法可以访问的任何位置定义。
Action dispatcherAction = new Action(() =>
{
// Get the current clock speed
CPUClockSpeedText.Content = values[10] + "MHz";
// Get the current Voltage
CPUCVoltageText.Content = (Convert.ToDouble(values[11]) / 10).ToString() + " Volts";
});
最后,使用OnCPUDetEvent
方法发送您的操作。就是这样。
this.Dispatcher.Invoke(dispatcherAction, DispatcherPriority.Normal);
如果需要调度的代码短几行,则无需在外部创建新操作。但是通过执行此操作,您可以将操作绑定到调用它的方法。
this.Dispatcher.Invoke(
new Action (() =>
{
// Get the current clock speed
CPUClockSpeedText.Content = values[10] + "MHz";
// Get the current Voltage
CPUCVoltageText.Content = (Convert.ToDouble(values[11]) / 10).ToString() + " Volts";
}),
DispatcherPriority.Normal
);