Как открыть окно WPF из асинхронного метода - PullRequest
0 голосов
/ 25 ноября 2018

Я пытался найти решение для этого, но на данный момент ничего, что я нашел, не сделал щелчок.Я начинаю с WPF, так что, возможно, я просто пропустил какой-то очевидный ответ, но я был бы очень признателен за ответ.Это ситуация:

Мое приложение структурировано с использованием MVVM.Его основной функционал вызывается с кнопки.После вызова он перебирает все файлы в указанном каталоге и выполняет некоторое переименование.Делая этот цикл, я хотел показать индикатор выполнения, показывающий, сколько файлов было переименовано из общего количества.Значение прогресса - это свойство, связанное с элементом управления Progressbar

. Мне удалось только асинхронно обновлять индикатор выполнения;в противном случае он останется пустым до завершения процесса.Сейчас он работает с BackgroundWorker .

_bgWorker.DoWork += (s, e) =>
{
    RenamingMethod();
};
...
RenamingMethodCommand = new RelayCommand(_ =>_bgWorker.RunWorkerAsync());

. Для выполнения переименования в имени файла ожидается хеш.Этот хэш будет преобразован во введенное пользователем значение, если оно найдено, в противном случае должно появиться окно с просьбой ввести его.

Это окно невозможно отобразить в MTAThread (Я использую ShowDialog , я не нашел другого способа заставить его появиться), и поэтому я не могу вызвать его из асинхронных методов в моем главном окне.Единственный способ заставить его работать - это вызвать все внутри RenamingMethod следующим образом:

public void RenamingMethod()
{
    Application.Current.Dispatcher.Invoke((Action)delegate
    {
        //code for renaming
    }
}

На этом этапе индикатор выполнения обновляется только в тот момент, когда я закрываю всплывающее окно.,Затем он остается на том же значении, пока не откроется и не закроется новое окно.

Я хотел бы открыть всплывающее окно из асинхронного метода, который обновляет индикатор выполнения, поэтому развитие этого процесса происходит плавно, ион останавливается при открытии нового окна.Или возможность синхронного обновления индикатора выполнения, вероятно, также будет работать.

Вот код более подробно:

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

1 Ответ

0 голосов
/ 26 ноября 2018

Вызов Invoke переключается на поток GUI, поэтому вы в основном связываете этот поток, выполняя всю вашу работу и фактически сводя на нет весь смысл использования рабочего потока для начала.Измените RenameMethod () так, чтобы единственная вещь внутри блока Invoke была единственной вещью, которая должна быть там, то есть линией, которая обновляет индикатор выполнения.

Еще лучше, начните использовать правильную привязку данных для элементов управления GUIтак что вам вообще не придется возиться с переключением задач.

...