CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 5 of 5
  1. #1
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Question Referencing application resources from XAML

    One of my older toy development apps (ported from originally Windows Forms to WPF, but I don't think that matters) uses some bitmap resources (originally JPG and PNG files) which have been added to the application's Resources.resx file. While, in most variations I tried, it works fine on the development system, it reliably keeps failing on anything else, due to exceptions getting thrown because it tries to access the original bitmap files in the project directory of which, usually, not even the drive letter exists on the target machine.

    The closest I got, I think, was XAML BitmapImage references that looked quite similar to those shown in the unanswered thread http://forums.codeguru.com/showthrea...lem-with-fonts! from one and a half year ago, yet didn't work. After having tried every this and that I could think of and really running out of ideas, I'm starting to feel rather silly now... MSDN docs didn't turn out to really be helpful to me this time either.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  2. #2
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Referencing application resources from XAML

    Not sure what you've tried and it's been a while since I've coded in WPF, but if you haven't done so try setting the resource as a embedded resource (right click on the resource in the solution explorer and choose properties. You should see a setting that allows you to compile it as an embedded resource.

  3. #3
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Referencing application resources from XAML

    Thanks for your suggestion. In fact, all the image files' Build Action property in Solution Explorer was set to None, changed that to Embedded Resource. This led to approximately doubling the application's .exe file size, which is logical: The .exe contains very few actual code, just for a relatively simple GUI; the actual model code resides in a separate DLL which is common among the WPF and Windows Forms versions of the program. The four image resources are one larger JPEG of about 580 KB and three small PNGs of insignificant size. Now they obviously are embedded twice, once per the changed property in Solution explorer, onjce per Resources.resx (as they already were before, just not accessed properly).

    Here are the relevant parts of the XAML files involved:

    App.xaml:
    Code:
    <Application
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:local="clr-namespace:DClockWPF"
                 xmlns:System="clr-namespace:System;assembly=System" x:Class="DClockWPF.App"
                 StartupUri="MainWindow.xaml">
      <Application.Resources>
        <BitmapImage x:Key="TurmDunkel" UriSource="Resources/V21TurmDunkel.jpg"/>
        <ObjectDataProvider x:Key="dclockConfig" ObjectType="{x:Type local:Config}" />
      </Application.Resources>
    </Application>
    MainWindow.xaml:
    Code:
    <Window
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:local="clr-namespace:DClockWPF"
            xmlns:Properties="clr-namespace:DClockWPF.Properties" xmlns:System="clr-namespace:System;assembly=System" x:Name="window" x:Class="DClockWPF.MainWindow"
            mc:Ignorable="d"
            Title="Düsselclock WPF" Height="{Binding WindowHeight, Mode=TwoWay}" Width="{Binding WindowWidth, Mode=TwoWay}" ScrollViewer.VerticalScrollBarVisibility="Disabled" Closed="window_Closed" Left="{Binding WindowLeft, Mode=TwoWay}" Top="{Binding WindowTop, Mode=TwoWay}" ContentRendered="window_ContentRendered" MaxWidth="{Binding Width, ElementName=canvas}">
    
      <!-- ... -->
    
      <ScrollViewer x:Name="scrollviewer" Margin="0" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Disabled" ScrollChanged="scrollviewer_ScrollChanged" MouseMove="scrollviewer_MouseMove" MouseLeave="scrollviewer_MouseLeave">
    
        <!-- ... -->
    
        <Canvas x:Name="canvas" Width="{Binding ActualWidth, ElementName=image, Mode=OneWay}" SizeChanged="canvas_SizeChanged" MouseWheel="canvas_MouseWheel" MouseLeftButtonDown="canvas_MouseLeftButtonDown">
          <Image x:Name="image" Source="{StaticResource TurmDunkel}" Height="{Binding ActualHeight, ElementName=canvas, Mode=OneWay}"/>
          <TextBlock x:Name="tblkDateTime" Canvas.Left="10" TextWrapping="Wrap" Text="TextBlock" Canvas.Top="10" FontFamily="Arial" FontSize="18.667" FontWeight="Bold" Foreground="#FF0606E4"/>
        </Canvas>
      </ScrollViewer>
    </Window>
    The only image resource for which I changed the XAML settings is the JPEG, original file name V21TurmDunkel.jpg, so only this one is shown here.

    Unfortunately, this currently led to a situation in which the program even fails on the development machine. In the canvas_sizeChanged handler I'm accessing image.Source.Width to perform some calculations for programmatic scaling and position adjustments of a bunch owner-drawn graphical elements. After the changes applied, image.Source is null, causing the respective exception to be thrown. Commenting out the part of code that accesses image.Source and setting a conditional tracepoint shows that image.Source remains null and the immage apparently never is actually loaded: The window remains blank.

    Trying to access the image resource directly as (BitmapImage)image.FindResource("TurmDunkel") in replacement of image.Source leads to an exception complaining that resources/v21turmdunkel.jpg was not found. (The resource key "TurmDunkel" seems to be correct at this point: The lookup fails only one step later.)
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

  4. #4
    Arjay's Avatar
    Arjay is offline Moderator / EX MS MVP Power Poster
    Join Date
    Aug 2004
    Posts
    13,490

    Re: Referencing application resources from XAML

    Try creating a separate new test project, add and access a jpg resource.

    The add the v21turndunkel.jpg to the project and attempt to access it. If this works, compare the differences between the test project and the real project.

    Sorry for not being more helpful, but my wrestling with WPF skills are rusty.

  5. #5
    Join Date
    Jun 2010
    Location
    Germany
    Posts
    2,675

    Re: Referencing application resources from XAML

    Thanks again for that tip, an appealingjy simple approach! However, while playing around with the test project I quickly encountered pretty much the same problems, without any real idea of a solution again. One detail that (IMO) needlessly complicated things was that the VS 2ß15 XAML designer all the time correctly displayed the image even though my compiled program failed to do so. Therefor I allways needed to compile the program and run a debugging session for each and every tiny modification I tried.

    After fiddling around with that for a while, I started becoming both annoyed and bored at the same time, which is a state I really hate. So I decided to hack my way through it by removing the Image object from the XAML and instead creating it programmatically in the MainWindow c'tor from the resource embedded via Resources.resx, in an IMO unnecessarily complicated three-stage procedure:

    Code:
    public MainWindow()
    {
      // ...
    
      InitializeComponent();
    
      // ...
      
      var strmTower = new System.IO.MemoryStream();
      Properties.Resources.V21TurmDunkel.Save(strmTower, System.Drawing.Imaging.ImageFormat.Png);
      var bmiTower = new BitmapImage();
      bmiTower.BeginInit();
      bmiTower.CacheOption = BitmapCacheOption.OnLoad;
      bmiTower.StreamSource = strmTower;
      bmiTower.EndInit();
      m_imgTower = new Image();
      m_imgTower.Source = bmiTower;
      canvas.Children.Insert(0, m_imgTower);  // Not simply Add() it to avoid obscuring tblkDateTime
    
      // ...
    }
    In fact, this is the way I already handled it for the owner-drawn graphical elements, and althogh there was another XAML image resource in that program, that could be remove without requiring any further changes - I had already changed to handling that the same way earlier. The tower image was the only one left that I tried to handle as an XAML resource.

    One potenially problematic thing was the mutual binding between canvas and image: image.Height bound to canvas,ActualHeight and in turn canvas.Width bound to image.ActualWidth. Changing an object's size from within its very own SizeChanged handler must be done quite carefully. of course, to avoid infinite event cascades or recursions...

    Code:
    private void canvas_SizeChanged(object sender, SizeChangedEventArgs e)
    {
      Debug.WriteLine("canvas_SizeChanged(): e.NewSize = {0}, e.PreviousSize = {1}, window state = {2}",
        e.NewSize, e.PreviousSize, window.WindowState);
      if (e.WidthChanged && e.PreviousSize.Width > 0)
      {
        scrollviewer.ScrollToHorizontalOffset(scrollviewer.HorizontalOffset * e.NewSize.Width / e.PreviousSize.Width);
        double dScaleFactor = canvas.ActualWidth / m_imgTower.Source.Width;
        m_lights?.UpdateScaleFactor(dScaleFactor);
        m_moon?.UpdateScaleFactor(dScaleFactor);
        m_clFlash1?.UpdateScaleFactor(dScaleFactor);
        m_clFlash2?.UpdateScaleFactor(dScaleFactor);
        UpdateLightLevelTips();
      }
      if (e.HeightChanged)
      {
        m_imgTower.Height = e.NewSize.Height;
        m_imgTower.UpdateLayout();
        canvas.Width = m_imgTower.ActualWidth;
      }
    }
    This is a verbatim copy of the original event handler, to demonstrate the scale factor thing mentioned a few posts above, even leaving in the Debug.WriteLine() call which can be extremely helpful in such a scenario, to quickly diagnose event cascade problems.

    Quote Originally Posted by Arjay View Post
    Sorry for not being more helpful, but my wrestling with WPF skills are rusty.
    No problem. The term wrestling seems pretty adequate to describe what I experienced with XAML the past few days.

    In contrast, my XAML experience is rather few but relatively recent. I have just two apps with a GUI built entirely on WPF of which this one definitely is the graphically more advanced (ok, it has a Windows Forms About box, but the rest is pure WPF).

    And I have one hybrid app which fundamentally is Windows Forms but utilizes some WPF-based custom controls contained in System.Windows.Forms.Integration.ElementHost instances. WPF is great for rich text custom controls (not just displaying RTF content, of course, but custom content in richt text style), both flowing and non-flowing, and especially if you want to be able to choose between those, with fine-grained mouse interaction response.

    Ok, now, after that hack, I have something that works to my satisfaction. Yet, if someone reads this who can tell me how I'd get that XAML resource thinh to work, I'd really appreaciate that information.
    I was thrown out of college for cheating on the metaphysics exam; I looked into the soul of the boy sitting next to me.

    This is a snakeskin jacket! And for me it's a symbol of my individuality, and my belief... in personal freedom.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  





Click Here to Expand Forum to Full Width

Featured