如何从异步方法打开 WPF window
How to open a WPF window from an async method
我一直在尝试为此寻找解决方案,但目前我发现的任何东西都没有成功。我从 WPF 开始,所以我可能只是错过了一些明显的答案,但我非常感谢愚蠢的答案。是这样的情况:
我的应用程序是使用 MVVM 构建的。它的主要功能是通过按钮调用的。一旦被调用,它将遍历指定目录中的所有文件并执行一些重命名。在执行此循环时,我想显示一个进度条,指示总数中有多少文件已重命名。
进度值是绑定到 Progressbar 控件的 属性
我只能异步更新进度条;否则它将保持为空,直到该过程完成。现在它与 BackgroundWorker.
一起工作
_bgWorker.DoWork += (s, e) =>
{
RenamingMethod();
};
...
RenamingMethodCommand = new RelayCommand(_ =>_bgWorker.RunWorkerAsync());
要执行重命名,文件名中应包含散列。如果找到此哈希值,将转换为用户输入的值,否则应显示 window 要求用户输入。
这个 window 无法在 MTAThread 中显示(我正在使用 ShowDialog,我还没有没有找到任何其他方法让它出现),所以我不能从我的 main window 中的异步方法调用它。我发现让它工作的唯一方法是调用 RenamingMethod 中的所有内容,如下所示:
public void RenamingMethod()
{
Application.Current.Dispatcher.Invoke((Action)delegate
{
//code for renaming
}
}
此时,进度条仅在我关闭弹出窗口时更新 window。然后它保持相同的值,直到打开和关闭新的 window。
我想从更新进度条的异步方法打开弹出window,所以这个演变很顺利,当打开一个新的window时它就停止了。或者能够同步更新进度条也可能会起作用。
下面是更详细的代码:
public void RenameMethod()
{
Application.Current.Dispatcher.Invoke((Action)delegate
{
IEnumerable<string> files = Directory.EnumerateFiles(
OriginMediaFolder,
"*.*",
SearchOption.AllDirectories).
Where(name => name.EndsWith(".jpg") || name.EndsWith(".mp4"));
int numberOfFiles = files.Count();
float fileNumber = 0;
foreach (string file in files)
{
MoveAndRenameFile(file);
ProgressValue = (int)(100 * (fileNumber++ / numberOfFiles));
}
});
}
public void MoveAndRenameFile(string file)
{
//Name structure: path/1234567891234567-hash.ext
string fileName = Regex.Match(file, @"[^\/\]+\.",RegexOptions.RightToLeft).
Value.TrimEnd('.');
string date = fileName.Substring(0, 16);
string hash = fileName.Substring(17);
if (!HashToName(hash, out string gameName))
{
var gameNamingWindow = GetNewNameFromUser(hash);
}
string extension = Regex.Match(file, @"\..+\Z", RegexOptions.RightToLeft).Value;
if (!Directory.Exists(DestinationMediaFolder + "/" + gameName))
Directory.CreateDirectory(DestinationMediaFolder + "/" + gameName);
File.Move(file, DestinationMediaFolder + "/" + gameName + "/" + date + extension);
}
public string GetNewNameFromUser(string hash)
{
var gameNamingWindow = new NewGameWindow.MainWindow(OriginMediaFolder, hash);
if (gameNamingWindow.ShowDialog().Value)
{
string gameName = gameNamingWindow.Result;
NameHashes.Add(new NameHash
{
GameName = gameName,
Hash = hash
});
return gameName;
}
return hash;
}
Invoke 调用切换到 GUI 线程,因此您基本上是在占用该线程来完成您的所有工作,并有效地否定了开始时使用工作线程的全部意义。更改 RenameMethod() 以便 Invoke 块内唯一的东西是实际需要的东西,即更新进度条的行。
更好的是,开始为您的 GUI 控件使用正确的数据绑定,这样您就根本不必为任务切换而烦恼。
我一直在尝试为此寻找解决方案,但目前我发现的任何东西都没有成功。我从 WPF 开始,所以我可能只是错过了一些明显的答案,但我非常感谢愚蠢的答案。是这样的情况:
我的应用程序是使用 MVVM 构建的。它的主要功能是通过按钮调用的。一旦被调用,它将遍历指定目录中的所有文件并执行一些重命名。在执行此循环时,我想显示一个进度条,指示总数中有多少文件已重命名。 进度值是绑定到 Progressbar 控件的 属性
我只能异步更新进度条;否则它将保持为空,直到该过程完成。现在它与 BackgroundWorker.
一起工作_bgWorker.DoWork += (s, e) =>
{
RenamingMethod();
};
...
RenamingMethodCommand = new RelayCommand(_ =>_bgWorker.RunWorkerAsync());
要执行重命名,文件名中应包含散列。如果找到此哈希值,将转换为用户输入的值,否则应显示 window 要求用户输入。
这个 window 无法在 MTAThread 中显示(我正在使用 ShowDialog,我还没有没有找到任何其他方法让它出现),所以我不能从我的 main window 中的异步方法调用它。我发现让它工作的唯一方法是调用 RenamingMethod 中的所有内容,如下所示:
public void RenamingMethod()
{
Application.Current.Dispatcher.Invoke((Action)delegate
{
//code for renaming
}
}
此时,进度条仅在我关闭弹出窗口时更新 window。然后它保持相同的值,直到打开和关闭新的 window。
我想从更新进度条的异步方法打开弹出window,所以这个演变很顺利,当打开一个新的window时它就停止了。或者能够同步更新进度条也可能会起作用。
下面是更详细的代码:
public void RenameMethod()
{
Application.Current.Dispatcher.Invoke((Action)delegate
{
IEnumerable<string> files = Directory.EnumerateFiles(
OriginMediaFolder,
"*.*",
SearchOption.AllDirectories).
Where(name => name.EndsWith(".jpg") || name.EndsWith(".mp4"));
int numberOfFiles = files.Count();
float fileNumber = 0;
foreach (string file in files)
{
MoveAndRenameFile(file);
ProgressValue = (int)(100 * (fileNumber++ / numberOfFiles));
}
});
}
public void MoveAndRenameFile(string file)
{
//Name structure: path/1234567891234567-hash.ext
string fileName = Regex.Match(file, @"[^\/\]+\.",RegexOptions.RightToLeft).
Value.TrimEnd('.');
string date = fileName.Substring(0, 16);
string hash = fileName.Substring(17);
if (!HashToName(hash, out string gameName))
{
var gameNamingWindow = GetNewNameFromUser(hash);
}
string extension = Regex.Match(file, @"\..+\Z", RegexOptions.RightToLeft).Value;
if (!Directory.Exists(DestinationMediaFolder + "/" + gameName))
Directory.CreateDirectory(DestinationMediaFolder + "/" + gameName);
File.Move(file, DestinationMediaFolder + "/" + gameName + "/" + date + extension);
}
public string GetNewNameFromUser(string hash)
{
var gameNamingWindow = new NewGameWindow.MainWindow(OriginMediaFolder, hash);
if (gameNamingWindow.ShowDialog().Value)
{
string gameName = gameNamingWindow.Result;
NameHashes.Add(new NameHash
{
GameName = gameName,
Hash = hash
});
return gameName;
}
return hash;
}
Invoke 调用切换到 GUI 线程,因此您基本上是在占用该线程来完成您的所有工作,并有效地否定了开始时使用工作线程的全部意义。更改 RenameMethod() 以便 Invoke 块内唯一的东西是实际需要的东西,即更新进度条的行。
更好的是,开始为您的 GUI 控件使用正确的数据绑定,这样您就根本不必为任务切换而烦恼。