Using IronPython and WPF in VS2017:
I have added an icon located in my code source directory to the XAML Window tag. I've also included the file in the project in the solution explorer. If I use the full file C: drive file path, it works. But, I need to use a relative path because it will be run on other computers.
If I try Icon="../xxx.ico" the file isn't found (squiggly line, "Cannot find...).
If I try Icon="xxx.ico" the file IS found while editing, however at run-time I get an exception
Exception: Failed to create a 'Icon' from the text 'xxx.ico'.
It seems that the Python interpreter cannot locate it.
What do I need to do so that it is found at run-time?
Ran into this same issue myself and have found a solution, albeit a little late!
It does indeed seem that IronPython interprets the field as text unless it determines a valid uri is present.
We can get around this via code, a bit of reading into c# and how it handles icons was needed here;
Dynamically setting our path and file
location at runtime.
Create a BitmapImage
after casting our file location as a c# Uri
Update our xaml to give our window an x:Name=""
Set our window.Icon to the BitmapImage we defined earlier
XAML
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xml:lang="en-AU"
x:Name="window">
</Window>
Code behind
import os
import clr
clr.AddReference('System')
from System import Uri
from System.Windows.Media.Imaging import BitmapImage
class Application(Window):
def __init__(self):
## Set our icon file path dynamically based off working directory ##
icon = os.path.join(os.getcwd(), 'app-logo.ico')
## Define BitmapImage from Uri(filepath) ##
self.icon = BitmapImage(Uri(icon))
## Load our wpf component, defined under self.ui to allow easy access ##
self.ui = wpf.LoadComponent(self, 'application.xaml')
## Set our XAML x:Name="window"'s Icon field to our BitmapImage
self.ui.window.Icon = self.icon
Thats it! Our Icon is now set and working as intended.
Taskbar:
Application Icon:
Related
I've added a XAML file to a Windows Phone 8 project. Its build action is "Page". I want to load the XAML as a text string (to feed into XamlReader.Load()). How can I accomplish this?
It's not available as a separate file in the XAP package; it's probably somewhere in the DLL.
When set to Page, the compiler will compile the XAML into BAML and add the BAML file as a resource to the assembly.
If you wish to get the original XAML back out from the BAML resource at runtime, then you will need to deserialize the BAML, and then serialize your object to XAML.
You can have a look at the Baml2006Reader, or a better option would be to use Application.LoadComponent which is what the InitializeComponent method uses internally. InitializeComponent is called by the partially generated class for your XAML code behind.
var uri = new Uri("/MyAppName;component/MyXaml.xaml", //Note extension: XAML, not BAML
UriKind.Relative);
Page rootObject = new Page(); //Assuming XAML root element is Page - it could be anything
Application.LoadComponent(rootObject, uri);
(assuming the root element of your XAML file is a Page).
You can then serialize the Page to a XAML string using the XamlWriter:
string xaml = XamlWriter.Save(rootObject);
Note that this is the XamlWriter in the System.Windows.Markup namespace, not System.Xaml. If your XAML has WPF types, then you should use this XamlWriter to avoid errors.
I've created a WPF application that contains some control templates which we can call 'default templates' and these templates are compiled into the application. This application also loads an external XAML file at runtime with additional control templates which can call 'custom templates'.
Everything works fine until I add a Callout control from the Expression Blend SDK to the 'custom' templates XAML that get loaded at runtime and then try to use that template. I get the following exception:
Cannot create unknown type '{http://schemas.microsoft.com/expression/2010/drawing}Callout'.
I noticed that if I put that Callout control in my 'default templates' file (the one that is compiled) and first use that default template, the then load and use the 'custom templates' it will work.
It seems to me that the referenced expression sdk assembly is not being loaded when I add my 'custom templates' to my MergedDictionaries. Any ideas here?
This is the xmlns declaration at the top of the XAML file:
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing
And this is the sub where I am adding the resource dictionary at runtime:
Private Sub LoadResourceFileButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Try
Dim path As String = "\ResourceFiles\CustomMapSymbols.xaml"
Dim resDict As New ResourceDictionary
resDict.Source = New Uri(path, UriKind.Relative)
Application.Current.Resources.BeginInit()
Application.Current.Resources.MergedDictionaries.Add(resDict)
Application.Current.Resources.EndInit()
Catch ex As Exception
MessageBox.Show(ex.ToString)
End Try
End Sub
I have similar symptoms in my situation where I get the following error:
Cannot create unknown type
'{htp://schemas.microsoft.com/expression/2010/drawing}Callout'.
However it works fine in a user control that is compiled in the application. This made me think that the ResourceDictionary implementation maybe doesn't handle the 'pretty' namespaces (htp://...) the same was as in the app.
So I tried referencing the explicit clr namespace directly in the declaration and it now works. I obviously had to change references in the rest of the xaml to ef:Callout. Easily done with auto replace. I didn't want to replace ed: with it in case I break Expression Blend that might be relying on it.
Old namespace declaration in the external resource dictionary:
xmlns:ed="htp://schemas.microsoft.com/expression/2010/drawing"
Sorry: I had to replace the http to htp to to post this answer.
New namespace declaration in the external resource dictionary:
xmlns:ef="clr-namespace:Microsoft.Expression.Controls;assembly=Microsoft.Expression.Drawing"
I found where the exact namespace was of the Callout class by using the Object Browser.
We have some WPF modules which are hosted in a cpp unmanaged/managed application. This enviroment is causing troubles when specifying relative Uri's for media content. For example, I have no problem of doing something like this in a testapp:
<MediaElement Grid.Row="0" x:Name="player" Source="media\Getting started with.wmv" LoadedBehavior="Manual" UnloadedBehavior="Stop" Stretch="Fill"
MediaOpened="Element_MediaOpened" MediaEnded="Element_MediaEnded"/>
But, as mentioned, this does not work in production code.
If I try to use the pack schema like this:
Source="pack://application:,,,/media/Getting started with.wmv"
I get the exception:
Cannot navigate to application resource 'pack://application:,,,/media/Getting started with.wmw' by using a WebBrowser control. For URI navigation, the resource must be at the application�s site of origin. Use the pack://siteoforigin:,,,/ prefix to avoid hard-coding the URI.
If I try to use the 'siteoforigin' schema like this:
Source="pack://siteoforigin:,,,/media/Getting started with Olga 7.wmv"
I get another error:
Application identity is not set.
The media file is set up as "Content" and with "copy always".
How can I specify the MediaElement source using an absolute uri in a Wpf desktop application?
I found a solution (kinda). I am guessing that the relative url's can not be resolved because the main .exe is an cpp mfc application. So in order to create absolute uri's I did something like this:
player.Source = new Uri(CreateAbsolutePathTo("media/Getting started with.wmv"), UriKind.Absolute);
private static string CreateAbsolutePathTo(string mediaFile)
{
return Path.Combine(new FileInfo(Assembly.GetExecutingAssembly().Location).DirectoryName, mediaFile);
}
I am binding to a viewmodel so this logic is wrapped into a property on the viewmodel and the source is databound in xaml.
Its working, but its not as pretty as I would like it to be.
Only you need to do this:
MediaElement bb = new MediaElement();
stage.Children.Add(bb);
bb.Source = new Uri("Recursos/MagicWandNoise.wav", UriKind.Relative);
Debug.WriteLine("URL:" + bb.Source);
bb.LoadedBehavior = MediaState.Manual;
bb.Play();
And then add the Binary Resources in your folder debug, check this link
Remember the media element to work fine, you need to add in a Visual Three
Canvas.Children.Add(bb);
Wondering how to accomplish setting the Style xaml with the code in F#. The code is simple enough:
this.DefaultStyleKey <- typeof<MyControl>
In a C# project the build options allow you to mark the XAML as a resource custom build command of: MSBuild:Compile
I don't see it in the properties panel, so I tried to add it by hand to the project file myself...
Any ideas? The application loads - the custom control has no output (but the code executes).
Thanks
UPDATE:
I checked the manifests and the resource was included as expected between my project and the project I am porting... Looking for a next step.
UPDATE 2:
Well it may be included in the manifest OK - but it is not being "compiled" as the C# version of the project throws an error in the build process when I malform the XML while the F# version allows the malformed XML to be brought into the application.
UPDATE 3:
Loading the XAML is fine now (i guess) however I am having some issues with the properties of the control:
static member ItemsProperty : DependencyProperty =
DependencyProperty.Register(
"Items",
typeof<MyMenuItemCollection>,
typeof<MyMenu>,
null);
member this.Items
with get () : MyMenuItemCollection = this.GetValue(MyMenu.ItemsProperty) :?> MyMenuItemCollection
and set (value: MyMenuItemCollection) = this.SetValue(MyMenu.ItemsProperty, value);
The problem occurs on access:
for menuItem in this.Items do
let contentElement: FrameworkElement = menuItem.Content
where I get a null pointer exception on this.Items; however I have it initialized in the constructor:
do
this.Items <- new CoolMenuItemCollection()
The C# style of compilation of XAML files is not supported by the F# tools for Visual Studio, so there is no way to get the same behavior as in C#. I think you have two options:
Create a C# project with XAML files and reference F# library which implements the core functionality (or reference C# library from F# and load user interface from the C# library in your F# application)
Use XamlReader object (see MSDN) and load the XAML file (embedded in resources in the simple way) programmatically. You won't get any of the C#-compiler generated features (e.g. named properties for all objects with x:Name), but otherwise, it should work in the usual way.
Normally, all resources are put in app.xaml, or other resources xaml files (as resource dictionary) and then reference it in app.xaml.
When applying prism pattern, for those module, there is no app.xaml file. Application class is replaced by a class implementing interface IModule. So where is the right place for resources used by controls in the module?
this is how i do it: have modules register resources with the app.
Composite WPF (Prism) module resource data templates
You can add the resource dictionary in the same assembly as the module or in other loaded assembly, then access it programmatically by using the Application.GetResourceStream method; however you will need to know the name of the assembly where the resource is defined.
For example, if your assembly is named TheAssembly and the resource dictionary is named TheDictionary.xaml, you can do (Disposes not shown for brevity):
StreamResourceInfo sr = Application.GetResourceStream(
new Uri("/TheAssembly;component/TheDictionary.xaml", UriKind.Relative));
StreamReader r=new StreamReader(sr.Stream);
string xaml=r.ReadToEnd();
ResourceDictionary rd = (ResourceDictionary)XamlReader.Load(xaml);
From here, you can for example use the Unity container to make the resource dictionary available application-wide.
UPDATE
The following version avoids having to hard-code the assembly name, provided that the resource is in the currently executing assembly:
string assemblyName = Assembly.GetExecutingAssembly().FullName.Split(',')[0];
string uri = string.Format("/{0};component/Dictionary1.xaml", assemblyName);
StreamResourceInfo sr = Application.GetResourceStream(
new Uri(uri, UriKind.Relative));
//etc...