locked
UserControl image URI's are referencing app's content, not its own?

    Question

  • I have a UserControl that loads images at runtime that are marked as Content in the project using the ms-appx URI format.  For example:

    ms-appx:///Assets/Images/icons/microphone-off.png

    Here's an example of an assignment:

    imgMicrophone.source = new Uri("ms-appx:///Assets/Images/icons/microphone-off.png");

    Unfortunately, this doesn't work for assets in the UserControl project, even though the code that is making the assignment is resident in the UserControl project, not the Startup app project.  The asset has to be located in the Startup app project that consumes the UserControl.  If not, the image does not appear.  How can I create URI references that tell the WinRT libraries to pull the image from assets belonging to the project the code is resident in, not the Startup app that consumes the project?

    Also, debugging this cost me a lot of time because it appears that the WinRT libraries don't raise an Exception or output anything to the Output window if an image can not be found, despite telling the IDE to break all Exceptions in the IDE, thrown or Unhandled.  Is there a way to force an Exception to be raised if the runtime code can't find an Asset, images or otherwise? 


    -- roschler

    Thursday, February 13, 2014 10:47 PM

Answers

  • I think what you are looking for is replacing this:

    imgMicrophone.source = new Uri("ms-appx:///Assets/Images/icons/microphone-off.png");

    with this

    imgMicrophone.source = new Uri("ms-appx:///MyLibraryAssembly/Assets/Images/icons/microphone-off.png");

    - for assets defined inside of your library named "MyLibraryAssembly".



    Filip Skakun

    • Marked as answer by roschler Friday, February 14, 2014 8:29 PM
    Friday, February 14, 2014 4:58 PM

