Silverlight without XAML makes source URIs fail? - silverlight

I'm in the process of removing the XAML from my Silverlight project and making it use only code (as per this article).
Here is my very simple startup event for a Silverlight application (with the standard App.xaml from the template project):
private void Application_Startup(object sender, StartupEventArgs e)
{
Grid grid = new MainPage();
this.RootVisual = grid;
var mediaElement = new MediaElement();
mediaElement.MediaFailed += (s, ea) => { mediaFailed = true; };
mediaElement.Source = new Uri(#"/Content/Some Music.mp3", UriKind.Relative);
grid.Children.Add(mediaElement);
}
Where the MP3 file is set to "Build Action: None, Copy if newer" (ie: it's beside the XAP). Here's the XAML for MainPage:
<Grid x:Class="TestGame.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Grid>
And the C# - nothing unusual here at all.
public partial class MainPage : Grid
{
public MainPage()
{
InitializeComponent();
}
}
That all works so far. So my question is this: why is it that when I change
Grid grid = new MainPage();
to
Grid grid = new Grid();
the mediaElement.MediaFailed event gets called (with a AG_E_NETWORK_ERROR)?
The only interesting thing that InitializeComponent is doing is calling Application.LoadComponent (it's the default generated code). So what might that function be doing that allows source URIs to work?
It seems that Application.GetResourceStream still works just fine. But I need to be able to get a few resources external to the XAP.
(Note: it seems this guy is having the same problem - but no one answered his question.)

The key factor is UriKind.Relative. The question is to what is it relative?
One of the effects of LoadComponent is that it shifts the location of "/". Before LoadComponent executes the path "/" refers to the same location as it would do in the host browser. After LoadComponent "/" refers to a hybrid of the root contents of the Xap and the folder that contains the Xap.
Since you are running this via a standalone test html page "/" in your second example refers to the root of the physical drive, e.g. "c:\".
If you change the Url to "Content/Some Music.mp3" (that is remove the "/" prefix) and assuming your test html page is the same folder as the Xap it should work as expected.
Note you can't escape the hybrid path with the parent path "..", Silverlight doesn't let you do that.

Related

opening WPF user control winform window shrinks parent winform window

I have a WPF user control like this...
namespace WpfApplication1
{
public partial class MyControl : UserControl
{
......
}
}
I also have a win form to contain this WPF user control...
namespace WindowsFormsApplication4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
ElementHost ctrlHost = new ElementHost();
ctrlHost.Dock = DockStyle.Fill;
WpfApplication1.MyControl win = new WpfApplication1.MyControl();
ctrlHost.Child = win;
this.Controls.Add(ctrlHost);
}
}
}
I have one more parent win form that has a button. Clicking the button will open the Form1 that contains the ElementHost.
namespace WindowsFormsApplication4
{
public partial class Parent : Form
{
public Parent()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Form1 form1 = new Form1();
form1.Show();
}
}
}
My application runs the Parent form by default...
Application.Run(new Parent());
The problem I'm facing is strange. When i run the application, the parent form opens and on clicking the button the child window form containing the WPF control also opens. But the problem is the size of parent form window automatically shrinks(the window displaces, restores itself and the controls and font becomes smaller in it.) as soon as the WPF control form pops up. If I comment the content of the 'Form1_Load' function then the parent window does not shrink. To check the worst case i commented everything in 'Form1_Load' except
ElementHost ctrlHost = new ElementHost();
line. The mere presence of this line itself makes the parent form shrink as I mentioned earlier. I tried to search in internet for this problem extensively. I was not able to find a solution. Please help me with a answer. I'm exhausted....
I commented above that I had the same issue and have since resolved it. I'm writing up my changes here for anyone else.
As observed above, the behaviour seems to occur whenever using Windows UI scaling in an WinForms application and the Just In Time (JIT) Compiler processes anything from the WPF libraries. In my case, entering a method that contains code that opens the WPF version of MessageBox will make it happen.
Ordinarily Windows will handle basic scaling for you, rendering to a bitmap offscreen and then drawing it on screen but scaled up. When WPF loads it seems to take over, as if it's saying to Windows, "Hey.. i got this..". After that Windows stops scaling the WinForms for you and you end up with the 1x scale version and often some confused controls. The WPF portion however is handling its own scaling and looks fine.
So the way I went about solving it was to tell Windows that I would handle the WinForms scaling. To enable that you have to add this to your application manifest (dll manifest is ignored)
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
OR uncomment the following section if it is already in there:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
You can add a manifest file:
Right click on application project -> Add -> New item... -> Application Manifest File
Then in...
Application Project -> Properties -> Application -> Resources
Make sure "Manifest" is set to app.manifest
You can now find that file and add the XML above into the root <asmv1:assembly>element.
If you've taken the default application manifest and added that element it probably looks something like this
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
</asmv1:assembly>
Now when you start your WinForms app you will notice it's much crisper because it's being rendered at high dpi instead of 96dpi and then scaled up to fill the space.
You'll probably notice that a lot of your images have shrunk!!
In my case Buttons, MenuStripItems and ToolStripItems did not scale as desired.
What I found was that most controls have a method that you can override as below
protected override void ScaleControl(SizeF factor, BoundsSpecified specified)
{
base.ScaleControl(factor, specified);
}
This is called when the application launches and filters through your controls from the main form. My Windows is set to 200%, my main form's scaling mode was set to DPI and all the forms were designed at 100% scale (96dpi). I changed everything in my first attempts to fix the problem to inherit the scaling mode and this was what worked for me, if you're using font or none i suspect it will work just the same but I haven't tried it.
As mine was 200% UI scaling when this method was called factor was simply {2.0, 2.0} which I then used to recreate a scaled Image in Buttons, and to increase the ImageScalingSize on each of the Items of MenuStrip and ToolStrip since these do not receive the ScaleControl call. If you never added the XML above then this method is still called but will only ever have {1.0, 1.0} for factor, which isn't helpful.
Note: if you're using image list then don't dynamically set the image if in DesignMode or the ImageList will become unset and when you save then nothing will be set
Also not that factor is a factor from the current. What you will notice is if you move the application between different dpi monitors you will get 2.0, then 0.5, then 2.0, then 0.5, etc.
Now my WinForms application looks super crisp and it can call WPF ui elements without going crazy! yyayyyyy
Hope this helps someone

