简体   繁体   中英

Odd behaviour of Image control in WPF

I'm trying to get started with WPF in c#. I set a Canvas as the content of a window, then I create another Canvas and put it as a child of the first Canvas (together with other elements, such as buttons and labels). Everything runs fine until I create an Image object and add it dynamically to the inner Canvas:

Image m_Img = new Image
{
    Name = "img1",
    Width = cvWindow.Width,
    Height = dUsefulHeight,
    Stretch = Stretch.Uniform
};

m_Img.Source = new BitmapImage(
    new Uri(xMap.SelectSingleNode("@image").FirstChild.Value,
        UriKind.RelativeOrAbsolute));

Canvas.SetLeft(m_Img, 0);
Canvas.SetTop(m_Img, 0);

double d = m_Img.Source.Width;

cvWindow.Children.Add(m_Img);

Here m_Img is the image I create, cvWindow is the inner Canvas. The source of the image is a PNG file, extracted from an XML file (the string returned is correct).

The odd behaviour is here: if I comment out the line

double d = m_Img.Source.Width;

the Image is not displayed anymore, although other controls in the Canvas (such as labels and buttons) are correctly displayed.

I don't need the width of the source image, so the compiler tells me that variable is never used.

I updated Visual Studio 2010 to the last SP1, but the behaviour remained. Google doesn't help either. I came to think that the Width property may have a getter method that triggers some action, but cannot solve the puzzle.


Edit: Same thing happens using another property of Source (eg Height). If I access at least one property, the image displays ok.

I finally discovered what happens: the Image control needs that the properties DecodePixelWidth and DecodePixelHeight of the Source are set to the correct values.

Once the Source is created, those values are not set, and the Image is not drawn. Upon first access to any property of the BitmapImage that serves as source the image is actually decoded and those properties are set to the final width and height of the decoded image (so the Image can be drawn).

I can solve this by setting those values by hand (with a cast to int) like this

BitmapImage bs1 = new BitmapImage();
bs1.BeginInit();
bs1.UriSource = new Uri(
    xMap.SelectSingleNode("@image").FirstChild.Value,
    UriKind.RelativeOrAbsolute);
bs1.EndInit();
bs1.DecodePixelHeight = (int)bs1.Height;
bs1.DecodePixelWidth = (int)bs1.Width;

m_Img.Source = bs1;

but I think I will re-design my views with a better separation (views in XAML, model and viewmodel via code).

This bit is also mentioned in the second note in http://msdn.microsoft.com/en-us/library/ms747027.aspx

When you specify the size of an image with either Width or Height, you should also set either DecodePixelWidth or DecodePixelHeight to the same respective size.

Don't define your views via code - use XAML,
even if you are trying to creating dynamic views, using XAML is much more clean
and using a good designer app (eg Blend), you'll notice things that you didn't consider.

Some things that you didn't consider are that.Width is not necessary equal to.ActalWidth.
Default widths are usually double.NaN (which means auto-width).

Use bindings, bind width of A to width of B, use margin, padding or value converters to make width A binded to width of B - const.

Use layout panels (eg Grid, StackPanel, DockPanel) to make alignment of multiple controls simple (and binding free).

Also, prefer using standard naming conventions (eg no m_).

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