Prism 6.1.0 MefBootstrapper's implementation: CreateShell method throws an exception - c#

I'm using Prism 6.1.0 for the first time.
I created a new WPF project just to try the taste of the newest prism.
My shell looks this way:
<Window x:Class="Prism.Shell"
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:Prism"
mc:Ignorable="d"
Title="Shell"
Height="300"
Width="300">
<Grid Background="HotPink"></Grid>
using System.ComponentModel.Composition;
using System.Windows;
namespace Prism
{
/// <summary>
/// Interaction logic for Shell.xaml
/// </summary>
[Export]
public partial class Shell : Window
{
public Shell()
{
InitializeComponent();
}
}
}
Bootstrapper:
using Prism.Mef;
using System.ComponentModel.Composition.Hosting;
using System.Windows;
namespace Prism
{
public class MyBootstrapper : MefBootstrapper
{
protected override DependencyObject CreateShell()
{
return Container.GetExportedValue<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Window)Shell;
Application.Current.MainWindow.Show();
}
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
AggregateCatalog.Catalogs
.Add(new AssemblyCatalog(typeof(MyBootstrapper).Assembly));
}
}
}
and App c# code is:
using System.Windows;
namespace Prism
{
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
(new MyBootstrapper()).Run();
}
}
}
After this line:
I have an exception:
Exception thrown: 'System.InvalidOperationException' in PresentationCore.dll
Additional information: The calling thread must be STA, because many UI components require this.
Does someone know how to fix that?

The exception says that your window constructor is invoked by another thread. In WPF world every control: window, button, etc. must be created by a single special thread called STAThread. Furthermore, only STAThread can access UI controls (if another thread does this, same InvalidOperationExcepiotion would be thrown). So it looks like you invoke a new thread somewhere. I can't see a mistake in your code, I also don't know whether your container starts any thread. But make sure you don't create any suspicious thread in your code.
Also look for StartupURI in App.xaml. It shouldn't be set if you use DI container. But it is not this I think.
I created an example app, I copied your code and it works. So your error is something strange.

Related

C# - WPF - Changing the inheritance of my window

Hello StackOverflow community,
I got a question regarding the inheritance of the WPF windows.
If you create a new window it inherits from System.Windows.Window - so let's just say: I want that every window that I use have following code in his constructor:
MinHeight = Height;
MinWidth = Width;
So my first attempt was that I create a new class called BaseWindow and put the code block into the constructor of BaseWindow (this method I also often used for forms application and it worked there).
namespace MyProject.Classes
{
public class BaseWindow : System.Windows.Window
{
public BaseWindow()
{
MinHeight = Height;
MinWidth = Width;
}
}
}
My BaseWindow class is now existing and now I let my window wndLogin inherit from the BaseWindow - so something like that:
using MyProject.Classes;
namespace MyProject
{
public partial class wndLogin : BaseWindow
{
public wndLogin()
{
InitializeComponent();
InitializeWindow();
}
private void InitializeWindow()
{
// Initializing window specific stuff
}
}
}
I noticed that this class is partial, so I also changed the inheritance on the other part of the class in the file wndLogin.g.i.cs - it says there, that this file is an auto generated file and should not be edited.
namespace MyProject {
/// <summary>
/// wndLogin
/// </summary>
public partial class wndLogin : System.Windows.Window, System.Windows.Markup.IComponentConnector {
// auto generated stuff here
}
}
When I edit it anyway it replaces my custom inheritance with the standard inheritance → System.Windows.Window after debugging the project.
Is there a way to disable this automatic generated code or to tell Visual Studio that it should use an other class for inheritance?
Thanks in advance! :-)
Don't hand-edit wndLogin.g.i.cs, it is generated automatically (as you found out).
In order to do this, you need to also change the XAML file to use your window:
<local:BaseWindow x:Class="MyNamespace.Window1"
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:MyNamespace"
mc:Ignorable="d"
Title="Window1">
<Grid>
</Grid>
</local:BaseWindow>
Notice the <local:BaseWindow ... instead of Window. If your BaseWindow class is in an entirely different namespace, you'll need to specify that in the xaml:
<baseWindowNs:BaseWindow x:Class="MyNamespace.Window1"
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:MyNamespace"
xmlns:baseWindowNs="clr-namespace:MyProject.Classes"
mc:Ignorable="d"
Title="Window1">
<Grid>
</Grid>
</baseWindowNs:BaseWindow>
The key line being the namespace qualifier: xmlns:baseWindowNs="clr-namespace:MyProject.Classes", and then use your namespace name in place of local from the first sample.
The only other change needed is the WindowName.xaml.cs file to inherit from the new base class:
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : BaseWindow
{
public Window1()
{
InitializeComponent();
}
}
Alternatively (and more in-line with WPF MVVM conventions) you could create a base view model that has those properties and bind your window class Height and Width to those viewmodel properties. Personally that is how I would approach this rather than create a new base class for windows.