set the silverlight start page

How do I set the Startup page in Silverlight?
Not sure if am googling for the wrong terminology or it just does not seem to be mentioned anywhere.
Cheers
The term "Startup page" is somewhat ambiguous. Inside a Silverlight application you probably mean one of a few things.
The initial UserControl to load as the RootVisual
In app.xaml.cs you will find code like :-
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new MainPage();
}
Where MainPage is the user control that is the initial root visual. You can change this is your own choice.
Perhaps though you want to set the RootVisual to one of a number of possible choices. In which case you would need to use InitParams. Something like:-
private void Application_Startup(object sender, StartupEventArgs e)
{
Type t = Type.GetType("SilverlightApplication1." + e.InitParams["StartupPage"]);
this.RootVisual = Activator.CreateInstance(t);
}
You then need to include the InitParams value in the <object> tag in the host HTML:-
<object ...>
...
<param name="InitParams" value="StartupPage=Page1" />
</object
Use the navigation framework
Another approach would be needed if you building a navigation application. In this case the MainPage will contain a Frame with a Source proeperty that would contain the initial URL to map.
With this type application you could specify alternative pages to load by simply adding a path following the # in the url of the page.

Cannot show up WPF application when setting MainWindow manually and composing application (MEF)

I got my hands om MEF for a week now and I am trying to build up a WPF application that loads imported controls from MEF.
I created a WPF application project and removed the default window and application start up URI. Then I handled the application startup event to compose the application:
public partial class App : Application, IPartImportsSatisfiedNotification
{
{...}
private void App_Startup(object sender, StartupEventArgs e)
{
this.Compose();
}
public void Compose()
{
try
{
globalCatalog.Catalogs.Add(new DirectoryCatalog(extensionsDirectoryPath));
CompositionContainer container = new CompositionContainer(globalCatalog);
container.ComposeParts(this);
}
catch (Exception ex)
{
// Do something
}
}
{...}
}
Actually, when debugging and watching objects after imports are satisfied, everything has hierarchically composed fine like I wanted. But when I try to show up the MainWindow of the application an exception is thrown on MainWindow.Show() call:
"Specified element is already the logical child of another element. Disconnect it first."
Though my code in OnImportsSatisfied method seems fine as it is working when not using MEF mecanism:
public void OnImportsSatisfied()
{
Window mainWindow = new Window();
mainWindow.Content = this.importedControl;
this.MainWindow = mainWindow;
this.MainWindow.Show();
}
I insist on the fact that this works perfectly when not importing controls with MEF. What is surprising is that this code does not work too:
Window mainWindow = new Window();
//mainWindow.Content = this.importedControl;
this.MainWindow = mainWindow;
this.MainWindow.Show();
So I suspect that ComposeParts is doing a bit more than what it says as it is the only member acting on my actual application instance.
Hope someone can help me (Glenn?).
Thanks.
Edit:
I discovered that when I remove the IPartImportsSatisfiedNotification interface from my parts, no exception is thrown and the window shows up. But of course the window is empty as I need this OnImportsSatisfied method to set the DataContext of the window to its associated imported view model.
The sample applications of the WPF Application Framework (WAF) show how to use MEF within a WPF application.
I finally discovered that I was importing my WPF user controls by using the default ImportAttribute constructor, which in fact will make a shared instance of the class if the creation policy is not specified during export. And as many of my controls were implementing the same interface and I was binding them in my views, I was actually trying to add this shared user control instance to different visual elements, which is not permited by WPF (and so the exception).
I marked my imports using the RequiredCreationPolicy set to NonShared and everything got back in order! That was all about learning MEF...

