Throw exception when access image from App.xaml.cs - wpf

First, try this.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="80" Width="100" Loaded="Window_Loaded">
<Image x:Name="image"/>
</Window>
and
public partial class MainWindow : Window
{
public static BitmapImage okImage = new BitmapImage(new Uri("pack://Application:,,,/ok.png"));
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
image.Source = okImage;
}
}
REMEMBER put any image named ok.png to the project directory. This runs well and shows an image in the window.
Now try to move public static BitmapImage okImage = new BitmapImage(new Uri("pack://Application:,,,/ok.png")); to App.xaml.cs.
Run again, we get TypeInitializationException. Why?
I know I can define okImage in MainWindow.xaml.cs, but if I insist it must be defined and initilized in App.xaml.cs, how should I do?

Usually TypeInitializationException iteslf is not very helpful - always check it's InnerException property.
In your case, it says "Invalid URI: Invalid port specified.", which is much more helpful.
It tells that the problem isn't image itself, it's Uri constructor which throws exception.
But why it succeeds in MainWindow.xaml.cs and fails in App.xaml.cs?
It's all about UriParser schemes. UriParser defines way of parsing some parts of Uri and is involved in Uri object initialization. When your MainWindow object is accessed for the first time, it's static constructor is called and your image is created. At this moment, your application has already registered a UriParser for pack:// scheme, and Uri parser succeeds. But when you try to do the same thing in Application.xaml.cs, custom UriParser for pack:// scheme is not yet registered, and default one fails.
Solution:
Do not instantiate your images in Application static constructor. You may still keep fields or properties and make them static if you want, but move all your image initialization code to OnStartup():
public partial class App : Application
{
public static BitmapImage okImage;
protected override void OnStartup(StartupEventArgs e)
{
okImage = new BitmapImage(new Uri("pack://application:,,,/ok.png"));
base.OnStartup(e);
}
}

Related

Where is ExecWB?

If I use WPF syntax:
<WebBrowser Grid.Row="1" Grid.Column="1" x:Name="htmlView"></WebBrowser>
Or if I add a WindowsFormsHost:
<WindowsFormsHost x:Name="formsHost" Grid.Row="1" Grid.Column="1">
</WindowsFormsHost>
And in the CS file:
public partial class MainWindow : Window
{
System.Windows.Forms.WebBrowser htmlView= new System.Windows.Forms.WebBrowser();
public MainWindow()
{
InitializeComponent();
formsHost.Child = htmlView;
htmlView.Navigate("d:\\test.xml");
}
private void menuFilePageSetup_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Do do");
}
}
Either way, the ExecWB method is not exposed. So I can't port this code over from my MFC C++ CHtmlView derived class:
ExecWB(OLECMDID_PAGESETUP, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL);
Am I missing something? At this point in time I can't implement my print preview / page setup / zoom functionality because I can't use ExecWB.
I have tried deriving my own class from the Winforms browser but it is still not listed.
Thank you.
I don't understand why in this question:
How do I programmatically change printer settings with the WebBrowser control?
It refers to ShowPrintDialog but even that is not listed.
Partial success! I don't know why, but now, when I use the hosted Winforms version, I can atleast see some of the methods:
public partial class MainWindow : Window
{
System.Windows.Forms.WebBrowser htmlView= new System.Windows.Forms.WebBrowser();
public MainWindow()
{
InitializeComponent();
formsHost.Child = htmlView;
htmlView.Navigate("d:\\test.xml");
}
private void menuFilePageSetup_Click(object sender, RoutedEventArgs e)
{
htmlView.ShowPageSetupDialog();
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
htmlView.ShowPrintPreviewDialog();
}
}
But ExecWb is still missing for managing things like zooming. I found this:
How do I print from the wpf WebBrowser available in .net 3.5 SP1?
One of the answers:
mshtml.IHTMLDocument2 doc = webBrowser.Document as mshtml.IHTMLDocument2;
doc.execCommand("Print", true, null);
But even if I add a reference to the mshtml library the compiler will not let me convert from HtmlDocument to IHtmlDocument2. Eventually I got this:
Clearing the selection in a webbrowser control
So now I know how to select all and copy to clipboard again (as long as I use the Winforms edition). However, having looked here:
https://msdn.microsoft.com/en-us/library/ms533049(v=vs.85).aspx
There seems to be no match for Optical Zoom:
ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, &vZoom, NULL);
The basic answer for calling ExecWb is to do this:
Use the WinForms WebBrowser control
Then something like htmlView.Document.ExecCommand
Pass in commands like:
SelectAll
Copy
for clipboard items.
There are native methods for displaying the print dialogues etc..
Only thing that does not seem to be supported is:
ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, &vZoom, NULL);

