tags:

views:

2441

answers:

4

I have found some evidence that this is possible,

self.scene = Canvas()
Application.LoadComponent(self.scene, Uri('app.xaml', UriKind.Relative))

but my code is failing:

class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        Canvas scene = new Canvas();
        Application.LoadComponent(scene, new Uri("app.xaml", UriKind.Relative));
    }
}

I am using the same app.xaml with 'Build Action: None', and 'Copy always".

<Canvas
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="System.Windows.Controls.Canvas"
    x:Name="ball_design" >

    <Canvas x:Name="workaround_canvas" >

        <TextBlock x:Name="fps" Canvas.Left="10" Canvas.Top="10" Height="40" Canvas.ZIndex="10000" Text="-- fps" />
    <Canvas x:Name="wpfe_ball_0" Width="52" Height="52" Canvas.Left="0" Canvas.Top="30">
      <!-- Layer 3/<Group>/<Path> -->
      <Path Opacity="0.900000" StrokeThickness="2.000000" Stroke="#ffa6d000" StrokeMiterLimit="1.000000" Fill="#ffcbff00" Data="F1 M 51.000000,26.000000 C 51.000000,39.806641 39.807129,51.000000 26.000000,51.000000 C 12.192871,51.000000 1.000000,39.806641 1.000000,26.000000 C 1.000000,12.193359 12.192871,1.000000 26.000000,1.000000 C 39.807129,1.000000 51.000000,12.193359 51.000000,26.000000 Z"/>

      <!-- Layer 3/<Group>/<Path> -->
      <Path Opacity="0.740000" Data="F1 M 43.143066,13.087891 C 50.602051,22.888672 49.009766,36.642578 39.590332,43.812500 C 30.170898,50.980469 16.489258,48.842773 9.032715,39.042969 C 1.573242,29.240234 3.166016,15.486328 12.584961,8.316406 C 22.003906,1.149414 35.685547,3.285156 43.143066,13.087891 Z">
        <Path.Fill>
          <RadialGradientBrush MappingMode="Absolute" GradientOrigin="156.791016,170.453125" Center="156.791016,170.453125" RadiusX="53.626404" RadiusY="53.626404">
            <RadialGradientBrush.GradientStops>
              <GradientStop Offset="0.000000" Color="#ffffffff"/>
              <GradientStop Offset="0.361685" Color="#fff5f7dd"/>
              <GradientStop Offset="0.415730" Color="#ffebf0bc"/>
              <GradientStop Offset="1.000000" Color="#ffcbff00"/>
            </RadialGradientBrush.GradientStops>
            <RadialGradientBrush.Transform>
              <MatrixTransform Matrix="1.190000,0.165000,-0.165000,-1.281300,-113.414185,241.757843" />
            </RadialGradientBrush.Transform>
          </RadialGradientBrush>
        </Path.Fill>
      </Path>

      <!-- Layer 3/<Group>/<Path> -->
      <Path Fill="#ffffffff" Data="F1 M 23.100586,9.477539 C 24.741699,11.634766 23.116211,15.630859 19.470703,18.404297 C 15.825684,21.178711 11.540039,21.678711 9.899414,19.522461 C 8.258301,17.365234 9.883789,13.369141 13.529297,10.594727 C 17.174316,7.821289 21.459961,7.321289 23.100586,9.477539 Z"/>
    </Canvas>
    <TextBlock x:Name="dbgwin" FontSize="10" Canvas.Top="10" Canvas.Left="250" Height="500" Width="200" Text="IronPython DLR" />

  </Canvas>
</Canvas>

I get IOException: Cannot locate resource 'app.xaml'.

What is a solution?

A: 

You will need Absolute File URI including entire file://c:/ etc.. because Relative is used inside the application's resources only.

Akash Kava
Now I get ArgumentException: Cannot use absolute URI.
alex2k8
+1  A: 

It seems that external XAML file can't be loaded by LoadComponent.