XAML resources aren't loaded when calling from different project

I have a WPF project with some styles in XAML in Application.Resources. This works perfectly fine. But when I open a window from this project from another one (this one is a console application), the resources from XAML aren't loaded. When I first tried it, I got a XamlParseException on StaticResource calls in XAML, so I changed it to DynamicResource and now the style just doesn't get loaded. How do I fix this?
The code I use:
[STAThread]
static void Main()
{
App app = new App();
MyWindow wnd = new MyWindow ();
wnd.Show();
app.Run();
}
You should call the Run method that takes a Window parameter. In your current code, you're creating and showing the window before running the app, which means the application resources aren't loaded yet.
Try:
App app = new App();
MyWindow wnd = new MyWindow();
app.Run(wnd);

How to change StartupUri of WPF Application?

I am trying to modify App.cs and load the WPF XAML files from code behind but its not working as it should.
No matter whatever I try to set as StartupUri it doesnt start, the program quits after this.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
LoginDialog dlg = new LoginDialog();
if (dlg.ShowDialog() != true)
return;
switch (dlg.ChoiceApp) {
case ChoiceApp.CustomerEntry:
StartupUri = new Uri("/MyApp;component/Forms/CustomerEntry.xaml",
UriKind.Relative);
break;
case ChoiceApp.VendorEntry:
StartupUri = new Uri("/MyApp;component/Forms/VendorEntry.xaml",
UriKind.Relative);
break;
}
}
}
Now I even did trace and found out that LoginDialog is working correctly and is returning values correctly but setting "StartupUri" does not work.
I checked in reverse assembly that DoStartup method of App gets called after OnStartup, so technically my StartupUri must load, but it doesnt, in App.xaml startup uri is not at all defined.
Note: Bug Confirmed
I noticed that ShowDialog sets Application.MainWindow and when dialog ends, it sets it back to null, and because of this setting StartupUri does not work after calling Modal Dialog in OnStartup or Startup event.
There is no error or exception about invalid uri or anything like that.
This method works without DialogBox being called in Startup event or OnStartup, i think calling showdialog on this method causes something like its mainwindow being set to expired window and it shuts down after this.
Akash, I ran into this exactly issue trying to implement a LoginDialog just like yours. The dialog does not have a bug, but rather the behavior is by design.
Not a bug. The default ShutdownMode of
Application is OnLastWindowClosed, so
as soon as the first window is closed
your application will start shutting
down! Change to OnExplicitShutdown and
it will work, but you'll have to
manage the shutdown.
See this previous StackOverflow question: WPF ShowDialog returns null immediately on second call
instead of overriding the OnStartup() method, hook into the event instead.
in the XAML
<Application x:Class="SOTestWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup">
<Application.Resources>
</Application.Resources>
</Application>
in the code behind:
private void Application_Startup(object sender, StartupEventArgs e)
{
var rnd = new Random();
if (rnd.NextDouble() > 0.5)
StartupUri = new Uri("/SOTestWPF;component/Window1.xaml", UriKind.Relative);
else
StartupUri = new Uri("/SOTestWPF;component/Window2.xaml", UriKind.Relative);
}
This is only my test case and I have verified that it performs correctly (randomly :D)
Just try in OnStartup() :
StartupUri = new Uri("Forms/CustomerEntry.xaml", UriKind.Relative);
Do you still have a StartupUri specified in the XAML? If so, remove it and see if that helps.MSDN Source
If not, you may need to approach this differently: have your Dialog as your startup, then from that point open another Window based on the selected value.

Resources