Исключение: «System.InvalidOperationException» в WindowsBase.dll - PullRequest
0 голосов
/ 06 мая 2018

У меня проблемы с обновлением пользовательского интерфейса WPF. Та же самая часть кода, которая вызывает исключение, прекрасно работает в другой функции. Теперь, по какой-то причине, я не могу понять, что это не только исключение, но и обновление не так, как я хочу. Я хочу, чтобы элементы пользовательского интерфейса в функции OnCPUDetEvent() обновлялись в зависимости от установленного таймера.

Вот мой код:

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";
        }
    }


}

Вот 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>

Ответы [ 2 ]

0 голосов
/ 13 мая 2018

В моем предыдущем ответе я упоминал, что нет единого решения этого вопроса. Вот еще один, который намного проще, чем предыдущий. Просто помните, что у каждого решения есть свои преимущества и недостатки.


В этом решении используются Threading методы самого класса Windows. Подпроцессы связываются с GUI с помощью метода диспетчера, который в противном случае не разрешен системой. Диспетчер используется для получения / установки свойств элементов графического интерфейса в сторонних методах. Недостатком является то, что вам нужно писать новую строку диспетчера для каждой отдельной / независимой работы, которую вы делаете.

Решение очень простое:

Сначала добавьте пространство имен для Threading методов класса Windows.

using System.Windows.Threading;

Затем создайте Action. Для этого просто оберните ваши фрагменты кода getter / setter, связанные с GUI, в новое определение 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                
    );
0 голосов
/ 06 мая 2018

Полученная ошибка является результатом ошибки «Отказано в доступе». Ваша функция начинает свою жизнь в потоке, отличном от самого GUI, в результате чего ей не разрешено изменять элементы GUI.

Это описано по этой ссылке: Метод, выполняющийся в потоке без пользовательского интерфейса, обновляет пользовательский интерфейс

Выше ссылка дает методы, чтобы убежать от проблемы. Тем не менее, нет единого решения для вашей проблемы. Следующее - только один из них. Он делает пошаговые изменения, которые вам нужно применить. (РЕДАКТИРОВАТЬ: я добавил другое решение в следующем ответе)


Добавьте using System.ComponentModel; к своим заголовкам. Это использовать связки с изменяющимися свойствами. При необходимости добавьте справочный файл dll.

Теперь используйте этот заголовок для вашего класса:

public partial class CPUBase : Page,INotifyPropertyChanged

Следующий шаг - добавление уведомителей. Код используется как для .NET v4.0, обратитесь к Интерфейс INotifyPropertyChanged как использовать для других версий.

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, я пытался уменьшить частоту процессора, и результат не изменился)

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}" ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...