Отображение изображения по умолчанию и его изменение после завершения загрузки фактического изображения - PullRequest
0 голосов
/ 18 декабря 2011

У меня есть расширение разметки WPF, отвечающее за поиск изображений по имени, возвращая объект BitmapImage.

<Image Source="{my:ImageProvider ImageName=myImageName}"></Image>

Поскольку получение изображения - это операция, которая может занять несколько секунд, я хотел бы показать изображение по умолчанию и отобразить запрошенное изображение, как только оно будет готово.

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

BitmapImage img;
public override object ProvideValue(IServiceProvider serviceProvider)
{
    img = new BitmapImage(new Uri(@"D:\defaultImage.png", UriKind.Absolute));

    BackgroundWorker bw = new BackgroundWorker();
    bw.DoWork += new DoWorkEventHandler(bw_DoWork);
    bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
    bw.RunWorkerAsync();

    return img;
}

void bw_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    System.Threading.Thread.Sleep(5000);
}

void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    img.UriSource = new Uri(@"D:\actualImage.png", UriKind.Absolute);
}

Есть ли способ обновить пользовательский интерфейс, чтобы использовать измененный BitmapImage (что-то вроде INotifyPropertyChanged) или есть другой подход для достижения этой цели?

Ответы [ 2 ]

1 голос
/ 18 декабря 2011

PriorityBinding это то, что вы можете искать, я думаю.Вы можете связать два разных DP с вашим фактическим источником изображения в качестве наивысшей привязки и не забыть установить для свойства IsAsync значение true для этой привязки.Как только ваш источник изображения готов, он автоматически заменит вторую привязку.

Для начала перейдите по этой ссылке - http://msdn.microsoft.com/en-us/library/system.windows.data.prioritybinding.aspx

0 голосов
/ 07 января 2012

У меня было два способа сделать это. Оба способа используют класс, который оборачивает изображение и реализует INotifyPropertyChanged:

class ImageSourceWrapper : ObservableObject
{
    private ImageSource _image;
    public ImageSource Image
    {
        get { return _image; }
        set
        {
            if (value != _image)
            {
                _image = value;

                RaiseOnPropertyChanged("Image");
            }
        }
    }

    public ImageSourceWrapper(ImageSource image)
    {
        Image = image;
    }
}

Первый подход

Получив это, я могу заставить мое расширение разметки возвращать объект ImageSourceWrapper и связываться с ним, например,

<Image Source="{Binding Source={my:ImageProvider ImageName=myImageName}, Path=Image}" />

Мне не очень понравился этот способ, потому что он довольно грязный и предполагает необходимость знать класс ImageSourceWrapper, а не просто работать с ImageSource. Затем я придумал второй подход.

Второй подход

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

Расширение разметки выглядит примерно так:

private ImageSourceWrapper _imageSourceWrapper;
public override object ProvideValue(IServiceProvider serviceProvider)
{
    // Get the object and the property to be bound.
    IProvideValueTarget service = IProvideValueTarget)provider.GetService(typeof(IProvideValueTarget));
    DependencyObject targetObject = service.TargetObject as DependencyObject;
    DependencyProperty targetProperty = service.TargetProperty as DependencyProperty;

    // Set up the binding with the default image.
    _imageSourceWrapper = new ImageSourceWrapper(DefaultImage);
    Binding binding = new Binding("Image");
    binding.Source = _imageSourceWrapper;
    BindingOperations.SetBinding(targetObject, targetProperty, binding);

    // Retrieve the actual image asynchronously.
    GetImageAsync();

    return binding.ProvideValue(serviceProvider);
}

private void GetImageAsync()
{
    // Get the image asynchronously.

    // Freeze the image so it could be accessed from all threads regardless
    // of which thread it was created on.
    newImage.Freeze();

    // Got the image - update the _imageSourceWrapper object.
    _imageSourceWrapper = newImage;
}

Тогда я могу использовать его в XAML следующим образом

<Image Source="{my:ImageProvider ImageName=myImageName}" />

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

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

...