I checked the source code:

    public static void LoadComponent(Object component, Uri resourceLocator) 
    {
        ...

        // Passed a relative Uri here.
        // needs to resolve it to Pack://Application.
        //..\..\ in the relative Uri will get stripped when creating the new Uri and resolving to the 
        //PackAppBaseUri, i.e. only relative Uri within the appbase are created here
        Uri currentUri = new Uri(BaseUriHelper.PackAppBaseUri, resourceLocator); 

        ...
    }

So the resourceLocator SHOULD be a relative path. And it will be treated under application:/// authority.

MSDN

WPF supports two authorities: application:/// and siteoforigin:///. The application:/// authority identifies application data files that are known at compile time, including resource and content files. The siteoforigin:/// authority identifies site of origin files.

Possible data files are:

  • Packages and parts are analogous to applications and files, where an application (package) can include one or more files (parts), including:

  • Resource files that are compiled into the local assembly.

  • Resource files that are compiled into a referenced assembly.

  • Resource files that are compiled into a referencing assembly.

  • Content files.

  • Site of origin files.

The first 4 files are accessible with application://, but I am looking for external files, so the only option is "Content file".

So I turned app.xaml into content file (details are here)

  1. Build Action to Content
  2. Copy to Output Directory to Copy always
  3. [assembly: AssemblyAssociatedContentFile("app.xaml")]

As result such exception was thrown: 'application/xaml+xml' ContentType is not valid.

    public static void LoadComponent(Object component, Uri resourceLocator) 
    {
        ...

            if (!MimeTypeMapper.BamlMime.AreTypeAndSubTypeEqual(contentType)) 
            {
                throw new Exception(SR.Get(SRID.ContentTypeNotSupported, contentType)); 
            }
        ...
    }

So the LoadComponent expects 'application/baml+xml' instead of 'application/xaml+xml'.

I don't know a way to store xaml as external file in 'application/baml+xml', so assume the task has no solution.

alex2k8
The latest research shows that having BAML we can load it. Will change response latter.
alex2k8
A: 

I got this to work. My specific goal was to load a ResourceDictionary into the App class so I could have custom skins loaded from raw XAML files. Here's how. First, in App.xaml, Add the following:

    protected override void OnStartup(StartupEventArgs e) {
        base.OnStartup(e);
        LoadSkin("Skins/Skin1.xaml");

    }

    private void LoadSkin(string FilePath) {
        XmlReader XmlRead = XmlReader.Create(FilePath);
        Application.Current.Resources = 
            (ResourceDictionary)XamlReader.Load(XmlRead);
        XmlRead.Close();
    }

Create a "bin\Debug\Skins" folder and added a "Skin1.xaml" with the following content:

<ResourceDictionary 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
    <Style x:Key="ButtonAStyle" TargetType="{x:Type Button}">
        <Setter Property="Control.Background" Value="Green" />
        <Setter Property="Control.Foreground" Value="White" />
    </Style>
</ResourceDictionary>


Specific to Skins

In this particular case, you have to make sure you are using the style resources correctly in your individual windows. Here's an example:

<Button 
    Name="button1" 
    Style="{DynamicResource ButtonAStyle}" 
    Click="button1_Click"
>Button</Button>

Here's a neat trick to try. In button1_Click(), try adding the same code you did in App.OnStartUp() above but with "Skin2.xaml" with "Red" as the background color for your buttons. The skin changes instantly without requiring a reload of the app or window.

Jim Carnicelli
1. What is your VS version? I don't see such build action "Content Copy to Output" in VS2008? 2. Are you sure that Skin1.xaml is ONLY copied, and not compiled into .exe as a resource?
alex2k8
I'm using VS 2008. Looks like I fooled myself, though. This works for me if I set "Build Action" to "Content". If I just put loose files in bin\Debug\Skins\, it doesn't work. :-P I'm looking for a fix now.
Jim Carnicelli
Fixed. The key was using a XamlReader instead of Application.LoadComponent(). I've updated the code here to reflect it.
Jim Carnicelli
Unfortunately, XamlReader.Load will work only for xaml files with out events in it
amazedsaint
A: 

In order for LoadComponent to work on an XAML resource, it will need to have a build action of "Page". Then the LoadComponent works, even in a Console app.

This could be useful when wanting to access sample data generated from Blend in a server app.

Wolfgang Grinfeld