BitmapImage не работает с Binding?

127
22 июня 2019, 21:00

Интересная ситуация, при попытке установки значения для BitmapImage.UriSource через привязку данных, вот так:

<catel:UserControl x:Class="Watch.Views.SerialPreviewView"
                   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                   xmlns:catel="http://schemas.catelproject.com"
                   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                   mc:Ignorable="d"
                   d:DesignWidth="200" d:DesignHeight="300" MaxWidth="200" MaxHeight="300" Margin="2.5">
    <Border Background="WhiteSmoke" BorderBrush="Gray" CornerRadius="3" BorderThickness="1">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="21" />
                <RowDefinition Height="*" />
                <RowDefinition Height="21" />
            </Grid.RowDefinitions>
            <Border Grid.Row="1">
                <Image MinHeight="300" MinWidth="200">
                    <Image.Source>
                        <BitmapImage CacheOption="OnDemand" CreateOptions="DelayCreation"
                                     UriSource="{Binding SerialInfo.PreviewImage, TargetNullValue='https://dummyimage.com/200x300/cecece/000000.png'}" />
                    </Image.Source>
                </Image>
            </Border>
            <Border Grid.Row="0" BorderBrush="Gray" Padding="3">
                <TextBlock FontSize="14" Text="{Binding SerialInfo.TranslatedTitle}" VerticalAlignment="Center"
                           TextTrimming="CharacterEllipsis" HorizontalAlignment="Stretch"
                           ToolTip="{Binding Text, RelativeSource={RelativeSource Self}}" />
            </Border>
            <Border Grid.Row="2" BorderBrush="Gray" Padding="3">
                <TextBlock FontSize="14" Text="{Binding SerialInfo.OriginalTitle}" VerticalAlignment="Center"
                           TextTrimming="CharacterEllipsis" HorizontalAlignment="Stretch"
                           ToolTip="{Binding Text, RelativeSource={RelativeSource Self}}" />
            </Border>
        </Grid>
    </Border>
</catel:UserControl>

После инициализации View происходит исключение:

InvalidOperationException: Необходимо задать свойство "UriSource" или "StreamSource".

Т.е. в момент инициализации DataContext еще не установлен, но это произойдет немного позже. Как с этим бороться т.к. игнорируется значение TargetNullValue а так же FallbackValue?

Answer 1

Смотрите (c).

TargetNullValue предназначен для случаев, когда привязка работает, но источник содержит значение null, у вас же, пока DataContext не установлен, привязка оказывается в нерабочем состоянии, но для BitmapImage это недопустимо, он обязательно должен иметь установленное свойство UriSource или StreamSource.

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

<BitmapImage UriSource="{Binding Uri, FallbackValue='https://dummyimage.com/200x300/cecece/000000.png'}"/>

Но он не заработает как нам нужно: заглушка для картинки появится, но когда мы установим DataContext, она не поменяется на нужную нам. Тут есть еще одна загвоздка (читайте комментарии в статье про BitmapImage на MSDN):

... После инициализации изменения свойств игнорируются.

т. е. созданное единожды BitmapImage навсегда останется таким.

Тут у вас есть несколько вариантов:

  • написать простой конвертер, который будет по строке (или Uri) создавать новый экземпляр BitmapImage:

    class UriToBitmapImageConverter : ConverterBase
    {
        public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var b = new BitmapImage
            {
                CacheOption = BitmapCacheOption.OnDemand,
                CreateOptions = BitmapCreateOptions.DelayCreation
            };
            b.BeginInit();
            b.UriSource = new Uri((string)value);
            b.EndInit();
            return b;
        }
    }
    

    и воспользоваться им:

    <Image Source="{Binding Uri, Converter={c:UriToBitmapImageConverter},
        FallbackValue='https://dummyimage.com/200x300/cecece/000000.png'}"/>
    
  • воспользоваться фичей PriorityBinding:

    <Window.Resources>
        <BitmapImage x:Key="Placeholder" UriSource="https://dummyimage.com/200x300/cecece/000000.png"/>
    </Window.Resources>
    

    затем:

    <Image>
        <Image.Source>
            <PriorityBinding>
                <Binding Path="Uri" IsAsync="True"/>
                <Binding Source="{StaticResource Placeholder}"/>
            </PriorityBinding>
        </Image.Source>
    </Image>
    

    но здесь, конечно, BitmapImage создается автоматически с настройками по умолчанию.

В обоих вариантах получаем:

READ ALSO
Как присвоить переменную типа Camera через метод Start()

Как присвоить переменную типа Camera через метод Start()

В скрипте есть переменная : public Camera PlayerCam; Через метод public void Start() {

105
SearchBar в С#(mvvm)

SearchBar в С#(mvvm)

Хочу сделать searchbar для поиска людей по имени или фамилии, но всё никак не получаетсяВот код

113
Проблема с добавлением таблицы в word

Проблема с добавлением таблицы в word

Пытаюсь добавить таблицу между двух параграфовНе получается

138
Как проверить большое целое число на четность или нечетность?

Как проверить большое целое число на четность или нечетность?

Написал программку, которая считывает с textbox число и проверяет его на четность и не четностьПрограмма работает, все норм, НО, выскакивает...

129