Accessing Properties.Resources/Settings within partial subclasses of Application

I'm trying to access an icon from my resources and it's not available. It seems to be available in a window. How can I access them?
namespace My_Application
{
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
/* Compile error "'System.Collections.IDictionary' does not contain a definition for 'Resources'…" */
Properties.Resources.Application;
base.OnStartup(e);
}
}
This one works! It's in a Window Class
public partial class MainWindow : Window
{
public MainWindow()
{
/* Available */
Properties.Resources.Application;
}
}
So for some reason within App.xaml.cs, it's necessary to disambiguate using the project namespace whenever using automatic Settings or Resources classes. using directives and assembly references are verified to be irrelevant.

Override a UseControl's library

I would like to develope a WPF User Control Library that uses a Class Library with a member function that can be over-ridden . I’m using C# 4.0 and VS 2010.
My test class library looks like:
using System.Diagnostics;
namespace MyLibrary {
public class Foo {
virtual public void Bar() {
Debug.WriteLine(" Hi from MyLibrary, class Foo, method Bar.");
}
}
}
My WPF User Control looks like:
using System.Windows.Controls;
using System.Diagnostics;
using MyLibrary;
namespace MyUserControl {
public partial class UserControl1 : UserControl {
public UserControl1() {
InitializeComponent();
Debug.WriteLine("MyUserControl: ");
var foo = new Foo();
foo.Bar();
}
}
}
I have built a WPF Application called ProgramA, and it looks like:
using System;
using System.Diagnostics;
using System.Windows;
namespace ProgramA {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
}
}
When debugging ProgramA, you will see:
MyUserControl:
Hi from MyLibrary, class Foo, method Bar.
in the debug Output window. So far, so good.
I have also built ProgramB to try to override the Bar method of MyLibrary.
ProgramB looks like:
using System.Windows;
namespace ProgramB {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
}
}
}
The XML for both ProgramA and ProgramB contains a reference to MyUserControl:
<Window x:Class="ProgramB.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:my="clr-namespace:MyUserControl;assembly=MyUserControl">
<Grid>
<my:UserControl1 Name="userControl11"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
</Grid>
</Window>
I have added a Class to ProgramB's project called NewFoo.cs, and it looks like:
using System.Diagnostics;
using MyLibrary;
namespace ProgramB {
class NewFoo : MyLibrary.Foo{
override public void Bar() {
Debug.WriteLine(" Hi from ProgramB Foo, method Bar.");
}
}
}
ProgramB compiles and runs, but the output is:
MyUserControl:
Hi from library Foo, method Bar.
The override did not work.
Here is the problem. The Namespace for ProgramB’s Foo method is ProgramB, and hence it does not override the Bar method of MyLibrary.
Is there a way ProgramB can override the Bar method used by MyUserControl?
Any help or suggestions will be greatly appreciated.
Charles
Its because UserControl1 doesnt know about NewFoo and only instantiates Foo. It has nothing to do with namespaces.
The override will work, as long as you provide the correct instance.
You should expose a property in UserControl1 where you can set what foo to use.

PRISM + MEF -- Can't get regions to work properly

a bit new to Prismv4 and MEF.
I went through the QuickStarts and tried to combine two of them together, but I can't seem to get it working.
First, I got a Bootstrapper to load the Shell Window.
public sealed class ClientBootstrapper : MefBootstrapper
{
protected override void ConfigureAggregateCatalog()
{
base.ConfigureAggregateCatalog();
//Add this assembly to export ModuleTracker (Shell is in this Assembly).
AggregateCatalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
}
protected override DependencyObject CreateShell()
{
return Container.GetExportedValue<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
Application.Current.MainWindow = (Window)Shell;
Application.Current.MainWindow.Show();
}
}
This worked fine. The Shell window was shown and a nice Hello World message appeared. Then I tried to create a Region inside the Shell window so I can load a View into that region. I haven't even gotten this to work to even look at moving it to an outside assembly.
[ModuleExport(typeof(HelloWorldModule), InitializationMode = InitializationMode.OnDemand)]
public class HelloWorldModule : IModule
{
[Import(AllowRecomposition = true)]
private IRegionViewRegistry regionViewRegistry;
[ImportingConstructor()]
public HelloWorldModule(IRegionViewRegistry registry)
{
this.regionViewRegistry = registry;
}
public void Initialize()
{
regionViewRegistry.RegisterViewWithRegion("PrimaryRegion", typeof(Views.HelloWorldView));
}
}
The HelloWorld view (just a simple UserControl that contains a TextBlock) is not being loaded into the region! I guess I'm a bit lost here on how to load in my regions.
You can try this and it works for me, the Module class as follows:
[ModuleExport(typeof(HelloWorldModule))]
public class HelloWorldModule : IModule
{
[Import]
private IRegionManager regionManager;
public void Initialize()
{
Uri viewNav = new Uri("HelloWorldView", UriKind.Relative);
regionManager.RequestNavigate("PrimaryRegion", viewNav);
}
}
The View class as follows:
[Export("HelloWorldView")]
[PartCreationPolicy(CreationPolicy.Shared)]
public partial class HelloWorldView : UserControl
{
public HelloWorldView()
{
InitializeComponent();
}
}
The xaml in HelloWorldView
<UserControl x:Class="HelloWorldModule.HelloWorldView"
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>Hello World</TextBlock>
</Grid>
</UserControl>
You will need
protected override void ConfigureAggregateCatalog()
{
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Bootstapper).Assembly));
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(HelloWorldModule.HelloWorldModule).Assembly));
}
It looks like you're trying to use the view discovery approach?
Have you tried the following:
[ModuleExport(typeof(HelloWorldModule), InitializationMode = InitializationMode.OnDemand)]
public class HelloWorldModule : IModule
{
private IRegionManager regionManager;
[ImportingConstructor]
public HelloWorldModule(IRegionManager regionManager)
{
this.regionManager = regionManager;
}
public void Initialize()
{
this.regionManager.RegisterViewWithRegion("PrimaryRegion", typeof(Views.HelloWorldView));
}
}
Your ConfigureAggregateCatalog method in your bootstrapper needs to add the assembly that your view is in. Adding the line below to the end of the method should get you past your current problem.
this.AggregateCatalog.Catalogs.Add(new AssemblyCatalog(typeof(Views.HelloWorld).Assembly));
This can also be done in XAML in a ModuleCatalog.
Your Views.HelloWorld class will also need an [Export] attribute added to it.
It's hard to tell what's going wrong based on the information you share.
I would suggest looking at the examples and the reference implementation that comes with PRISM4. I guess the reference implementation and a little debugging should help you in finding the problem.
public class ModuleC : IModule
{
#region IModule Members
/// <summary>
/// Initializes the module.
/// </summary>
public void Initialize()
{
/* We register always-available controls with the Prism Region Manager, and on-demand
* controls with the DI container. On-demand controls will be loaded when we invoke
* IRegionManager.RequestNavigate() to load the controls. */
// Register task button with Prism Region
var regionManager = ServiceLocator.Current.GetInstance<IRegionManager>();
regionManager.RegisterViewWithRegion("TaskButtonRegion", typeof(ModuleBTaskButton));
/* View objects have to be registered with Unity using the overload shown below. By
* default, Unity resolves view objects as type System.Object, which this overload
* maps to the correct view type. See "Developer's Guide to Microsoft Prism" (Ver 4),
* p. 120. */
// Register other view objects with DI Container (Unity)
var container = ServiceLocator.Current.GetInstance<IUnityContainer>();
container.RegisterType<Object, ModuleBRibbonTab>("ModuleBRibbonTab");
container.RegisterType<Object, ModuleBNavigator>("ModuleBNavigator");
container.RegisterType<Object, ModuleBWorkspace>("ModuleBWorkspace");
}
}

