抛出异常:WindowsBase.dll 中的“System.InvalidOperationException”

问题描述 投票:0回答:2

我在 WPF UI 中更新某些内容时遇到问题。引发异常的代码的同一部分在其他函数中运行得很好。现在,由于某种原因,我无法弄清楚它不仅抛出异常,而且也没有像我想要的那样更新。我希望

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>
c# .net wpf xaml
2个回答
6
投票

您收到的错误是“访问被拒绝”错误的结果。您的函数在与 GUI 本身不同的线程中开始其生命周期,因此不允许更改 GUI 元素。

在此链接中进行了描述:在非 UI 线程上运行的方法更新 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
是为了显示它正在工作,因为值不会立即显示更改)(编辑:添加有效,但我不认为代码变得 active
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}" ...

2
投票

在我之前的回答中,我提到这个问题没有单一的解决方案。这是另一种比前一种简单得多的方法。请记住,每种不同的解决方案都有其优点和缺点。


此解决方案使用

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                
    );
© www.soinside.com 2019 - 2024. All rights reserved.