Simple Injector + Caliburn Micro 2.0 , error in resolving the view

I've got a strange problem with SimpleInjector and Caliburn Micro 2.0 .... I got an exception telling
"Cannot locate resource 'views/simpleviewmodel.xaml'."}
The resource is set as
Build Action : Page
If I set it to resource (loosing the .cs) it loads...
My bootstrapper is
public class MefBootstrapper : BootstrapperBase
{
public static readonly Container ContainerInstance = new Container();
public MefBootstrapper()
{
Initialize();
}
protected override void Configure()
{
ContainerInstance.Register<IWindowManager, WindowManager>();
ContainerInstance.RegisterSingle<IEventAggregator, EventAggregator>();
ContainerInstance.Register<SimpleViewModel, SimpleViewModel>();
ContainerInstance.Verify();
}
protected override object GetInstance(Type serviceType, string key)
{
return ContainerInstance.GetInstance(serviceType);
}
protected override IEnumerable<object> GetAllInstances(Type serviceType)
{
return ContainerInstance.GetAllInstances(serviceType);
}
protected override void BuildUp(object instance)
{
ContainerInstance.InjectProperties(instance);
}
protected override void OnStartup(object sender, StartupEventArgs e)
{
DisplayRootViewFor<SimpleViewModel>();
}
}
The SimpleViewModel is really simple
public class SimpleViewModel : Screen
{
}
and the view is simple as well
<UserControl x:Class="SimpleInjector.Views.SimpleView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<TextBlock Text="IT works!" Foreground="Red"></TextBlock> </Grid>
</UserControl>
I've uploaded a project to test it yourself... if in the MefBoostrapper you comment the whole bootstrapper then uncomment the other one it works...so I think it's a problem related to SimpleInjector
Any help would be appreciated
Thanks
Your problem has actually nothing to do with SimpleInjector. The problem is that you named your assembly SimpleInjector.
So when the program builds it will create a SimpleInjector.exe file. This works because simpleinjector is a class library and thus is named .dll.
But when Caliburn will search for the view, it searches for namespace SimpleInjector in an assembly SimpleInjector. But it has two of those (.dll and .exe), and will search in SimpleInjector.dll for this view, which it will not find! And there is your exception.
Just rename your assembly output file to SimpleInjector.TestApp or something. You probably know this, but to be complete: You can change this in the properties of your project on the tabpage Application, field Assembly Name

Window is not recognizing an application resource

To reproduce the error:
Create a new MVVM-Light WPF application.
Copy MainWindow.xaml to MainWindow2.xaml Rename MainWindow2's class name to MainWindow2 (and the constructor)
Rename MainWindow2 window class attribute to "x:Class="MvvmLight2.MainWindow2"
Remove StartupUri from App.xaml
Add the following to App:
protected override void OnStartup(StartupEventArgs e)
{
new MainWindow().Show();
new MainWindow2().Show();
}
Run the application and get error:
Cannot find resource named '{Locator}'. Resource names are case sensitive. Error at object 'System.Windows.Data.Binding' in markup file 'MvvmLight2;component/mainwindow.xaml' Line 10 Position 9.
To resolve the error:
Remove DataContext="{Binding Main, Source={StaticResource Locator}}" from both windows.
Add the following line to both windows' constructors:
DataContext = new ViewModelLocator().Main;
The application now runs.
The question is why doesn't it recognize the Locator even though it's defined as an application resource?
Update:
I just noticed that I can add the same resource on both xaml and code without any visible side effects. The question now becomes, is there a problem with this? Does it create a duplicate resource or it doesn't because they have the same key?
More than just hacking it, I'm trying to understand what's going on.
Managed to fix this by adding InitializeComponent() inside Application.Startup event handler:
App.xaml
<Application x:Class="SomeNamespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup"> <!-- Important to use Startup -->
App.xaml.cs
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
InitializeComponent(); // <-- Important to set this!
var window = new MainWindow();
window.Show();
}
}

