I follow official guide to use simple injector to inject object in web form and it works, but now i cant make it works in custon control
this is what i do:
public partial class GestioneAttivita_cnStruttureSocieta : System.Web.UI.UserControl
{
[Import]
public IUnitOfWork iuow { get; set; }
public Domain.Entity.Attivita attivitaGestita {get; set;}
protected void Page_Load(object sender, EventArgs e)
{
using (iuow)
{
attivitaGestita = iuow.Attivita.Read(attivitaGestita.IdAttivita);
}
}
}
but i get null reference exception since iuow is null
i try to edit global.asax to manage UserControl in this way:
private static void RegisterWebPages(Container container)
{
var pageTypes =
from assembly in BuildManager.GetReferencedAssemblies().Cast<Assembly>()
where !assembly.IsDynamic
where !assembly.GlobalAssemblyCache
from type in assembly.GetExportedTypes()
where (type.IsSubclassOf(typeof(Page)) **|| type.IsSubclassOf(typeof(UserControl)))**
where !type.IsAbstract && !type.IsGenericType
select type;
foreach (Type type in pageTypes)
{
var registration = Lifestyle.Transient.CreateRegistration(type, container);
registration.SuppressDiagnosticWarning(
DiagnosticType.DisposableTransientComponent,
"ASP.NET creates and disposes page classes for us.");
container.AddRegistration(type, registration);
}
}
}
class ImportAttributePropertySelectionBehavior : IPropertySelectionBehavior {
public bool SelectProperty(Type serviceType, PropertyInfo propertyInfo) {
// Makes use of the System.ComponentModel.Composition assembly
bool _return = false;
_return = (typeof(Page).IsAssignableFrom(serviceType) &&
propertyInfo.GetCustomAttributes<ImportAttribute>().Any())
**||
(typeof(UserControl).IsAssignableFrom(serviceType) &&
propertyInfo.GetCustomAttributes<ImportAttribute>().Any());**
return _return;
}
}
but i get same error
is this doable?
To be able to get this working, you will have to hook onto the PreLoad event of the page during initialization. During PreLoad you can walk the page's control hierarchy and initialize all controls like you do with the page itself.
There's actually example code in the Simple Injector repository (that never made it to an official package) that shows you how to do this. Take a look here.
Related
I have a form1 (not mdi) which displays dialog on button click event, dialog basically is a pop up form which shows data on datagridview control.
I am using simple injector.
PopUpForm has a property called LocationData which is a datatable. I need to set that property in form1 (parent) so that data can be displayed on the PopUpForm when it is displayed on the screen.
Sorry, i am new to simple injector and still learning, any help or guidence would be appreciated. I even don't know if i am doing in a right way.
form1
On button click event
this._formOpener.ShowModalForm<PopUpForm>();
PopUpForm
public partial class PopUpForm : Form
{
public DataTable LocationData { get; set; }
public PopUpForm()
{
InitializeComponent();
}
private void PopUpForm_Load(object sender, EventArgs e)
{
dgvNearestLocations.DataSource = LocationData;
}
}
program class
static class Program
{
private static Container container;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Bootstrap();
Application.Run(container.GetInstance<Form1>());
}
private static void Bootstrap()
{
// Create the container as usual.
container = new Container();
// Register your types, for instance:
container.RegisterSingleton<IFormOpener, FormOpener>();
container.Register<Form1>(Lifestyle.Singleton); ;
container.Register<PopUpForm>(Lifestyle.Singleton); ;
// Optionally verify the container.
container.Verify();
}
}
FormOpener
public class FormOpener : IFormOpener
{
private readonly Container container;
private readonly Dictionary<Type, Form> openedForms;
public FormOpener(Container container)
{
this.container = container;
this.openedForms = new Dictionary<Type, Form>();
}
public DialogResult ShowModalForm<TForm>() where TForm : Form
{
using (var form = this.GetForm<TForm>())
{
return form.ShowDialog();
}
}
private Form GetForm<TForm>() where TForm : Form
{
return this.container.GetInstance<TForm>();
}
}
First of all, you copied the FormOpener probably from this answer. But you missed the part about Forms needing to be transient. Don't register your forms as Singleton. Especially because you dispose them, this will work one and exactly one time. The next time you would want to show a Form you will get an ObjectDisposedException.
When you register the Forms as Transient Simple Injector will tell you that the forms implement IDisposable and this is (of course) correct. But because you take care of disposing in the FormOpener you can safely suppress this warning. Register your forms like this:
private static void RegisterWindowsForms(
this Container container, IEnumerable<Assembly> assemblies)
{
var formTypes =
from assembly in assemblies
from type in assembly.GetTypes()
where type.IsSubclassOf(typeof(Form))
where !type.IsAbstract
select type;
foreach (var type in formTypes)
{
var registration = Lifestyle.Transient.CreateRegistration(type, container);
registration.SuppressDiagnosticWarning(DiagnosticType.DisposableTransientComponent,
"Forms are disposed by application code. Letting Simple Injector do this " +
"is problematic because that would need a scope, which is impossible to do.");
container.AddRegistration(type, registration);
}
}
To come to your question:
What you need is some extra infrastructure to initialize the Form.
By letting your forms implement an interface IFormInit<T> you can pass data to the form and directly show it.
public interface IFormInit<T> : IDisposable
{
DialogResult InitAndShowForm(T data);
}
To let Simple Injector create the forms based on this interface we need to register them in the container. We can let Simple Injector search for all closed implementations by supplying a list of assemblies, like this:
container.Register(typeof(IFormInit<>), assemblies, Lifestyle.Transient);
Notice that Simple Injector will automatically merge these registrations with the ones from RegisterWindowsForms. So you can now get an instance of Form by calling:
container.GetInstance<PopupForm>();
or
container.GetInstance<IFormInit<SomeDataClass>>();
You can now add this code to your FormOpener class:
public DialogResult ShowModalForm<TData>(TData data)
{
Type formType = typeof(IFormInit<>).MakeGenericType(typeof(TData));
dynamic initForm = this.container.GetInstance(formType);
DialogResult result = (DialogResult) initForm.InitAndShowForm(data);
initForm.Dispose();
return result;
}
This will get the Form from the container based on the IFormInit<T> type that it implements. When you get the form, you call the function on the interface instead of directly call Form.ShowDialog(). When the form is closed you dispose of the Form.
Note: The use of dynamic typing maybe needs clarification. Why it is needed is inspired by the QueryHandler pattern described here.
Usage is as follows:
// Add a specific class to pass to the form
public class LocationDataWrapper
{
public DataTable LocationData { get; set; }
}
public partial class PopUpForm : Form, IFormInit<LocationDataWrapper>
{
public PopUpForm() => InitializeComponent();
// Implement the interface, the loaded event can be removed
public DialogResult InitAndShowForm(LocationDataWrapper data)
{
dgvNearestLocations.DataSource = data.LocationData;
return this.ShowDialog();
}
}
On button click event
DialogResult result = this._formOpener.ShowModalForm(new LocationDataWrapper
{
LocationData = locationDataTable,
});
You can create wrapper or data classes for each form and it will automatically show the correct form, when you let this Form implement IFormInit<ThisSpecificDataClass>.
I am building a reusable Razor class library for Blazor components.
I have an interface that any Blazor component could implement:
public interface IControllableComponent
{
void SetSomeValue(int someValue);
}
How can I get a List of all components that implement that interface?
public interface IControllableComponentManager
{
void SetSomeValueForAllControllableComponents(int someValue);
}
public class ControllableComponentManager : IControllableComponentManager
{
IList<IControllableComponent> _controllableComponentList;
public ControllableComponentManager()
{
_controllableComponentList = ??? // how to populate this list?
}
public void SetSomeValueForAllControllableComponents(int someValue)
{
foreach (var controllableComponent in _controllableComponentList)
{
controllableComponent.SetSomeValue(someValue);
}
}
}
I want for my reusable Razor class library to be used in the code behind in different projects:
public class MyControllableComponentBase : ComponentBase, IControllableComponent
{
protected int _someValue;
public void SetSomeValue(int someValue)
{
_someValue = someValue;
}
}
Is there a way to populate _controllableComponentList in ControllableComponentManager?
I would like to do this in a clean, proper way, without using reflection. Preferably in the style of ASP.NET Core with dependency injection.
The problem is that Blazor components are instantiated in Razor markup as <Component></Component> so I don't know how to get their references to add them to the List<>.
I guess you can do it as the following:
Define a service that stores a collection of the interface type
Expose a method to add a component, a method to remove a component, an event delegate to notify of addition, removal, etc.
Inject the service into the component you want to add itself to the service, and in the OnInitialized method do something like this:
protected override void OnInitialized()
{
MyComponents.AddComponent(this);
this.MyProperty = "I was born with the sun...";
}
You can get a Blazor component reference using #ref but you can do that only within Razor markup.
To use a reference to a Blazor component outside Razor markup, the component must register itself.
To do that, you have to use a partial class for the component in the code behind:
public partial class ComponentContainer : ComponentBase, IComponentContainer
{
public Type ComponentType { get; protected set; }
public RenderFragment Component { get; set; }
[Parameter]
public string Name { get; set; }
[Inject]
protected IComponentContainerManager ComponentContainerManager { get; set; }
public void SetComponentType(Type componentType)
{
ComponentType = componentType;
StateHasChanged();
}
protected override void OnInitialized()
{
ComponentContainerManager.RegisterComponentContainer(Name, this);
Component = builder =>
{
if (ComponentType != null)
{
builder.OpenComponent(0, ComponentType);
builder.CloseComponent();
}
};
}
}
Use [Inject] attribute to use dependency injection to inject a service where the component can register.
Use the protected override void OnInitialized() of the ComponentBase to register the component in the registration service.
public interface IComponentContainer
{
Type ComponentType { get; }
void SetComponentType(Type componentType);
}
public interface IComponentContainerManager
{
void RegisterComponentContainer(string componentContainerName, IComponentContainer componentContainer);
void SetComponentType(string componentContainerName, Type componentType);
Type GetComponentType(string componentContainerName);
}
public class ComponentContainerManager : IComponentContainerManager
{
readonly IDictionary<string, IComponentContainer> _componentContainerDict = new Dictionary<string, IComponentContainer>();
public void RegisterComponentContainer(string componentContainerName, IComponentContainer componentContainer)
{
_componentContainerDict[componentContainerName] = componentContainer;
}
public void SetComponentType(string componentContainerName, Type componentType)
{
_componentContainerDict[componentContainerName].SetComponentType(componentType);
}
public Type GetComponentType(string componentContainerName)
{
return _componentContainerDict[componentContainerName].ComponentType;
}
}
Now you can use IComponentContainerManager anywhere in your code.
If you want to dynamically create components, then you can use the RenderFragment concept for this. A render fragment is essentially a function that uses a RenderTreeBuilder to create a component. This is what your Razor components are compiled to when you author .razor files.
For example, the following code creates a RenderFragment that just renders a single component:
Type componentType = typeof(MyControllableComponentBase);
RenderFragment fragment = builder =>
{
builder.OpenComponent(0, componentType);
builder.CloseComponent();
};
You could then assign that fragment to a property and render it dynamically within your Razor component:
<div>
#Fragment
</div>
#code {
public RenderFragment Fragment
{ get; set; }
}
This will solve the issue how to create components dynamically when you just know their type. However, it will not solve the problem that you want to call SetSomeValue on the component instances. For that, you will have to understand that you don’t really have control over the component instances: The render tree builder is responsible for creating the components from that virtual markup, so you don’t ever call new ComponentType(). Instead, you rely on the renderer to create the instance for you and then you could take a reference to the used instance and work with that.
You can use the #ref directive to capture references to rendered component instances:
<MyControllableComponent #ref="controllableComponent" />
#code {
private MyControllableComponent controllableComponent;
private void OnSomething()
{
controllableComponent.SetSomeValue(123);
}
}
I'm working on an application that supports various plugins (well, that's planned at least). And I'd love to have it beautiful.
So I want to let the plugin developer send a big control (like a panel or other containers) to my host application and have the user setup their settings for the plugin in the application.
That would take the plugin-developer's effort to somehow implement a settings-panel that runs by in an own window.
Thing is, I'm not sure how to do that.
I can pass variables to my host application but as soon as I try to add the control to my container panel, I get a RemoteException, telling me that the field 'parent' on type 'System.Windows.Forms.Control' can't be found.
I tried to add the plugin-control that way:
panel.Controls.Add(pluginControl);
If I try it the other way around:
pluginControl.Parent = panel;
I get a SerializationException because the class System.Windows.Forms.Control isn't marked Serializable.
Maybe some person ran into the same thing and can help me.
Let me know if you need more information!
Edit: Have a look on my current implementation: https://dl.dropboxusercontent.com/u/62845853/Random%20crap/NotModified_SamplePluginSystem.zip
I tried something which you can adorn to your needs:
First i created a PluginBase class and the proper EventArgs in a ClassLibrary:
public abstract class PluginBase
{
public abstract void Initialize();
protected void showControl(UserControl control)
{
ShowControl(this, new ControlToBeShownEventArgs() { TheControl = control });
}
public event EventHandler<ControlToBeShownEventArgs> ShowControl = delegate { };
}
public class ControlToBeShownEventArgs : EventArgs
{
public UserControl TheControl { get; set; }
}
This library is referenced by every Plugin and by the host application.
The Plugin is in turn also a Class Library (build path set to the one of the host)
inside i made a plugin inheriting this base type:
class SomePlugin : PluginBase
{
public override void Initialize()
{
showControl(new UserControl1());
}
}
The UserControl1 is the Control to be shown.
Done that, I next added the following code to the main window of the host:
List<PluginBase> plugins = new List<PluginBase>();
private void Form1_Load(object sender, EventArgs e) //Hook in the event too
{
DirectoryInfo dir = (new FileInfo(Assembly.GetExecutingAssembly().Location)).Directory;
foreach (var item in dir.GetFiles())
{
if (item.Name.Contains("Plugin") && item.Name.EndsWith(".dll"))
{
Assembly ass = Assembly.LoadFile(item.FullName);
foreach (Type type in ass.GetTypes().Where(t => t.BaseType.Name == "PluginBase"))
{
PluginBase pibase = (PluginBase)Activator.CreateInstance(type,false);
plugins.Add(pibase);
}
}
}
foreach (var item in plugins)
{
item.ShowControl += item_ShowControl;
item.Initialize();
}
}
void item_ShowControl(object sender, ControlToBeShownEventArgs e)
{
this.Controls.Add(e.TheControl);
}
I've created some custom textboxes which are inherited from textbox.
For the next step I want to register javascript with a wrapper.
Decorator pattern allow me to do if only I can inherit it from textbox and pass custom textbox as a constructor parameter.
Problem is that how can I use constructor when I add a control to aspx page or basically how can I use decorator pattern for asp.net controls.
EDIT:
Simply this is my validation base class (IField is an validation interface. This can be ignored):
public abstract class ValidationBase : TextBox, IField
{
private readonly IField _field;
protected ValidationBase(IField field)
{
_field = field;
}
public int MinLength
{
get { return _field.MinLength; }
set { _field.MinLength = value; }
}
public bool Required
{
get { return _field.Required; }
set { _field.Required = value; }
}
// other porperties etc...
protected override void OnPreRender(EventArgs e)
{
// DO SOME STUFF...
base.OnPreRender(e);
}
}
And this is my concrete class (EmailField is a concrete impl. of IField ignore...):
public class ValidationEmail : ValidationBase
{
public ValidationEmail()
: base(new EmailField(string.Empty))
{
}
}
And finally I want to implement this (I've made up my mind on wordpad this can't be the exact impl.):
public class JsRegisterDecorator : ValidationBase
{
private readonly ValidationBase _validationObj;
//I am not quite sure about the constructor but i can handle
public JsRegisterDecorator(ValidationBase concreteValidationObj)
: base(concreteValidationObj)
{
_validationObj = concreteValidationObj;
}
//Wrap the properties
protected override void OnPreRender(EventArgs e)
{
//Register JS Files...
_validationObj.OnPreRender(e);
}
}
The problem is that How can I use this decorator? Because asp.net construct controls automatically:
<vc:ValidationEmail ID="ValidationEmail1" runat="server"/>
I don't know can I use this (where can I put the constructor parameter?):
<vc:JsRegisterDecorator ID="ValidationEmailWithJs1" runat="server"/>
I don't think Decorator pattern fits well here. In general I saw more applications of Builder and Factory Method for ASP.NET controls.
To partially solve your task you can use ControlBuilder. It will give you ability to change the type of the control from ValidationBase to JsRegisterDecorator or ValidationEmail. You need to decorate ValidationBase class with ControlBuilderAttribute, inherit builder class from ControlBuilder and override Init method.
[ControlBuilder(typeof(ValidationBaseBuilder))]
public abstract class ValidationBase : TextBox, IField { }
public class ValidationBaseBuilder: ControlBuilder
{
public override void Init(TemplateParser parser, ControlBuilder parentBuilder, Type type, string tagName, string id, System.Collections.IDictionary attribs)
{
var newType = typeof(/*here you can put a JsRegisterDecorator type*/);
base.Init(parser, parentBuilder, t, tagName, id, attribs);
}
}
But I'm not sure about such approach. ControlBuilder cannot give you easy control over constructor. Surely you can override ProcessGeneratedCode in ControlBuilder and David Ebbo has a blog post worth reading but it would not be an easy task to rewrite constructor for control and make solution simple.
As alternative that will work I can suggest to add an abstract (or virtual) method like RegisterScripts inside ValidationBase and call it in OnPreRender. Every control will know what scripts it needs and the process of new validator control creation will be clean and simple. If you want to separate knowledge of JS scripts from concrete implementations then approach as seen in ASP.NET DynamicData (read MetaTable) could be used.
Another thing that I can see is that your idea is close enough to DynamicControl and maybe it would be possible to get more ideas from ASP.NET DynamicData like Field Templates and IFielTemplateFactory.
I solve my problem AlexanderManekovskiy's help and also some other questions:
ASP.NET RenderControl or RenderChildren fail
How to add child nodes to custom asp.net user control derived from System.Web.UI.Control
ASP.NET Custom/User Control With Children
And here is the solution:
I've made JsRegistererForValidationBase as a WebControl and implemented INamingContaier.
For the children elements I've created Children property which accepts olny list of Validation Base.
And finally OnInit method, I've registered the js.
Here is the code:
[ParseChildren(true)]
[PersistChildren(true)]
[ToolboxData(#"<{0}:JsRegistererForVB runat=""server""></{0}:JsRegistererForVB>")]
public class JsRegistererForValidationBase : WebControl, INamingContainer
{
private ValidationFieldCollection _children;
[PersistenceMode(PersistenceMode.InnerProperty)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ValidationFieldCollection Children
{
get
{
if (_children == null)
_children = new ValidationFieldCollection();
return _children;
}
}
protected override void CreateChildControls()
{
Controls.Clear();
foreach (var c in _children)
Controls.Add(c);
}
protected override void OnInit(EventArgs e)
{
//DO THE REGISTER STUFF
base.OnInit(e);
}
protected override void Render(HtmlTextWriter writer)
{
RenderChildren(writer);
}
}
public class ValidationFieldCollection : List<ValidationBase> { }
}
And at the aspx side it becomes like this:
<vc:JsRegisterer ID="JsRegisterer1" runat="server">
<Children>
<vc:ValidationEmail ID="ValidationEmail1" runat="server"/>
<vc:ValidationEmail ID="ValidationEmail2" runat="server"/>,
<!--etc-->
</Children>
</vc:JsRegisterer>
For the detailed imlementation I added the code to codeplex
So this particular topic has been beaten to death; but apparently my iteration is either not the method of choice or isn't practical for implementation.
The goal is to create a Windows Form and a Web Form. Those two forms; set a property in a separate class. Then other classes reference the container; that way as the interface is changed the functionality will change without a lot of recoding.
Windows Form ---> Container <--- Web Form
Container --> Referenced by these: Class 1, Class 2, Class 3
I approached it like this:
Form:
private Some.Reference.ToClass.Container _container;
public void Method(Some.Reference.ToClass.Container Container)
{
_container = Container;
}
private void button_click(object sender, EventArgs e)
{
_container.Name = textbox.Text (or some other component)
}
Class: "Container"
public Name { get; set; }
Class or Form to Reference:
Class WhateverName
{
private string REFERENCE;
private Some.Reference.ToClass.Container _container;
public void Method(Some.Reference.ToClass.Container Container)
{
_container = Container;
}
public void NewMethod()
{
REFERENCE = _container.Name;
}
}
It continually doesn't set; it states it's a null object. What am I missing? Any help would be appreciated. Or even a nice tutorial for me to compare to learn; or find why it doesn't work would be fine as well.
Based on the details provided; including the issue with the null value. I set a Constructor to automatically initialize based upon the change. Then just added some null reference check.
An example of this resolution:
public class One
{
// declared:
private.some.Reference.ToSomeClass _container;
One(ToSomeClass container)
{
_container = container;
}
private void button_click(object sender, EventArgs e)
{
if(_container != null)
{
_container.Name = textbox.Text (or some other component)
}
}
}
Then in the other class; it references the Property; for it to be set. So to avoid it failing cause a value hasn't been assigned yet or an instance hasn't been created just added a null reference verification.