See the question and my original answer on StackOverflow

There are multiple ways to do it but none using XAML. The "classic" way is to use WinRT's BitmapDecoder class and customize the way it works.

Solution 1

With this XAML:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <Button Grid.Row="0" Click="Button_Click">click</Button>

    <Image x:Name="img" Grid.Row="1" Width="300" Height="300" />

</Grid>

And this code:

private async void Button_Click(object sender, RoutedEventArgs e)
{
    // create a BitmapDecoder
    var file = await StorageFile.GetFileFromPathAsync(@"c:\somepath\q4QAb.png");
    using var stream = await file.OpenStreamForReadAsync();
    using var ras = stream.AsRandomAccessStream();
    var decoder = await BitmapDecoder.CreateAsync(ras);

    // create a bitmap transform.
    // you'll have to call this with width & height corresponding to desired Image's with & height
    var transform = new BitmapTransform();
    transform.InterpolationMode = BitmapInterpolationMode.NearestNeighbor;
    transform.ScaledWidth = (uint)img.Width;
    transform.ScaledHeight = (uint)img.Height;

    // load the bitmap & bitmap source using transform
    using var bmp = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied, transform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.ColorManageToSRgb);
    var source = new SoftwareBitmapSource();
    await source.SetBitmapAsync(bmp);
    img.Source = source;
}

Solution 2

Here is another way, using the Visual Layer (aka: Direct Composition) which is not limited to UWP contrary to what the doc might say:

With this XAML:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="30" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <Button Grid.Row="0" Click="Button_Click">click</Button>

    <Canvas x:Name="canvas" Grid.Row="1" />

</Grid>

And this button handling code:

private void Button_Click(object sender, RoutedEventArgs e)
{
    // get visual layer's compositor
    var compositor = ElementCompositionPreview.GetElementVisual(canvas).Compositor;

    // create a surface brush, this is where we can use NearestNeighbor interoplation
    var brush = compositor.CreateSurfaceBrush();
    brush.BitmapInterpolationMode = CompositionBitmapInterpolationMode.NearestNeighbor;

    // create a visual
    var imageVisual = compositor.CreateSpriteVisual();
    imageVisual.Brush = brush;

    // load the image
    var image = LoadedImageSurface.StartLoadFromUri(new Uri(@"c:\somepath\q4QAb.png"));
    brush.Surface = image;

    // set the visual size when the image has loaded
    image.LoadCompleted += (s, e) =>
    {
        // choose any size here
        imageVisual.Size = new System.Numerics.Vector2(300, 300);
        image.Dispose();
    };

    // add the visual as a child to canvas
    ElementCompositionPreview.SetElementChildVisual(canvas, imageVisual);
}

Note 1: in the solution 2 case, you don't have to use async/await code. Also you can resize the imageVisual visual at any time. Without any more context information, this is my preferred way.

Note 2: all UI/XAML namespaces must start by Microsoft.UI.Xaml to ensure you're using WinUI3 and not UWP.