简体   繁体   中英

How do I get the dimensions of an image synchronously in a UWP app?

I am trying to get an image's dimensions before I display it in the OnLoaded() method. I want to do this synchronously (not asynchronously) because I need to know the image's dimensions before the program continues. I'm making a mapping program where the coordinates, scale and translation offset of the background image are important and are all dependent on knowing the image's dimensions from the very beginning of the app.

I have read most of the msdn documentation on StorageFile, SoftwareBitmap, and asynchronous method calls along with trying many methods I have found online with no success. https://msdn.microsoft.com/en-us/windows/uwp/threading-async/asynchronous-programming-universal-windows-platform-apps

https://msdn.microsoft.com/en-us/windows/uwp/threading-async/call-asynchronous-apis-in-csharp-or-visual-basic

Basically I am looking for a synchronous c# function that takes the uri of an image file and returns a Point containing the dimensions. ie public Point getImageDimensions(Uri u){...}

This function is so easy to implement in Android apps, I don't understand why it is so difficult in UWP apps.

I can provide more details if needed, thanks in advance.

I get the following error with this code:
Exception thrown: 'System.Exception' in App1.exe
Exception thrown: 'System.AggregateException' in mscorlib.ni.dll

App1.xaml.cs

using System;
using System.Diagnostics;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Imaging;
using Windows.UI.Xaml.Shapes;

namespace App1
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            this.Loaded += OnLoaded;

            Point p = Task.Run(() => getImageDimensions("ms-appx:///Assets/NewSpaceBackground.png")).Result;
            Debug.WriteLine("p.X: " + p.X + " p.Y: " + p.Y);
        }

        void OnLoaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
        {
            Rectangle backgroundRect1 = new Rectangle();
            ImageBrush imgBrushBackground1 = new ImageBrush();
            imgBrushBackground1.ImageSource = new BitmapImage(new Uri(@"ms-appx:///Assets/NewSpaceBackground.png"));
            backgroundRect1.Fill = imgBrushBackground1;
            //backgroundMap.Add(backgroundRect1);
        }

        public async Task<Point> getImageDimensions(string strURI)
        {
            Point p;
            StorageFile photo;
            try
            {
                var uri = new Uri(strURI);
                photo = await StorageFile.GetFileFromApplicationUriAsync(uri);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.StackTrace);
                return p;
            }
            //read in the bitmap stream
            IRandomAccessStream stream = await photo.OpenAsync(FileAccessMode.Read);
            BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
            SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync();

            //convert the image to a bitmap
            SoftwareBitmap softwareBitmapBGR8 = SoftwareBitmap.Convert(softwareBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
            SoftwareBitmapSource bitmapSource = new SoftwareBitmapSource();
            await bitmapSource.SetBitmapAsync(softwareBitmapBGR8);

            //print out the dimensions
            Debug.WriteLine(softwareBitmapBGR8.PixelWidth + " " + softwareBitmapBGR8.PixelHeight);
            p.X = softwareBitmapBGR8.PixelWidth;
            p.Y = softwareBitmapBGR8.PixelHeight;

            return p;
        }
    }
}

I am trying to get an image's dimensions before I display it in the OnLoaded() method. I want to do this synchronously (not asynchronously) because I need to know the image's dimensions before the program continues.

There are some problems when you want to call async functions in constructor, for details you can refer to Stephen Cleary's blog: Async OOP 2: Constructors .

In UWP, You can put the Page initialization codes ( getImageDimensions and the codes of displaying the image) in Page.OnNavigatedTo method and use await in it:

protected override async void OnNavigatedTo(NavigationEventArgs e)
{
    Point p = await getImageDimensions("ms-appx:///Assets/NewSpaceBackground.png");
    Debug.WriteLine("p.X: " + p.X + " p.Y: " + p.Y);

    Rectangle backgroundRect1 = new Rectangle();
    ImageBrush imgBrushBackground1 = new ImageBrush();
    imgBrushBackground1.ImageSource = new BitmapImage(new Uri(@"ms-appx:///Images/image03.jpg"));
    backgroundRect1.Fill = imgBrushBackground1;
    ...
}

Update:

Would you recommend an easier, synchronous, way to get the dimensions?

Most of the file related operations are designed to be async, user can use/not use await to choose to do the operation sychronously or asynchronously.

There is indeed an easy way to obtain the image's dimension:

StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///images/image01.png"));
var properties=await file.Properties.GetImagePropertiesAsync();
var width = properties.Width;
var height = properties.Height;

Instead of streaming it into a Bitmap, could you load it into a System.Drawing.Image, like so?:

System.Drawing.Image img = System.Drawing.Image.FromStream(myStream);

Then you have access to the System.Drawing.Image's Size property, which should give you what you need, as well as easy access to resizing. All of this is available in .NET Core and UWP if memory serves.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM