I have a UserControl I've created which imports several parts using the [Import] attribute.
public class MyUserControl : UserControl, IPartImportsSatisfiedNotification
{
[Import]
public IService Service { get; set; }
public MyUserControl()
{
}
public void OnImportsSatisfied()
{
// Do something with Service.
}
}
This UserControl is instantiated from XAML, so its imports aren't being satisfied and OnImportsSatisfied isn't being called.
<local:MyUserControl />
My question is how can I satisfy my class's imports when it's being created in XAML.
From MSDN:
To be instantiated as an object element in XAML, a custom class must
meet the following requirements:
The custom class must be public and must expose a default (parameterless) public constructor. (See following section for notes
regarding structures.)
The custom class must not be a nested class. The extra "dot" in the full-name path makes the class-namespace division ambiguous, and
interferes with other XAML features such as attached properties.
If an object can be instantiated as an object element, the created object
can fill the property element form of any properties that take the
object as their underlying type.
You can still provide object values
for types that do not meet these criteria, if you enable a value
converter. For more information, see Type Converters and Markup
Extensions for XAML.
From there, you have two choices:
1) Using a TypeConverter:
Using a type converter will allow you to instantiate an object without a parameterless constructor, but you will have to provide a TypeConverter that will do the instantiation.
Now, I never had to use it, I cannot help you further with that.
2) Retrieve IService using the ServiceLocator:
public class MyUserControl : UserControl
{
public IService Service { get; set; }
public MyUserControl()
{
Service = Microsoft.Practices.ServiceLocation.ServiceLocator.Current.GetInstance<IService>();
// You can do something with Service here already.
}
}
I realize it is a change in the design of your class, but hopefully you can cope with it.
Hope this helps,
Bab.
if you did not want mef to create your usercontrol, you have to use the compositioncontainer in your usercontrol and call GetExport direct. but then you have the problem to get the instance of your compositioncontainer :)
ps: i let mef create my wpf views in my applications.
(I'm resurrecting this in case anyone comes across it. As a disclaimer, I'm no expert and these are just solutions I found to be working.)
I found that calling CompositionContainer.ComposeParts(myUserControl) works. I call this on the control's constructor. You'll need to get a reference to the CompositionContainer somehow:
public MyUserControl()
{
compositionContainer.ComposeParts(this);
}
Additional solution:
This is probably unnecessary, but here's another way. This is far more convoluted but it does allow you to "Import" your usercontrol in XAML.
To have your imports satisfied, MyUserControl needs to be exported and then instantiated by MEF. My solution was to have static field in a class that holds a "Locator" object. This Locator object is responsible for importing and returning exported objects. Then I could refer to this static field in XAML, like so:
<ContentControl Content="{Binding MyUserControl, Source={x:Static v:SomeClass.Locator}}">
SomeClass has a static property called Locator which gets assigned early in the application's life cycle. The Locator could then have a MyUserControl property that gets Imported.
(Disclaimer: the links below are to my own framework and the solution, being as crude as it is, if used should be used with care.)
To provide an example of the above, I'll explain how I implemented it in my framework:
In my case, SomeClass is a subclass of System.Windows.Application that replaces App.xaml, and ViewLocator is assigned on its OnStartup, as can be seen here.
The ViewLocator class is a System.Dynamic.DynamicObject that imports views, which have a custom ViewExport attribute. Views are identified using the ViewExportAttribute.Alias property.
This is an example of a view being exported and being assigned an alias.
Finally, the MEF instantiated instance of the view can be used in XAML as follows:
<ContentControl Content="{Binding HomeView, Source={x:Static v:FrameworkApp.ViewLocator}}">
Related
first question so I'm open to advice on effectively participating in the StackOverflow community as well as pertaining to the question.
I'm working on a text-based UI in C#. I have an abstract window class and an abstract control class, each of which implements common functionality for the types that inherit them (e.g. pop-up windows or text box controls). Currently, within a program that might implement the library, a developer would have to create window objects and control objects, and then add the controls to their respective windows, and the windows to a window manager class, like this:
var mainWindow = new MainWindow(...);
var textBox1 = new TextBox(...);
mainWindow.AddControl(textBox1);
WindowManager.Add(mainWindow);
This works, but it's a bit clunky. Since a control should never have to exist outside of a window, I was hoping to implement the control types as nested types. However, to maintain extensibility of the program, I'd like for there to be a way to extend the window class with new control types. My question is this: Should I use reflection, or rely on developers using container classes to extend the window class? Alternatively, is there a better way to structure the program than how it's currently laid out?
I've also considered using generics, e.g.:
public abstract class Window : DrawableObject, IWindow
{
public void AddControl <T>(object[] constructorArgs) where T : class, IControl
{
}
}
I'm aiming for ease of implementation without sacrificing extensibility/loose coupling. Thanks in advance for any thoughts!
EDIT: Should clarify, the primary reason for this is to fix some weirdness with how Windows and Controls cooperate. Each control has a parentWindow property which is used to access the window on which a control resides, for various purposes like creating an exit button for a particular window, etc.
Right now, this property is passed to the constructor, but that seems redundant to me since after doing so you have to add the control to the window's control list. I'd like to find a way to set this property when the control is added to a window instead, but restrict this action to when the control is added only, to prevent potential problems if the parentWindow property is changed outside of this context.
The way you coded AddControl method:
public void AddControl <T>(object[] constructorArgs)
where T : class, IControl
{
}
You intend developers to just provide type and your AddControl method will create an instance of it using constructorArgs. This method itself implicitly forces you to use reflection. Anything else does not stand a chance. Because To Add control of type T, Creating Instance of Control of type T is necessary. Since your Window class does not have a clue about T reflection is the only solution.
To facilitate other approaches, you might want to consider few overloads of AddControl.
public virtual T AddControl <T>()
where T : class, new(),IControl
{
//now you can create instance no reflection required
var control = new T();
this.Controls.Add(control);
return control;
}
public void AddControl <T>(T control)
where T : class, IControl
{
}
public abstract void AddControl <T>(object[] constructorArgs)
where T : class, IControl;
Creating an abstract method passes onus of implementation on child class and creating new instance of T can be handled the assuming type of T is known there or at-least all cases of known types of what T might be are handled.
It's a wide scope topic and I guess subjective as well. The best use of OOP is to achieve a design which fits your logical objective whatever that maybe.
I'm wondering if there's a way to hook to an event whenever an object is instantiated.
If it doesn't, is there a way to retrieve the object to which an attribute is attached to when the attribute is instantiated?
What I want to do is give some of my classes a custom attribute and whenever a class with this attribute is instantiated, run some code for it.
Of course, I could simply place the code in each of those classes' constructor but that's a lot of copy and pasting and I could easily forget to copy that code into one or two classes. And of course, would be very convenient for end users as all they would have to do is add my attribute to their classes and not worry about remember to add that bit of code in their constructors.
I actually can't do a base class because all of those objects already have a base.
Thanks in advance.
Here's an example of what I'd like to do. Either use the attribute's constructor or have an event handler for object instantiation.
public class MySuperAttribute : Attribute
{
public MySuperAttribute()
{
//Something akin to this or the event in Global
Global.AddToList(this.TheTargetObject);
}
}
[MySuperAttribute]
public class MyLabel : System.Windows.Forms.Label
{
}
public static class Global
{
public static void AddToList(Object obj)
{
//Add the object to a list
}
//Some pseudo-hook into the instantiation of any object from the assembly
private void Assembly_ObjectInstantiated(Object obj)
{
if(obj.GetType().GetCustomAttributes(typeof(MySuperAttribute), true).Count != 0)
AddtoList(obj);
}
}
There is no easy way to hook object instantiation externally, maybe with some debugging API, and it has a good reason. It makes your code harder to maintain and understand for other people.
Attributes won't work, because the instance of an attribute is not actually created until it is required - via reflection, and an attribute is assigned to a type, not an instance.
But you may well put the code in a base class, and derive all other classes from it, although it is also not a good practice to pass half-initialized instance to other methods. If the class inherits from ContextBoundObject, you can assign a custom implementation of ProxyAttribute to it and override all operations on it.
If you can't create a common base class (when your types inherit from different types), you can always create the instance with a custom method like this one:
public static T Create<T>() where T : new()
{
var inst = new T();
Global.AddToList(inst);
return inst;
}
However, seeing as you inherit from form controls, their instantiation is probably controlled by the designer. I am afraid there is no perfect solution, in this case.
I'm trying to expose the ViewModel as a static resource on the page so that it can be easily accessible by the binding.
TestViewModel.cs
namespace Test.WPFUI.Home
{
public class TestViewModel....
HelloWorldView.Xaml
xmlns:local="clr-namespace:Test.WPFUI.Home"
<UserControl.Resources>
<local:TestViewModel x:Key="mainPageViewModel" />
</UserControl.Resources>
TestViewModel Can't be found. May I ask for some tips or suggestions Please.
Getting help from http://www.telerik.com/help/silverlight/gridview-troubleshooting-blank-cells.html
public class LoanViewModel : ScreenViewModelBase<LoanViewModel>, IRecord, INotifyPropertyChanged
{
public LoanViewModel(IEventAggregator events) .............
It sounds like your initial problem was not having the full xmlns definition. You usually need both the namespace and assembly.
The easiest way to get it right, in my experience, is to let intellisense do it for you. Just start typing the namespace you want, and as long as its in a referenced project, there will be an autocomplete option.
Your second problem is due to not having a default constructor. You wrote this:
<local:TestViewModel x:Key="mainPageViewModel" />
Which will invoke the default constructor. However, you define a constructor here:
public LoanViewModel(IEventAggregator events) .............
Which removes the provided (paramaterless) default constructor. I'm going to take a wild guess and say that creating the correct IEventAggregator is not simple or desired from XAML, so I see two choices:
You didn't really need that parameter in the constructor. Simply add a default constructor to your view model and you are good to go!
You really need that parameter, so instantiating from XAML just isn't a good idea. Pass in your view model from somewhere else on the view's constructor.
If you feel like you can instantiate the correct object from XAML, use this post to invoke the paramaterized constructor: Calling a parameterized constructor from XAML
In my opinion, putting truly regular classes into XAML is not a good pattern to follow, so I wouldn't. By regular, I mean not related at all to the view.
I have a rootcontroller pushed on a UINavigationController.
Inside that rootcontroller class, I can access the UINavigationController with this.NavigationController
However, this rootcontroller has a ScrollView and I'm adding subcontrollers (or more precise, the View of this subcontroller) to this ScrollView.
I would now like to access the UINavigationController from inside such subcontroller.
Following properties are all null
this.NavigationController
this.ParentViewController
this.PresentedViewController
this.PresentingViewController
It seems in ObjectiveC you can use following code
YourAppDelegate *del = (YourAppDelegate *)[UIApplication sharedApplication].delegate;
[del.navigationController pushViewController:nextViewController animated:YES];
Unfortunately, i don't know how to map this to C# in MonoTouch.
I tried the following, but it's not working:
UIApplication.SharedApplication.KeyWindow.RootViewController.NavigationController
I know I could pass the UINavigationController object to all my classes (parameter in constructor), but that's probably not the cleanest way to go.
To extend poupou's answer, here is an example of what I usually do in my AppDelegate class:
Add a static property of the AppDelegate itself:
public static AppDelegate Self { get; private set; }
Add my root navigation controller as a property:
public UINavigationController MainNavController { get; private set; }
In FinishedLaunching:
Self = this;
window = new UIWindow(UIScreen.MainScreen.Bounds);
this.MainNavController = new UINavigationController(); // pass the nav controller's root controller in the constructor
window.RootViewController = this.MainNavController;
// ..
This way, I can access the root view controller from anywhere, like this:
AppDelegate.Self.MainNavController.PushViewController(someViewController);
... instead of having to write this all the time:
AppDelegate myAppDelegate = (AppDelegate)UIApplication.SharedApplication.Delegate;
myAppDelegate.MainNavController.PushViewController(someViewController);
Plus, I can directly access all other AppDelegate's properties I might have.
Hope this helps.
UIApplicationDelegate does not, itself, define a navigationController property.
OTOH [UIApplication sharedApplication].delegate returns the instance of your own, application specific, UIApplicationDelegate - so it's a great place to share stuff (and why it's frequently used).
What commonly happens, in ObjectiveC, is that this custom ,UIApplicationDelegate-derived, type will implement it's own application-wise properties. IOW YourAppDelegate will implement a navigationController property that can be accessed, anywhere inside the app, by using [UIApplication sharedApplication].delegate.
You can do something very similar in .NET / C#. Simply add your own properties to your AppDelegate type, like this example. You'll be able to access them like Objective-C (if you like) or more directly (e.g. by making them public static properties).
Note that you still have to track and set your properties correctly (just like it needs to be done in Objective-C too).
During a refactoring, I added a generic type parameter to MyControl, a class derived from UserControl. So my class is now MyControl<T>.
Now I get an error at runtime stating that the embedded resource file MyControl`1.resources cannot be found. A quick look with .NET Reflector shows that the resource file is actually called MyControl.resources, without the `1.
At the start of the MyControl<T>.InitializeComponent method there is this line which is probably the one causing problems:
System.ComponentModel.ComponentResourceManager resources =
new System.ComponentModel.ComponentResourceManager(
typeof(MyControl<>));
How do I force the ComponentResourceManager to use the embedded resource file MyControl.resources? Other ways to resolve this issue are also welcome.
Turns out you can override the resource filename to load by inheriting from ComponentResourceManager like this:
using System;
using System.ComponentModel;
internal class CustomComponentResourceManager : ComponentResourceManager
{
public CustomComponentResourceManager(Type type, string resourceName)
: base(type)
{
this.BaseNameField = resourceName;
}
}
Now I can make sure that the resource manager loads MyControl.resources like this:
System.ComponentModel.ComponentResourceManager resources =
new CustomComponentResourceManager(typeof(MyControl<>), "MyControl");
This seems to work.
edit: the above line is overwritten if you use the designer, because it is in the
generated code region. I avoid the designer and make use of version control tools to revert any unwanted changes, but the solution is not ideal.
In addition to Wim's technique, you can also declare a non-generic base control that has the same name as your generic class, and have your generic control/form derive from that non-generic base class.
This way you can trick both the designer and the compiler into using the resource file from your generic class, and you get permanent designer support once the base class is setup without having to fiddle in the .designer file everytime you rebuild :
// Empty stub class, must be in a different file (added as a new class, not UserControl
// or Form template)
public class MyControl : UserControl
{
}
// Generic class
public class MyControl<T> : MyControl
{
// ...
}
The only requirements are to have exactly the same name for your generic class and its base class, and that the base class must be in another class file, otherwise the designer complains about not finding one of the two classes.
PS. I tested this with forms, but it should work the same with controls.
On my Visual Studio 2008 I have this error:
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MyControl));
Using the generic type 'WindowsFormsApplication1.UserControl1' requires '1' type arguments.
Notice that in my case code was generated without parentheses, <>, after the class name.
It is becoming interesting, see ImageList autogenerates non-compiling code in a Generic User Control.
What they said:
Posted by Microsoft on 7/6/2005 at 2:49 PM
This is an interesting bug. You've hit upon a generic scneario that we do not support in the Windows Forms designer. We will not be able to add support for this in the Whidbey (my note: Visual Studio 2008?) release. We will consider this for a future version. As a workaround, you can use the designer to create a none generic UserControl with a public Type property and then create a generic class that inherits from it and passes T into the base classes Type property.
I suppose this control cannot be designed in the Visual Studio forms designer either.
The simplest and easiest workaround is to make a dummy class for the autogenerated typeof(). You do not need to inherit from it or even expose it to the outside:
// Non-generic name so that autogenerated resource loading code is happy
internal sealed class GridEditorForm
{
}
(In my experience, the time required getting the designer to work around generics was not worth the ideal coolness generics can provide. I won't be using generic windows forms or controls again.)