如何在 WPF 应用程序中使用 BusyIndicator 和 Powershell 脚本执行
How to use BusyIndicator with Powershell script execution in WPF application
我最初没有计划实施 BusyIndicator,但我意识到我的 Powershell 脚本需要一些时间来执行,这可能会导致用户混淆。似乎没有任何教程展示如何在 C# 中将 BusyIndicator 与 Powershell 脚本一起使用。 This 是一个很棒的教程,因为它是由 WPF Extended Toolkit 的作者编写的,但它不是我需要的。
这是我的资料。为简洁起见,代码被截断:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void imageBtn_Click(object sender, RoutedEventArgs e)
{
Button imgBtn = sender as Button;
string appToCheck = GetSubString(imgBtn.Name);
switch(appToCheck)
{
case "weather":
InvokePowerShell(appToCheck);
break;
case "news":
//DO THE SAME FOR ALL CASES
break;
//17 more cases follow
}
}
private string GetSubString(string buttonName)
{
int index = buttonName.IndexOf('B');
return buttonName.Substring(0, index);
}
private void InvokePowerShell(string str)
{
str = char.ToUpper(str[0]) + str.Substring(1);
PowerShell ps = PowerShell.Create();
ps.AddScript("Get-AppxPackage | Select Name | Where-Object ($_.Name -like '*" + str + "*'}");
_busyIndicator.IsBusy = true;
ps.BeginInvoke<PSObject>(null, new PSInvocationSettings(), ar =>
{
try
{
var psOutput = ps.EndInvoke(ar);
this.Dispatcher.Invoke(() => _busyIndicator.IsBusy = false);
foreach (PSObject item in psOutput)
{
if (item.Equals(String.Empty))
{
MessageBox.Show(str + " is not installed so cannot be removed.");
}
else
{
if (MessageBox.Show("This cannot be undone.\nContinue?", "Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.No)
{
//DO NOTHING
}
else
{
//TODO Remove the app
MessageBox.Show(str + " successfully removed.");
}
}
}
}
finally
{
//dispose of it
ps.Dispose();
}
}, null);
}
}
}
我已经在 XAML 中设置了 BusyIndicator:
<toolkit:BusyIndicator x:Name="_busyIndicator" IsBusy="False" BusyContent="One moment....">
<!-- Insert the rest of my markup here -->
</toolkit:BusyIndicator>
我还会有一个更长的方法来删除应用程序中列出的所有内容,所以我肯定会想要这个指示器。我尝试按照上面 link 中给出的教程进行操作,但我 运行 遇到了 foreach 循环超出范围的问题。
我尝试使用异步 BeginInvoke() 方法无济于事。繁忙指示器一直在运行,如下所示:
PSDataCollection<PSObject> outputCollection = new PSDataCollection<PSObject>();
IAsyncResult result = PowerShellInstance.BeginInvoke<PSObject>(outputCollection);
while (result.IsCompleted == false)
{
_busyIndicator.IsBusy = true;
}
//Then my foreach loop shown above with the if statements and MessageBox notifications
我对此很陌生。任何帮助将不胜感激。
您可以 运行 分离线程上的代码(使用 Task.Run
或类似的构造),或者像这样使用 BeginInvoke
:
private void InvokePowerShell(string str) {
str = char.ToUpper(str[0]) + str.Substring(1);
// remove using, or move to a field
PowerShell ps = PowerShell.Create();
ps.AddScript("Get-AppxPackage | Select Name | Where-Object {$_.Name -like '*" + str + "*'}");
//This is where the app pauses slightly and I need a busyindicator
_busyIndicator.IsBusy = true;
ps.BeginInvoke<PSObject>(null, new PSInvocationSettings(), ar => {
try {
var psOutput = ps.EndInvoke(ar);
// note, you are not on UI thread here
this.Dispatcher.Invoke(() => _busyIndicator.IsBusy = false);
// the rest of your code here
}
finally {
// if did not move to a field - dispose here
ps.Dispose();
}
}, null);
}
我最初没有计划实施 BusyIndicator,但我意识到我的 Powershell 脚本需要一些时间来执行,这可能会导致用户混淆。似乎没有任何教程展示如何在 C# 中将 BusyIndicator 与 Powershell 脚本一起使用。 This 是一个很棒的教程,因为它是由 WPF Extended Toolkit 的作者编写的,但它不是我需要的。
这是我的资料。为简洁起见,代码被截断:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void imageBtn_Click(object sender, RoutedEventArgs e)
{
Button imgBtn = sender as Button;
string appToCheck = GetSubString(imgBtn.Name);
switch(appToCheck)
{
case "weather":
InvokePowerShell(appToCheck);
break;
case "news":
//DO THE SAME FOR ALL CASES
break;
//17 more cases follow
}
}
private string GetSubString(string buttonName)
{
int index = buttonName.IndexOf('B');
return buttonName.Substring(0, index);
}
private void InvokePowerShell(string str)
{
str = char.ToUpper(str[0]) + str.Substring(1);
PowerShell ps = PowerShell.Create();
ps.AddScript("Get-AppxPackage | Select Name | Where-Object ($_.Name -like '*" + str + "*'}");
_busyIndicator.IsBusy = true;
ps.BeginInvoke<PSObject>(null, new PSInvocationSettings(), ar =>
{
try
{
var psOutput = ps.EndInvoke(ar);
this.Dispatcher.Invoke(() => _busyIndicator.IsBusy = false);
foreach (PSObject item in psOutput)
{
if (item.Equals(String.Empty))
{
MessageBox.Show(str + " is not installed so cannot be removed.");
}
else
{
if (MessageBox.Show("This cannot be undone.\nContinue?", "Warning", MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.No)
{
//DO NOTHING
}
else
{
//TODO Remove the app
MessageBox.Show(str + " successfully removed.");
}
}
}
}
finally
{
//dispose of it
ps.Dispose();
}
}, null);
}
}
}
我已经在 XAML 中设置了 BusyIndicator:
<toolkit:BusyIndicator x:Name="_busyIndicator" IsBusy="False" BusyContent="One moment....">
<!-- Insert the rest of my markup here -->
</toolkit:BusyIndicator>
我还会有一个更长的方法来删除应用程序中列出的所有内容,所以我肯定会想要这个指示器。我尝试按照上面 link 中给出的教程进行操作,但我 运行 遇到了 foreach 循环超出范围的问题。
我尝试使用异步 BeginInvoke() 方法无济于事。繁忙指示器一直在运行,如下所示:
PSDataCollection<PSObject> outputCollection = new PSDataCollection<PSObject>();
IAsyncResult result = PowerShellInstance.BeginInvoke<PSObject>(outputCollection);
while (result.IsCompleted == false)
{
_busyIndicator.IsBusy = true;
}
//Then my foreach loop shown above with the if statements and MessageBox notifications
我对此很陌生。任何帮助将不胜感激。
您可以 运行 分离线程上的代码(使用 Task.Run
或类似的构造),或者像这样使用 BeginInvoke
:
private void InvokePowerShell(string str) {
str = char.ToUpper(str[0]) + str.Substring(1);
// remove using, or move to a field
PowerShell ps = PowerShell.Create();
ps.AddScript("Get-AppxPackage | Select Name | Where-Object {$_.Name -like '*" + str + "*'}");
//This is where the app pauses slightly and I need a busyindicator
_busyIndicator.IsBusy = true;
ps.BeginInvoke<PSObject>(null, new PSInvocationSettings(), ar => {
try {
var psOutput = ps.EndInvoke(ar);
// note, you are not on UI thread here
this.Dispatcher.Invoke(() => _busyIndicator.IsBusy = false);
// the rest of your code here
}
finally {
// if did not move to a field - dispose here
ps.Dispose();
}
}, null);
}