All replies

  • When I create a UserControl (as a Class Library Project) I don't use any file protocol at all - I can just set it to a local image such as "image.jpg" and it references the image which is content of the UserControl's project.

    The documentation is clear about problems loading images:

    Setting the source to a URI value that can't be resolved to a valid image source file doesn't throw an exception. Instead, it fires an ImageFailed event. You can write an ImageFailed handler and attach it to the Image object, and possibly use the ErrorMessage in event data to determine the nature of the failure. An error in decoding can also fire ImageFailed. If you want to verify that an image source file was loaded correctly, you can handle the ImageOpened event on the Image element.


    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Friday, February 14, 2014 1:57 PM
    Moderator
  • Hello Matt,

    "When I create a UserControl (as a Class Library Project) I don't use any file protocol at all - I can just set it to a local image such as "image.jpg" and it references the image which is content of the UserControl's project."

    Yes I can do that too.  But the problem arises when trying to change Image URI's from code.  I have an Image control that changes images (Source) based on different states in my app.  It is bound to a property that returns a URI for the correct image to show.  That's when I use the "ms-appx" URI's as I return different URI's based on the current state and I need those to reference assets in the UserControl instead of the host app.

    Thanks for the tip on the ImageFailed event.  That's what I needed for that part of my question.


    -- roschler

    Friday, February 14, 2014 2:14 PM
  • Does it work if you try it like this?

    BitmapImage bitmapImage = new BitmapImage();
    bitmapImage.UriSource = new Uri(imgMicrophone.BaseUri, "ImageFromUserControl.jpg");
    imgMicrophone.Source = bitmapImage;

    If so, I would use an enum property to present only allowable image sources (those that are packaged within the usercontrol itself).


    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Friday, February 14, 2014 2:53 PM
    Moderator
  • Hello Matt,

    No it doesn't.  When I do that I get the following Exception:

    An exception of type 'System.UriFormatException' occurred in System.dll but was not handled in user code
    Additional information: Invalid URI: The format of the URI could not be determined.

    Here's the URI's I tried, all of which reference JPG's that are packaged with the app and marked as Content.

    private const string URI_BUTTON_IMAGE_MIC_OFF = @"Assets/Images/icons/microphone-off.png";
    private const string URI_BUTTON_IMAGE_MIC_ON = @"Assets/Images/icons/microphone-on.png";
    private const string URI_BUTTON_IMAGE_MIC_HOVER = @"Assets/Images/icons/microphone-hover.png";

    They looked like this previously:

    private const string URI_BUTTON_IMAGE_MIC_OFF = @"ms-appx:///Assets/Images/icons/microphone-off.png";
    private const string URI_BUTTON_IMAGE_MIC_ON = @"ms-appx:///Assets/Images/icons/microphone-on.png";
    private const string URI_BUTTON_IMAGE_MIC_HOVER = @"ms-appx:///Assets/Images/icons/microphone-hover.png";

    But as I said, those only work if the JPG's are in the host app assets, and not for JPG's hosted in the UserControl project.  It's as if I need a URI syntax that allows me to select the Assembly to pull the assets from.

    I'm not sure how I could use your enum property idea.  You say "to present" like I'm asking the user to make a selection.  I'm not, I'm simply selecting different JPG's for an Image control as the UserControl's state changes, completely under program control and without user intervention.


    -- roschler

    Friday, February 14, 2014 4:05 PM
  • I got it working using the enum idea, here's the code:

    The UserControl:

    <UserControl
        x:Class="MyUserControl.MyUserControl1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="using:MyUserControl"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        d:DesignHeight="300"
        d:DesignWidth="400">
        
        <Grid>
            <Image Source="Kitten1.jpg" x:Name="TheImage"/>
        </Grid>
    </UserControl>
    namespace MyUserControl
    {
        public sealed partial class MyUserControl1 : UserControl
        {
            public MyUserControl1()
            {
                this.InitializeComponent();
            }
    
            public enum KittenEnum
            {
                Kitten1, Kitten2
            }
            private KittenEnum _KittenPicture;
    
            public KittenEnum KittenPicture
            {
                get { return _KittenPicture; }
                set
                {
                    _KittenPicture = value;
                    if (_KittenPicture == KittenEnum.Kitten1)
                    {
                        BitmapImage bitmapImage = new BitmapImage();
                        bitmapImage.UriSource = new Uri(TheImage.BaseUri, "Kitten1.jpg");
                        TheImage.Source = bitmapImage;
    
                    }
                    else
                    {
                        BitmapImage bitmapImage = new BitmapImage();
                        bitmapImage.UriSource = new Uri(TheImage.BaseUri, "Kitten2.jpg");
                        TheImage.Source = bitmapImage;
                    }
                }
            }
        }
    }
    Using it in a referenced project: MainPage.xaml
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
            <StackPanel>
                <user:MyUserControl1 x:Name="MyImage" Width="300" Height="400"/>
                <Button Content="Change Picture" Click="Button_Click"/>
            </StackPanel>
        </Grid>
    namespace UseUserControlProject
    {
        /// <summary>
        /// An empty page that can be used on its own or navigated to within a Frame.
        /// </summary>
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
            }
    
           
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                if (MyImage.KittenPicture == MyUserControl.MyUserControl1.KittenEnum.Kitten1)
                {
                    MyImage.KittenPicture = MyUserControl.MyUserControl1.KittenEnum.Kitten2;
                }
                else
                {
                    MyImage.KittenPicture = MyUserControl.MyUserControl1.KittenEnum.Kitten1;
                }
            }
        }
    }





    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Friday, February 14, 2014 4:10 PM
    Moderator
  • Hello Matt,

    Thank you for taking the time to code up that test.

    So there's no way to do this in a way suitable for data binding?  That's too bad if so since that's a lot of additional code.  


    -- roschler

    Friday, February 14, 2014 4:17 PM
  • I'm not sure how you do this with binding.  If the images are embedded in the user control, then you would want to restrict the values to only images that exist inside the user control. 

    You could create a dependency property for the user control so that valid values are given to the usercontrol, and those values would then map to the internal images.


    Matt Small - Microsoft Escalation Engineer - Forum Moderator
    If my reply answers your question, please mark this post as answered.

    NOTE: If I ask for code, please provide something that I can drop directly into a project and run (including XAML), or an actual application project. I'm trying to help a lot of people, so I don't have time to figure out weird snippets with undefined objects and unknown namespaces.

    Friday, February 14, 2014 4:30 PM
    Moderator
  • Hello Matt,

    It's too bad there isn't something like the "pack" URI format that let's you specify an Assembly.  I'm surprised more people haven't asked for this.

    Thanks for your help.


    -- roschler

    Friday, February 14, 2014 4:42 PM
  • I think what you are looking for is replacing this:

    imgMicrophone.source = new Uri("ms-appx:///Assets/Images/icons/microphone-off.png");

    with this

    imgMicrophone.source = new Uri("ms-appx:///MyLibraryAssembly/Assets/Images/icons/microphone-off.png");

    - for assets defined inside of your library named "MyLibraryAssembly".



    Filip Skakun

    • Marked as answer by roschler Friday, February 14, 2014 8:29 PM
    Friday, February 14, 2014 4:58 PM
  • Thanks Filip!  That did the trick nicely. :)

    -- roschler

    Friday, February 14, 2014 8:29 PM