Is there anyway to get the hostcontrol / parentcontrol from a region in PRISM C# WPF. Im trying to write an custom region behaviour that modifies the hostcontrol if there is no views in the region.
There is a HostControl property in RegionBehavior class but it is alwasy null. How do i set it ? or how to get the hostcontrol.
Thanks for advice!
[Export(typeof(CollapseRegionBehavior))]
[PartCreationPolicy(CreationPolicy.NonShared)]
public class CollapseRegionBehavior : RegionBehavior, IHostAwareRegionBehavior
{
public DependencyObject HostControl { get; set; }
protected override void OnAttach()
{
if (this.Region.ActiveViews.Count() == 0)
{
}
}
}
Are you adding the RegionBehavior manually or do you register it in the bootstrapper in ConfigureDefaultRegionBehaviors?
It seems that if you add a behavior manually like this:
IRegion region = regionManager.Regions["MyRegion"];
region.Behaviors.Add("MyBehaviorKey", new MyBehavior());
the IHostAwareRegionBehavior interface has no effect. If you look at the PRISM source code in RegionBehaviorCollection.cs (Prism.Dektop.Regions) you'll clearly see that the Add method does not do anything related to the IHostAwareRegionBehavior interface.
However in RegionAdapterBase.cs, the interface is checked for in AttachDefaultBehaviors.
I ended up registering my RegionBehavior in the bootstrapper and checking the region name to see if there is work to be performed. Not very clean, but it works.
Related
I would like to create my own service with global visibility. To implement that, I followed this sample solution.
Everything goes well, I can call my service within a class, which extends from the Package abstract class, in this way:
public class ClientPackage : Package
{
private void GetGlobalServiceCallback(object sender, EventArgs args)
{
IMyGlobalService service = GetService(typeof(SMyGlobalService)) as IMyGlobalService;
}
}
Because I'm in a Package, I can easily call GetService and I can get my service. But what about if I want to get my service from a class, which is not extends the Package abstract class?
For example, I have a class, which implements an ITagger interface. If I want to get a service in this class, I have to use Package.GetGlobalService() method in this way:
var txtMgr = (IVsTextManager)Package.GetGlobalService(typeof(SVsTextManager));
I tried to get my own service with the Package.GetGlobalServie(), but I always getting null. The linked sample code doesn't contain a soluiton for my problem.
May I missed something or I have a wrong scenario to get my service?
EDIT:
Okay, let's say I have to use MEF to get my service, because I can't get my service with Package.GetGlobalService().
I have a solution with 2 projects in it. One is a class library, which contains an interface like this:
public interface INamesAccessor
{
IEnumerable<string> GetNames();
}
The other project is the VSPackage (has a reference with the first project), which implements my interface as well:
[Export(typeof(INamesAccessor))]
public sealed class BitbucketExtensionPackage : Package, INamesAccessor
{
private IEnumerable<string> _names { get; set; }
public IEnumerable<string> GetNames()
{
return _names;
}
}
Let's say that if the user clicks a given menu under the Tools menu, the logic set the value of the names. Until that the _names is empty.
I would like to use the content of this _names list at my provider, like:
[Export(typeof(ITaggerProvider))]
[ContentType("text")]
[TagType(typeof(CommentTag))]
internal sealed class CommentTaggerProvider : ITaggerProvider
{
[Import]
public INamesAccessor _namesAccessor { get; set; }
public ITagger<T> CreateTagger<T>(ITextBuffer buffer) where T : ITag
{
if (buffer == null)
throw new ArgumentNullException("buffer");
return buffer.Properties.GetOrCreateSingletonProperty(() => new CommentTagger(buffer, _namesAccessor )) as ITagger<T>;
}
}
I get the namesAccessor here, but every fields are null, the _names IEnumerable also.
Is there a way to force the MEF to import my accessor again when the user click to the menu button? What did I wrong?
Thank you for your answer! :)
You should use MEF to composes services for your extensions; not the old COM-based ServiceProvider stuff.
Put all of your actual code in MEF-exported classes, then invoke them from the package class using the SComponentModel service.
For more information, see my blog.
I m reading Prism library by microsoft. I just dont understand ConfigureContainer().
The code in shell application like this:
internal class JamSoftBootstrapper : UnityBootstrapper
{
protected override IModuleEnumerator GetModuleEnumerator()
{
return new DirectoryLookupModuleEnumerator("Modules");
}
protected override void ConfigureContainer()
{
Container.RegisterType<IShellView, Shell>();
base.ConfigureContainer();
}
protected override DependencyObject CreateShell()
{
ShellPresenter presenter = Container.Resolve<ShellPresenter>();
IShellView view = presenter.View;
view.ShowView();
return view as DependencyObject;
}
}
public interface IShellView
{
void ShowView();
}
public class ShellPresenter
{
public IShellView View { get; private set; }
public ShellPresenter(IShellView view)
{
View = view;
}
}
I want to understand why I register IShellView,Shell in ConfigureContainer(). Whats going on behind sceens?
I've registered to give you answer :). ConfigureContainer is just method from abstract class UnityBootstrapper where you can register all Prism services (ModuleManager, RegionManager, EventAggregator). You can make your custom Bootstrapper class, where you can manage your dependency. In your case, every time when you in your code ask for IShellView, you'll get instance of Shell.
I recommend you this book. It's free.
UnityBootstrapper provided by PRISM internally uses UnityContainer to resolve dependency for your registered objects. You can configure your custom objects with it like you are doing in sample.
In your sample, you registered Shell with instance IShellView.
Container.RegisterType<IShellView, Shell>();
So whenever you ask container to get you object for IShellView, it will give you an instance of Shell object.
IShellView shellObject = Container.Resolve<IShellView>();
So, ConfigureContainer gives you support to register your objects with it's container.
Quote from MSDN link:
Configures the IUnityContainer. May be overwritten in a derived class
to add specific type mappings required by the application.
Also, you can read more about it here - Managing Dependencies Between Components Using the Prism Library 5.0 for WPF.
I have a simple interface like this:
interface IToolbarMaster {
ToolBar Toolbar { get; }
}
I added this Interface to my MasterPage and moved the field declaration of the Toolbar-Control to the Code Behind File:
public partial class Layout : ctrls.MasterPage, IToolbarMaster {
public global::Some.Web.Controls.ToolBar Toolbar;
(...)
}
My pages need to access this Toolbar Control via the Interface (we have a .FindMaster<IToolbarMaster>() Method) - but the code says, Layout doesn't implement IToolbarMaster
I could implement a property like
public ToolBar ToolBar { get { return this.Toolbar; } } // Different name spelling
But this looks really odd in my opinion.
I ran into a similar issue in VB using an auto property with a default value.
Public Property MessagePlaceHolder() As PlaceHolder = Me.phMessage
Apparently that doesn't work. I had to create the property the traditional way.
Public ReadOnly Property MessagePlaceHolder() As PlaceHolder
Get
Return Me.phMessage
End Get
End Property
Wow, I already got the answer, I was very close.
I tried this
public global::Some.Web.Controls.ToolBar Toolbar { get; }
but it didn't work. But as I added the setter everything was fine:
public global::Some.Web.Controls.ToolBar Toolbar { get; set; }
I'm leaving this for future reference, if anyone has this problem too.
Is there a simple way to implement databinding when neither of both classes is of type Control?
In my case, I would like to bind a variable to a property of a custom ToolStripButton.
EDIT for clarification: when binding to a Control, I can use Control's DataBindings collection. However, I am searching for a way to bind properties regardless of the source and target Type.
EDIT: using winforms
You can probably do this by using Truss.
Truss provides WPF-style databinding for any class that implements INotifyPropertyChanged. It gives you a bit more flexibility in this, since it doesn't restrict the classes to being derived from a specific base class.
I use this Implemetation of IBindableComponent on the ToolStripButton, found here. The BindableToolStripButton allows you to use Databinding like with a normal Control.
[ToolStripItemDesignerAvailability(ToolStripItemDesignerAvailability.ToolStrip | ToolStripItemDesignerAvailability.StatusStrip)]
public class BindableToolStripButton : ToolStripButton, IBindableComponent
{
public BindableToolStripButton()
: base() { }
public BindableToolStripButton(String text)
: base(text) { }
public BindableToolStripButton(System.Drawing.Image image)
: base(image) { }
public BindableToolStripButton(String text, System.Drawing.Image image)
: base(text, image) { }
public BindableToolStripButton(String text, System.Drawing.Image image, EventHandler onClick)
: base(text, image, onClick) { }
public BindableToolStripButton(String text, System.Drawing.Image image, EventHandler onClick, String name)
: base(text, image, onClick, name) { }
#region IBindableComponent Members
private BindingContext bindingContext;
private ControlBindingsCollection dataBindings;
[Browsable(false)]
public BindingContext BindingContext
{
get
{
if (bindingContext == null)
{
bindingContext = new BindingContext();
}
return bindingContext;
}
set
{
bindingContext = value;
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ControlBindingsCollection DataBindings
{
get
{
if (dataBindings == null)
{
dataBindings = new ControlBindingsCollection(this);
}
return dataBindings;
}
}
#endregion
}
Assuming you have a class MyClass implementing INotifyPropertyChanged, use it just like you would when binding to a control property:
bindableToolStripButton1.DataBindings.Add("Enabled", myClass1, "MyBooleanProperty");
Use dependency properties (your property in your ToolStripButton should be) and create a property for your variable in your other class and create a binding and set it to the property of your ToolstripButton.
I guess that's about the easiest way to do it.
EDIT: That's only for WPF...
Else implement INotifyPropertyChanged and when your variable changes, it should automatically change in your ToolStripButton.
For similar behaviour like Controls being bound to object properties, for any Type you can implement the same interfaces.
Based on that thought, you can subclass ToolStripButton (or desired Type to have bindings) and implement IBindableComponent for it. This works for all kinds of source and target Types as long as they're not sealed. For example, your tool strip button:
public class BindableToolStripButton : ToolStripButton, IBindableComponent {
//...
This will cause the BindableToolStripButton to have its own .DataBindings property whereas the base ToolStripButton class doesn't have such a propery.
You would need to follow through on filling out implementation details using examples seen here from Microsoft for ISite, IBindableComponent, IComponent and any inherited interfaces.
Then you would add Binding instances to any instance of BindableToolStripButton.
(Note: I only have fragements so will make my first community wiki post - and we'll see how that goes... )
I written some basic databinding stuff through reflection. It works on any object and doesn't need to implement something special (no INotifyPropertyChanged, it just works) it is part of my editor at http://github.com/filipkunc/opengl-editor-cocoa look at HotChocolate/Bindings (like re-implementation of Cocoa KVC, KVO into .NET) folder. You can see it in action in HotChocolateTest project.
There is another quick and simple solution which consists in creating properties in the Form, and bind them:
public MyForm : Form
{
...
public bool CanDelete
{
get { return deleteToolStripButton.Enabled; }
set { deleteToolStripButton.Enabled = value; }
}
public MyForm()
{
...
this.DataBindings.Add("CanDelete", this.MyModel, "DeleteAllowed",
false, DataSourceUpdateMode.Never);
...
}
}
Assuming that MyModel contains a DeleteAllowed property which notifies its changes.
public class StatisticsViewPresenter
{
private IStatisticsView view;
private Statistics statsModel;
public StatisticsViewPresenter(IStatisticsView view, Statistics statsModel)
{
this.view = view;
this.statsModel = statsModel;
}
}
I don't use events (but am willing to if it can solve my problem), so my View classes look like this:
public class StatisticsForm : Form, IStatisticsView
{
public StatisticsForm()
{
InitializeComponent();
}
[Inject]
public StatisticsViewPresenter Presenter
{
private get;
set;
}
}
With
kernel.Bind<StatisticsPresenter>().ToSelf().InSingletonScope();
kernel.Bind<IStatisticsView>().To<StatisticsForm>();
kernel.Get<IStatisticsView>();
it builds up the Form, builds up the presenter, then injects the presenter into the Presenter property. Everything's peachy. (Except for that singleton-scoped presenter--any thoughts on a better way to do that? Perhaps just manually inject the presenter into the view's Presenter property inside the presenter's constructor: this.view.Presenter = this).
But if I turn StatisticsForm into StatisticsUserControl and drag-drop it onto my MainForm, it's not being injected into MainForm by Ninject, it's simply being new'd by the Designer. I see three solutions here:
1) Don't use UserControls and just use one giant form that implements these multiple views (eww);
2) Inject UserControls into my form and lose Designer support;
3) Your solution! :)
My approach to use Ninject with forms, usercontrols and the designer is:
Use factories to create the forms (also for the usercontrols if you create some controls dinamically)
for the usercontrols and the forms keep the constructors without parameters and use property injection
add an Activation strategy to the kernel that check if ninject has just created a form or a usercontrol. If that is the case, the activation strategy iterates over the controls in the Controls property of the UserControl (or the Form) and calls Kernel.Inject(UserControl) for each usercontrol. (An Activation strategy is some code ninject executes after it has injected an object)
You can use the designer and have forms and usercontrols with dependencies injected via Ninject.
The only drawback is that you have to use property injection instead of constructor injection for the usercontrols (and the forms)
namespace Majiic.Ninject
{
public class WindowsFormsStrategy : ActivationStrategy
{
// Activate is called after Kernel.Inject
//even for objects not created by Ninject
//To avoid multiple "injections" in the same nested controls
//we put this flag to false.
private bool _activatingControls = false;
public override void Activate(IContext context, InstanceReference reference)
{
reference.IfInstanceIs<UserControl>(uc =>
{
if (!_activatingControls)
{
Trace.TraceInformation("Activate. Injecting dependencies in User control of type {0}", uc.GetType());
_activatingControls = true;
context.Kernel.InjectDescendantOf(uc);
_activatingControls = false;
}
});
reference.IfInstanceIs<Form>(form =>
{
if (!_activatingControls)
{
Trace.TraceInformation("Activate. Injecting dependencies in Form of type {0}", form.GetType());
_activatingControls = true;
context.Kernel.InjectDescendantOf(form);
_activatingControls = false;
}
});
}
}
}
Create the kernel and add the Activation Strategy
var kernel=new StandardKernel(new CommonMajiicNinjectModule());
kernel.Components.Add<IActivationStrategy, WindowsFormsStrategy>();
kernel extensions to iterate over descendents controls
namespace Majiic.Ninject
{
static public class WinFormsInstanceProviderAux
{
static public void InjectDescendantOf(this IKernel kernel, ContainerControl containerControl)
{
var childrenControls = containerControl.Controls.Cast<Control>();
foreach (var control in childrenControls )
{
InjectUserControlsOf(kernel, control);
}
}
static private void InjectUserControlsOf(this IKernel kernel, Control control)
{
//only user controls can have properties defined as n-inject-able
if (control is UserControl)
{
Trace.TraceInformation("Injecting dependencies in User Control of type {0}", control.GetType());
kernel.Inject(control);
}
//A non user control can have children that are user controls and should be n-injected
var childrenControls = control.Controls.Cast<Control>();
foreach (var childControl in childrenControls )
{
InjectUserControlsOf(kernel, childControl );
}
}
}
}
This is certainly an interesting area of, should I say, research. We've made ourselves a solution where we host user controls in a generic form.
Our generic form is not intended for use with the Designer. Through code we add the chosen user control to the Form dynamically.
For other frameworks you should look at Prism/Composite from the Microsoft Patterns & Practices group. Here's an article discussing extensions for WinForms.
I recently created some reuseable UserControls with the need of Dependencies to inject. As the IoC Container isn't used to create those UserControls, obviously he cannot automatically inject the Dependencies.
My Solution is, a Base-Class to enable at least Property Injection. Constructor Injection is not supported, as a parameterless Constructor is used to create those instances.
public class NinjectUserControl : UserControl
{
// Generally this is considered to be a bad practice,
//however I didn't find any better way. If you do, please share :)
public static IKernel Kernel { private get; set; }
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
RequestActivation(Kernel);
}
protected virtual void RequestActivation(IKernel kernel)
{
kernel?.Inject(this);
}
}
To get it to Work you need to set the Kernel once. Typically this is somewhere inside your program.cs (WinForms) or App.xaml.cs (WPF)
IocKernel = new StandardKernel(); // typically a static member
NinjectUserControl.Kernel = IocKernel;
IocKernel.Load(new Module()); // loading modules
// .. Create MainForm or whatever
To use, simply inherit NinjectUserControl and then let the Kernel Inject your dependencies via Property Injection:
[Inject]
public IService Service { private get; set; }
please note, that those dependencies are not accessible inside the Constructor.