1

I'm rendering images in a WPF ItemsControl as follows:

<ItemsControl.ItemTemplate>
  <DataTemplate>
    <Border
      Height="200"
      HorizontalAlignment="Stretch"
      CornerRadius="9,9,0,0">
      <Border.Background>
        <ImageBrush
          ImageSource="{Binding MediaUrl, Mode=OneWay, IsAsync=True}"
          RenderOptions.BitmapScalingMode="LowQuality"
          RenderOptions.CachingHint="Cache"
          Stretch="UniformToFill" />
      </Border.Background>
    </Border>
  </DataTemplate>
</ItemsControl.ItemTemplate>

The ImageSource is bound to a URL pointing to an image server on the Web (HTTP protocol)

Sometimes the ImageBrush doesn't render but if I move the mouse over something in the program that that has to draw (a button that highlights on mouseover for instance), the image renders.

Is there something I can do to nudge WPF to render the ImageBrush after it loads?

.NET Core 3.1

Mike Ward
  • 3,211
  • 1
  • 29
  • 42
  • Does it only happen when you set `IsAsync=True` on the ImageSource Binding? Setting that seems pointless anyway, since the loading of a BitmapImage or BitmapFrame from a remote URI is done asynchronously by default. And setting `Mode=OneWay` is of course also redundant. – Clemens Mar 02 '20 at 18:52
  • Happens whether I use IsAsync or not. – Mike Ward Mar 02 '20 at 18:58
  • Also without `RenderOptions.CachingHint="Cache"`? – Clemens Mar 02 '20 at 19:00
  • Removing `RenderOptions.CachingHint="Cache"` makes no difference either. – Mike Ward Mar 02 '20 at 19:04
  • 1
    I'd recommend to reduce the amount of WPF magic that happens behind the scenes by doing the web request manually, either by changing the property type from string/Uri to ImageSource, or by implementing a Binding Converter. Download the web resource and create a BitmapImage or BitmapFrame from a MemoryStream. Something like this: https://stackoverflow.com/a/46709476/1136211 – Clemens Mar 02 '20 at 19:05
  • Also, it seems related to `ImageBrush` or maybe `ImageSource`. If I remove `Border` and use `Image` it works just fine. However, I really would like to control the radius of each corner of the image. – Mike Ward Mar 02 '20 at 19:07
  • I could see using a binding converter. Would the lifetime of the bitmap be the same as the magic WPF binding (kinda guessing it would)? I'm using this in a virtualizing stack panel. – Mike Ward Mar 02 '20 at 19:12
  • 1
    The magic simply calls the built-in ImageSourceConverter, which returns a BitmapFrame that is potentially cached with the URI as cache key. However, if an Image element works, but an ImageBrush doesn't, doing the download manually will most likely not be very helpful. – Clemens Mar 02 '20 at 19:16
  • 1
    Maybe you could simply use an Image element that has its OpacityMask set to some Brush with those round corners, e.g. a VisualBrush with your Border as Visual, or an appropriate DrawingBrush. – Clemens Mar 02 '20 at 19:38
  • 1
    I made a visual brush out of the image and used it as the background the border which is similar to what you were suggesting. Thanks. – Mike Ward Mar 02 '20 at 21:32
  • That's even simpler. You should post that as an answer. – Clemens Mar 02 '20 at 21:41

1 Answers1

3

From the discussion above I put together this solution. VisualBrush allowed me to use an Image instead of ImageBrush, which behaves better in my use case.

<ItemsControl.ItemTemplate>
  <DataTemplate>
    <Border
      Height="200"
      HorizontalAlignment="Stretch"
      CornerRadius="9,9,0,0">
      <Border.Background>
        <VisualBrush
          AlignmentY="Top"
          Stretch="UniformToFill">
          <VisualBrush.Visual>
            <Image
              RenderOptions.BitmapScalingMode="LowQuality"
              Source="{Binding MediaUrl}" />
          </VisualBrush.Visual>
        </VisualBrush>
      </Border.Background>
    </Border>
  </DataTemplate>
</ItemsControl.ItemTemplate>
Mike Ward
  • 3,211
  • 1
  • 29
  • 42