如何从异步方法打开 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 控件使用正确的数据绑定,这样您就根本不必为任务切换而烦恼。