wpf 文本框内容在加载时冻结 ui
wpf textbox content freezes ui on load
我开始编写我的第一个 WPF 应用程序,但我在使用显示一些系统信息(cpu、内存、磁盘使用情况、mac 地址等)的文本框时遇到了问题。
我的应用程序在两个页面之间进行导航,并且所述文本框位于其中一个页面上。文本框的内容通过 WMI 查询检索。
我注意到的问题是,在使用文本框导航到该页面时,它会冻结 UI 大约两秒钟,然后再转到并显示该页面。
我是新手,我最好的猜测是八个 WMI 查询(也可能编码错误)正在这样做,或者我错误地将内容加载到文本框中。
我的查询是如何构建的示例
public string getCPU()
{
ManagementObjectSearcher searcher = new
ManagementObjectSearcher("root\CIMV2", "SELECT * FROM Win32_Processor");
StringBuilder sb = new StringBuilder();
foreach (ManagementObject wmi in searcher.Get())
{
try
{
sb.Append("Processor: " + wmi.GetPropertyValue("Name").ToString() + Environment.NewLine);
}
catch
{
return sb.ToString();
}
}
return sb.ToString();
}
public string getRAMsize()
{
ManagementClass mc = new ManagementClass("Win32_ComputerSystem");
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject item in moc)
{
return Convert.ToString(Math.Round(Convert.ToDouble(item.Properties["TotalPhysicalMemory"].Value) / (1024 * 1024 * 1024), 0)) + " GB";
}
return "RAMsize";
}
这就是我用来检索文本框中数据的方法:
private void TextBox1_Loaded(object sender, RoutedEventArgs e)
{
TextBox1.Text = getCPU();
TextBox1.Text += "Memory: " + getRAMsize() + Environment.NewLine;
TextBox1.Text += "Free Space: " + GetTotalFreeSpace(sysdrive) + " GB" + Environment.NewLine;
if (Is64BitSystem)
{
TextBox1.Text += getOS() + " 64bit" + Environment.NewLine;
}
else
{
TextBox1.Text += getOS() + " 32 Bit" + Environment.NewLine;
}
TextBox1.Text += "MAC Address : " + System.Text.RegularExpressions.Regex.Replace(GetMacAddress().ToString(), ".{2}", "[=12=] ") + Environment.NewLine;
TextBox1.Text += av();
}
我的问题是我做错了什么,我该如何解决。在我看来,如果查询构造正确,那是因为每次加载文本框时(在导航或启动时)它们都会一次又一次地完成,也许如果我能让它只加载一次并记住这些值(因为大多数数据应该保持不变)。
但正如我所说,我是菜鸟,我们将不胜感激任何帮助。
提前致谢
您正在 UI 话题中获取数据。
您应该在后台线程中加载数据,然后,因为您使用 wpf,所以将文本框文本绑定到一个变量。
示例(此代码已准备好复制粘贴):
// INotifyPropertyChanged is an Interface which enables binding of Properties in your window
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _systemInformation;
// Stub (works actually)
private bool is64BitSystem = (IntPtr.Size == 8);
// Stub
private string sysdrive = "YOLO\:";
public MainWindow()
{
InitializeComponent();
// Set Datacontext normally set to a viewmodel but ther is none in this code
this.DataContext = this;
// The Backgroundworker does things in the background (nonblocking)
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += DoItButDontInterruptMeDuh;
bw.RunWorkerAsync();
}
public event PropertyChangedEventHandler PropertyChanged;
public string SystemInformation { get => _systemInformation; set => _systemInformation = value; }
//Stub
public string getCPU()
{
return "Fancy CPU";
}
//Stub
public string getRAMsize()
{
return "1 PB";
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
//Stub
private string av()
{
return "Whatever av means.";
}
private void DoItButDontInterruptMeDuh(object sender, DoWorkEventArgs e)
{
// Simulate loading time
Thread.Sleep(5000);
SystemInformation = getCPU();
SystemInformation += "Memory: " + getRAMsize() + Environment.NewLine;
SystemInformation += "Free Space: " + GetTotalFreeSpace(sysdrive) + " GB" + Environment.NewLine;
if (is64BitSystem)
{
SystemInformation += getOS() + " 64bit" + Environment.NewLine;
}
else
{
SystemInformation += getOS() + " 32 Bit" + Environment.NewLine;
}
SystemInformation += "MAC Address : " + System.Text.RegularExpressions.Regex.Replace(GetMacAddress().ToString(), ".{2}", "[=10=] ") + Environment.NewLine;
SystemInformation += av();
OnPropertyChanged("SystemInformation");
}
//Stub
private object GetMacAddress()
{
return "Macintoshstreet 1234";
}
//Stub
private string getOS()
{
return "Cool OS";
}
//Stub
private string GetTotalFreeSpace(object sysdrive)
{
return "0";
}
}
并在 .xaml:
<TextBox Text={Binding Path=SystemInformation}/>
在检查了每一个查询之后,我发现 getCPU 导致加载延迟。
我用 Registry.GetValue 替换了它,速度非常快。
感谢 Sebastian L,因为虽然他的代码对我不起作用,但它让我走上了正确的道路,而且我能够使用后台工作者调整自己的代码以避免 UI 任何形式的冻结
我的工作代码:
private void TextBox1_Loaded(object sender, RoutedEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(delegate (object o, DoWorkEventArgs args)
{
});
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate (object o, RunWorkerCompletedEventArgs args)
{
TextBox1.Text = (string)Registry.GetValue(@"HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor[=10=]", "ProcessorNameString", null) + Environment.NewLine;
TextBox1.Text += "Memory: " + getRAMsize() + Environment.NewLine;
TextBox1.Text += "Free Space: " + GetTotalFreeSpace(sysdrive) + " GB" + Environment.NewLine;
if (Is64BitSystem)
{
TextBox1.Text += getOS() + " 64bit" + Environment.NewLine;
}
else
{
TextBox1.Text += getOS() + " 32 Bit" + Environment.NewLine;
}
TextBox1.Text += "MAC Address : " + System.Text.RegularExpressions.Regex.Replace(GetMacAddress().ToString(), ".{2}", "[=10=] ") + Environment.NewLine;
TextBox1.Text += av();
});
bw.RunWorkerAsync();
}
我开始编写我的第一个 WPF 应用程序,但我在使用显示一些系统信息(cpu、内存、磁盘使用情况、mac 地址等)的文本框时遇到了问题。 我的应用程序在两个页面之间进行导航,并且所述文本框位于其中一个页面上。文本框的内容通过 WMI 查询检索。 我注意到的问题是,在使用文本框导航到该页面时,它会冻结 UI 大约两秒钟,然后再转到并显示该页面。 我是新手,我最好的猜测是八个 WMI 查询(也可能编码错误)正在这样做,或者我错误地将内容加载到文本框中。 我的查询是如何构建的示例
public string getCPU()
{
ManagementObjectSearcher searcher = new
ManagementObjectSearcher("root\CIMV2", "SELECT * FROM Win32_Processor");
StringBuilder sb = new StringBuilder();
foreach (ManagementObject wmi in searcher.Get())
{
try
{
sb.Append("Processor: " + wmi.GetPropertyValue("Name").ToString() + Environment.NewLine);
}
catch
{
return sb.ToString();
}
}
return sb.ToString();
}
public string getRAMsize()
{
ManagementClass mc = new ManagementClass("Win32_ComputerSystem");
ManagementObjectCollection moc = mc.GetInstances();
foreach (ManagementObject item in moc)
{
return Convert.ToString(Math.Round(Convert.ToDouble(item.Properties["TotalPhysicalMemory"].Value) / (1024 * 1024 * 1024), 0)) + " GB";
}
return "RAMsize";
}
这就是我用来检索文本框中数据的方法:
private void TextBox1_Loaded(object sender, RoutedEventArgs e)
{
TextBox1.Text = getCPU();
TextBox1.Text += "Memory: " + getRAMsize() + Environment.NewLine;
TextBox1.Text += "Free Space: " + GetTotalFreeSpace(sysdrive) + " GB" + Environment.NewLine;
if (Is64BitSystem)
{
TextBox1.Text += getOS() + " 64bit" + Environment.NewLine;
}
else
{
TextBox1.Text += getOS() + " 32 Bit" + Environment.NewLine;
}
TextBox1.Text += "MAC Address : " + System.Text.RegularExpressions.Regex.Replace(GetMacAddress().ToString(), ".{2}", "[=12=] ") + Environment.NewLine;
TextBox1.Text += av();
}
我的问题是我做错了什么,我该如何解决。在我看来,如果查询构造正确,那是因为每次加载文本框时(在导航或启动时)它们都会一次又一次地完成,也许如果我能让它只加载一次并记住这些值(因为大多数数据应该保持不变)。 但正如我所说,我是菜鸟,我们将不胜感激任何帮助。 提前致谢
您正在 UI 话题中获取数据。
您应该在后台线程中加载数据,然后,因为您使用 wpf,所以将文本框文本绑定到一个变量。
示例(此代码已准备好复制粘贴):
// INotifyPropertyChanged is an Interface which enables binding of Properties in your window
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _systemInformation;
// Stub (works actually)
private bool is64BitSystem = (IntPtr.Size == 8);
// Stub
private string sysdrive = "YOLO\:";
public MainWindow()
{
InitializeComponent();
// Set Datacontext normally set to a viewmodel but ther is none in this code
this.DataContext = this;
// The Backgroundworker does things in the background (nonblocking)
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += DoItButDontInterruptMeDuh;
bw.RunWorkerAsync();
}
public event PropertyChangedEventHandler PropertyChanged;
public string SystemInformation { get => _systemInformation; set => _systemInformation = value; }
//Stub
public string getCPU()
{
return "Fancy CPU";
}
//Stub
public string getRAMsize()
{
return "1 PB";
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
//Stub
private string av()
{
return "Whatever av means.";
}
private void DoItButDontInterruptMeDuh(object sender, DoWorkEventArgs e)
{
// Simulate loading time
Thread.Sleep(5000);
SystemInformation = getCPU();
SystemInformation += "Memory: " + getRAMsize() + Environment.NewLine;
SystemInformation += "Free Space: " + GetTotalFreeSpace(sysdrive) + " GB" + Environment.NewLine;
if (is64BitSystem)
{
SystemInformation += getOS() + " 64bit" + Environment.NewLine;
}
else
{
SystemInformation += getOS() + " 32 Bit" + Environment.NewLine;
}
SystemInformation += "MAC Address : " + System.Text.RegularExpressions.Regex.Replace(GetMacAddress().ToString(), ".{2}", "[=10=] ") + Environment.NewLine;
SystemInformation += av();
OnPropertyChanged("SystemInformation");
}
//Stub
private object GetMacAddress()
{
return "Macintoshstreet 1234";
}
//Stub
private string getOS()
{
return "Cool OS";
}
//Stub
private string GetTotalFreeSpace(object sysdrive)
{
return "0";
}
}
并在 .xaml:
<TextBox Text={Binding Path=SystemInformation}/>
在检查了每一个查询之后,我发现 getCPU 导致加载延迟。 我用 Registry.GetValue 替换了它,速度非常快。 感谢 Sebastian L,因为虽然他的代码对我不起作用,但它让我走上了正确的道路,而且我能够使用后台工作者调整自己的代码以避免 UI 任何形式的冻结 我的工作代码:
private void TextBox1_Loaded(object sender, RoutedEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(delegate (object o, DoWorkEventArgs args)
{
});
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate (object o, RunWorkerCompletedEventArgs args)
{
TextBox1.Text = (string)Registry.GetValue(@"HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\System\CentralProcessor[=10=]", "ProcessorNameString", null) + Environment.NewLine;
TextBox1.Text += "Memory: " + getRAMsize() + Environment.NewLine;
TextBox1.Text += "Free Space: " + GetTotalFreeSpace(sysdrive) + " GB" + Environment.NewLine;
if (Is64BitSystem)
{
TextBox1.Text += getOS() + " 64bit" + Environment.NewLine;
}
else
{
TextBox1.Text += getOS() + " 32 Bit" + Environment.NewLine;
}
TextBox1.Text += "MAC Address : " + System.Text.RegularExpressions.Regex.Replace(GetMacAddress().ToString(), ".{2}", "[=10=] ") + Environment.NewLine;
TextBox1.Text += av();
});
bw.RunWorkerAsync();
}