WPF UserControl with generic code-behind

I have this code behind:
CustomUserControl.xaml.cs
namespace MyProject
{
public partial class CustomUserControl<T> : UserControl
{
...
}
}
and this xaml:
CustomUserControl.xaml
<UserControl x:Class="MyProject.CustomUserControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Grid>
</Grid>
It doesn't work since the x:Class="MyProject.CustomUserControl" doesn't match the code-behind's generic class definition. Is there some way to make this work?
You can create generic "code-behind" file without XAML-file:
public class CustomUserControl<T>: UserControl
{ }
and than derive from it providing specific class as a parameter:
public partial class SpecificUserControl : CustomUserControl<Presenter>
{
public SpecificUserControl()
{
InitializeComponent();
}
}
XAML:
<application:CustomUserControl
x:TypeArguments="application:Presenter"
xmlns:application="clr-namespace:YourApplicationNamespace"
...
Unfortunately, it seems that Visual Studio designer doesn't support such generics until Visual Studio 2012 Update 2 (see https://stackoverflow.com/a/15110115/355438)
Haven't found my solution anywhere else so far.
The main difference is, that I have got one .xaml file
for the generic user control class and not one for each actual user control.
GenericUserControl.xaml.cs
using System.Windows.Controls;
namespace TestGenericUserControl
{
public abstract partial class GenericUserControl : UserControl
{
// If you use event handlers in GenericUserControl.xaml, you have to define
// them here as abstract and implement them in the generic class below, e.g.:
// abstract protected void MouseClick(object sender, MouseButtonEventArgs e);
}
public class GenericUserControl<T> : GenericUserControl
{
// generic properties and stuff
public GenericUserControl()
{
InitializeComponent();
}
}
// To use the GenericUserControl<T> in XAML, you could define:
public class GenericUserControlString : GenericUserControl<string> { }
// and use it in XAML, e.g.:
// <GenericUserControlString />
// alternatively you could probably (not sure) define a markup extension to instantiate
// it directly in XAML
}
GenericUserControl.xaml
<UserControl x:Class="TestGenericUserControl.GenericUserControl"
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">
<Grid>
<Label Content="hello" />
</Grid>
</UserControl>
Unfortunately XAML does not support generic code behind, thou you can walk around this.
See links below:
http://forums.silverlight.net/forums/p/29051/197576.aspx
Can I specify a generic type in XAML (pre .NET 4 Framework)?
May be generic controls will be supported natively in future versions of Visual Studuo with
XAML 2009.
I ended up with an extra helper class, this is not a UserControl. It does the job well for a simple custom UserControl that needs a Generic Type.
So the UserControl, for instance UCMoveObject, has an InitFunction that returns to me the Helper instance:
// In the UCMoveObjects UserControl Class.
public UCMoveObjectsHelper<T> InitUCMoveObject<T>(List<T> argAllList, List<T> argSelectedList)
{
return new UCMoveObjectsHelper<T>(this, argAllList, argSelectedList);
}
Then in the helper class I place all the code for making the user control work.
public class UCMoveObjectsHelper<T>
{
public UCMoveObjectsHelper(UCMoveObjects argUserControl, List<T> argAllList, List<T> argSelected)
{
_ucMoveObjects = argUserControl;
// Example reaching the usercontrol
_ucMoveObjects.listBox.SelectionChanged += LbAll_SelectionChanged;
// Do whatever I want with T
}
public List<T> ReturnSelected()
{
// Code that return a List<T>;
}
}
In the MainWindow.xaml i place a usercontrol in the window. Then in code I do the following:
_ucMovingObjectsHelper = ucMovingObjects.InitUCMoveObject<TblUsers>(tempListAll, tempListSelected);
And later in my code I can call:
var tempSelectedList = _ucMovingObjectsHelper.ReturnSelected();

Categories

Resources