Как я могу передать аргумент Dispatcher.Invoke?

Я пытаюсь загрузить BitmapImage в фоновом потоке, а затем установить источник изображения (WPF) для этого BitmapImage.

В настоящее время я пытаюсь что-то вроде этого:

public void LoadPicture()
{
    Uri filePath = new Uri(Directory.GetCurrentDirectory() + "/" + picture.PictureCacheLocation);
    if (Visible && !loaded)
    {
        if (File.Exists(filePath.AbsolutePath) && picture.DownloadComplete)
        {
            BitmapImage bitmapImage = LoadImage(filePath.AbsolutePath);
            image.Dispatcher.Invoke(new Action<BitmapImage>((btm) => image.Source = btm), bitmapImage);

            loaded = true;
        }
    }
}

Но я получаю InvalidOperationException, потому что фоновый поток владеет BitmapImage. Есть ли способ передать право собственности на BitmapImage или копию потока пользовательского интерфейса?

Мне нужно загрузить растровое изображение в фоновом потоке, потому что оно может заблокироваться на долгое время.


person DeGandalf    schedule 03.06.2021    source источник
comment
Используйте async/await вместо того, чтобы возиться с Invoke. В этом нет необходимости с 2012 года. Кроме того, bitmapImage будет принадлежать фоновому потоку, только если LoadPicture будет вызван фоновым потоком или LoadImage будет использовать поток. Опубликуйте недостающий код   -  person Panagiotis Kanavos    schedule 03.06.2021
comment
Кроме того, вы можете создать BitmapImage, который загружает данные из файла напрямую с помощью var bitmapImage=new BitmapImage(filePath);. Используйте Path.Combine вместо прямого объединения строк   -  person Panagiotis Kanavos    schedule 03.06.2021
comment
Поскольку в коде используется папка current, объединение не требуется. var filePath=new Uri(Path.GetAbsolutePath(picture.PictureCacheLocation));   -  person Panagiotis Kanavos    schedule 03.06.2021
comment
Что-то похожее на это может быть полезно. Заморозить и вернуть BitmapImage из действия «Задача»   -  person Clemens    schedule 03.06.2021
comment
Кроме того, picture.DownloadComplete, по-видимому, указывает на то, что вы сначала загружаете изображение, затем записываете его в файл, а затем создаете BitmapImage из этого файла. Имейте в виду, что вы можете создать BitmapImage непосредственно из удаленного URL-адреса.   -  person Clemens    schedule 03.06.2021
comment
Я до сих пор не совсем понимаю, как работает async/await, поэтому могу ошибаться. Но в моем случае я вызываю этот код из события, которое (по крайней мере, я так думаю) не является потоком пользовательского интерфейса, поэтому мне нужно вызвать Dispatcher.Invoke, чтобы выполнить его в потоке пользовательского интерфейса, верно? Также спасибо за Path.GetAbsolutePath , хотя я смог найти только Path.GetFullPath. И я не могу загрузить растровое изображение напрямую, так как есть ошибка, из-за которой это блокирует файл даже после установки источника = null. И я не могу использовать прямой uri, так как мне нужна аутентификация для доступа к изображению. Все равно спасибо за ответ :)   -  person DeGandalf    schedule 04.06.2021


Ответы (1)


Вся работа с DependencyObject должна происходить в одном потоке.
За исключением замороженных экземпляров Freezable.

Также нет смысла (в данном случае) передавать параметр в Invoke — лучше использовать лямбду.

Также существует опасность самоблокировки Dispatcher, поскольку вы не проверяете поток.

    public void LoadPicture()
    {
        Uri filePath = new Uri(Directory.GetCurrentDirectory() + "/" + picture.PictureCacheLocation);
        if (Visible && !loaded)
        {
            if (File.Exists(filePath.AbsolutePath) && picture.DownloadComplete)
            {
                BitmapImage bitmapImage = LoadImage(filePath.AbsolutePath);

                bitmapImage.Freeze();

                if (image.Dispatcher.CheckAccess())
                    image.Source = bitmapImage;
                else
                    image.Dispatcher.Invoke(new Action(() => image.Source = bitmapImage));

                loaded = true;
            }
        }
    }

Объекты типа Freezable не всегда позволяют себя заморозить.
Но вашего кода недостаточно для выявления возможных проблем.
Если не получается заморозить, то покажите, как реализован метод LoadImage (Uri).

person EldHasp    schedule 03.06.2021
comment
image.Dispatcher.CheckAccess() не имеет смысла, когда вы знаете, что находитесь в потоке, отличном от потока пользовательского интерфейса. - person Clemens; 03.06.2021
comment
Я согласен. Но из показанного кода я не смог сделать подобный вывод. ТС пишет, что у него возникла проблема при вызове метода из фонового потока. Но он не написал, что всегда вызывает этот метод только из фонового потока. - person EldHasp; 03.06.2021
comment
Мне это больше не нужно, так как я нашел ошибку, из-за которой LoadImage блокируется так долго, чтобы я мог загрузить его в Invoke. Но это все еще очень полезный ответ, спасибо. - person DeGandalf; 04.06.2021
comment
Также не могли бы вы немного пояснить, как работает доступ диспетчера и когда он блокируется? Этот метод иногда вызывается из потока пользовательского интерфейса, но его никогда не следует вызывать из другого Dispatcher.Invoke. - person DeGandalf; 04.06.2021