Using same ResourceDictionary object in multiple controls

I have a scenario where I parse the XAML of the resource dictionary at runtime. Like following:
var parsedResourceDictionary = XamlReader.Parse(xaml) as ResourceDictionary;
This all happens inside a custom resource dictionary (derived from ResourceDictionary as base class). After parsing, I call
MergedDictionaries.Add(parsedResourceDictionary);
Since parsing the XAML takes quite some time I want to cache the parsers output and just call the add method on the MergedDictionary field.
Now, my question is if it is possible to keep a reference to this parsedResourceDictionary and add it later.
thanks
Yes. Read it once, put it in a variable somewhere and use that in future instead of reading it again. Did you try it?
I just made a small testing app where I created a ResourceDictionary from an embedded XAML:
public partial class MainWindow : Window
{
public static ResourceDictionary CachedResourceDictionary;
public MainWindow()
{
if (CachedResourceDictionary == null)
{
CachedResourceDictionary = new ResourceDictionary
{
Source =
new Uri("/ResourceDictionaryCache;component/Dictionary1.xaml",
UriKind.RelativeOrAbsolute)
};
}
Resources.MergedDictionaries.Add(CachedResourceDictionary);
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
var toOpen = new MainWindow();
toOpen.Show();
}
}
In the Button_Click event I just created a new instance of the MainWindow which then

Is there a way to start a WPF application without StartUpUri that doesn't break something else?

I've been trying for hours to get to the point where I can start a WPF application and have full control. I want to be able to create a ViewModel, create a View (Window), set the data context of the View to be the ViewModel, then show the View.
I've tried lots of methods, the most promising being to change the App.xaml to be a page and then adding my own Main method. Unfortunately this doesn't work properly because VS2010 then does not show the styles from the App.xaml in the designer, though they do work when running the app.
Is there a way to do what I want? If not, how do people normally start MVVM apps in WPF, creating a ViewModel outside of the View itself?
I would use the Startup event. You can add this to the App.xaml and remove the StartupUri line. When you add it, Visual Studio can create the event for you within the App.xaml.cs file. You can initialise your ViewModel and View within.
Here is one simple way...
<Application
x:Class="Demo.Ux.WpfApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Application>
Here is the basic App.xaml.cs
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
try
{
var mainView = new MainView();
mainView.Show();
mainView.DataContext = new MainViewModel();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
}
Application.MainWindow can be used as well. The first displayed Window will be assigned to MainWindow auto-magically. Of course, you can skip creating your mainView and write directly to MainWindow which would thin out the syntax as well.
MainWindow = new MainView();
MainWindow.Show();
MainWindow.DataContext = new MainViewModel();
One final note, I'm doing the Show before the data bind. You need to do this to avoid a situation where the MainViewModel throw an exception during creation. If the MainView hasn't been shown, the app will close without letting you see the error.
in our application, we have choosen the way which you already proposed: writing a new Main method. You also have to make some changes in the project application settings then (no startup object). The app xaml has to look something like this:
<Application x:Class="EVOCURA.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup"
Exit="Application_Exit">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--Custom Controls-->
<ResourceDictionary Source="<your resources here>"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
The code behind will look something like this:
public sealed partial class App : Application
{
static App()
{ }
public App()
{ }
private void Application_Startup(object sender, StartupEventArgs e)
{
// create the main window and assign your datacontext
MainAppWindow main = new MainAppWindow();
main.DataContext = <your datacontext here>
main.Show();
}
[STAThreadAttribute]
public static int Main(string[] args)
{
App app = new App();
app.InitializeComponent();
app.Run();
return 0;
}
}
Have a look at the Startup Event and notice, that no default StartupUri is specified im App.xaml
You could also pass the DataContext in a new constructor of your MainWindow, or create the DataContext directly in xaml.
The simplest way to assign an instance of the ViewModel to the DataContext of the view is in the code behind of the Window.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new myViewModel();
}
}
For the first part of your question, you can have the control of your application in the StartUp event
<Application x:Class="myApplication.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml" Startup="Application_Startup">
<Application.Resources>
</Application.Resources>
</Application>
Code Behind :
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
// Place your code here
}
}

Resources