Autofac & WinForms Integration Issue - c#

I have a very simple WinForms POC utilizing Autofac and the MVP pattern. In this POC, I am opening a child form from the parent form via Autofac's Resolve method. What I'm having issues with is how the child form stays open. In the Display() method of the child form, if I call ShowDialog() the child form remains open until I close it. If I call Show(), the child form flashes and instantly closes - which is obviously not good.
I've done numerous searches for integrating Autofac into a WinForms application, however I've not found any good examples on Autofac/WinForms integration.
My questions are:
What is the proper way to display a non-modal child form with my approach?
Is there a better way to utilize Autofac in a WinForms application than my approach?
How do I determine there are no memory leaks and that Autofac is properly cleaning up the Model/View/Presenter objects for the child form?
Only relevant code is shown below.
Thanks,
Kyle
public class MainPresenter : IMainPresenter
{
ILifetimeScope container = null;
IView view = null;
public MainPresenter(ILifetimeScope container, IMainView view)
{
this.container = container;
this.view = view;
view.AddChild += new EventHandler(view_AddChild);
}
void view_AddChild(object sender, EventArgs e)
{
//Is this the correct way to display a form with Autofac?
using(ILifetimeScope scope = container.BeginLifetimeScope())
{
scope.Resolve<ChildPresenter>().DisplayView(); //Display the child form
}
}
#region Implementation of IPresenter
public IView View
{
get { return view; }
}
public void DisplayView()
{
view.Display();
}
#endregion
}
public class ChildPresenter : IPresenter
{
IView view = null;
public ChildPresenter(IView view)
{
this.view = view;
}
#region Implementation of IPresenter
public IView View
{
get { return view; }
}
public void DisplayView()
{
view.Display();
}
#endregion
}
public partial class ChildView : Form, IView
{
public ChildView()
{
InitializeComponent();
}
#region Implementation of IView
public void Display()
{
Show(); //<== BUG: Child form will only flash then instantly close.
ShowDialog(); //Child form will display correctly, but since this call is modal, the parent form can't be accessed
}
#endregion
}

Look at this code:
using(ILifetimeScope scope = container.BeginLifetimeScope())
{
scope.Resolve<ChildPresenter>().DisplayView(); //Display the child form
}
First, it is good that you used a child lifetime scope. If you resolve out of the top-level container, the objects will live as long as that container (which is usually the whole lifetime of the application).
However, there is a problem here. When DisplayView calls Form.Show, it returns immediately. The using block ends, the child scope is disposed, and all of its objects (including the view) are also disposed.
In this case, you do not want a using statement. What you want to do is tie the child lifetime scope to the view so that when the view is closed, the child scope is disposed. See the FormFactory in one of my other answers for an example. There are other ways you could adapt this idea to your architecture - you could do it in the registration (ContainerBuilder) for example.

Related

set child winform properties from a parent form when using simple injector c#

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>.

WPF Caliburn.Micro : Life Cicyle problems

I have a class called AppViewModel, this class it's responsible to control the screens. AppViewModel extends my BaseConductor:
public class BaseConductor : Conductor<Screen>.Collection.OneActive
{
...
}
Then, I call a viewmodel (UserControl) on the constructor of AppViewModel:
this.ActivateItem(new FirstViewModel());
On FirstViewModel, after the user clicks on a button I want to open SecondViewModel and close the FirstViewModel:
var conductor = this.Parent as IConductor;
conductor.DeactivateItem(this, true);
conductor.ActivateItem(new SecondViewModel(param));
I already tried to do this:
((IApp)this.Parent).ActivateItem(new SecondViewModel(param));
TryClose();
SecondViewModel extends my BaseScreen:
public class BaseSceen : Screen
{
...
}
I want to close the FirstViewModel, because on the FirstViewModel and SecondViewModel I have shortcuts. When I'm with the SecondViewModel opened I hit a shortcut, and the method that is executed it's from FirstViewModel. So, the FirstViewModel still running.
How can I close the FirstViewModel, and avoid this problem with shortcuts?
Thanks!
Do you really need to use Conductor<T>.Collection.OneActive? You can just use Conductor<T> so that activating an item will automatically deactivate and close the previously active item. And also, is it required that the button/action pair reside in the FirstViewModel? I suggest that you just put those in the AppViewModel and let it orchestrate the navigation and activation/deactivation of the two child screens.
public AppViewModel : Conductor<Screen>
{
public void AppViewModel()
{
ActivateItem(new FirstViewModel());
}
public void ActivateSecondViewModel()
{
// FirstViewModel will automatically be deactivated
// and closed since we are using plain Conductor<T>
ActivateItem(new SecondViewModel());
}
}
I found it! The shortcut event was attached to the window, not to the usercontrol.
So, event when usercontrol was ended the event still "attached" to the window. Now, I added an method that is called when UserControl is Unloaded, to "deattach" the event.
Bad mistake!

Pass parameter to a constructor in the ViewModel

I am building a WPF browser application with MVVM pattern.
I have a first page (ConsultInvoice) with a dataGrid. When I double click on one of the row I want to navigate to another page (EditInvoice) passing the selected row in argument to my constructor.
I know if I want do things properly I should use a dependency injection, but I don't really see how to use it here.
How can I simply pass this constructor?
ConsultInvoiceViewModel
private Invoice _selected;
public Invoice Selected
{
get
{
return _selected;
}
set
{
_selected = value;
OnPropertyChanged("Selected");
}
}
private void Edit()
{
EditInvoiceViewModel editInvoice = new EditInvoiceViewModel(Selected);
/* doing something here*/
}
public ICommand EditCommand
{
get
{
return editCommand ?? (editCommand = new RelayCommand(p => this.Edit(), p => this.CanEdit()));
}
}
EditInvoiceViewModel
public class EditInvoiceViewModel : ViewModelBase
{
public Context ctx = new Context();
Invoice invoice;
PreInvoice preInvoice;
#region properties
private ObservableCollection<PreInvoice> collection;
public ObservableCollection<PreInvoice> Collection
{
get
{
return collection;
}
set
{
collection = value;
OnPropertyChanged("Collection");
}
}
#endregion
public EditInvoiceViewModel(Invoice inv)
{
/* do stuff*/
}
}
Basically you should avoid passing such parameters into the ViewModels constructor, as wiring it with Inversion of Control/Dependency Injection becomes a pain. While you can use Abstract Factory pattern to resolve objects with runtime parameters, it's imho not suitable for ViewModels.
Instead I always suggest using a form of navigation pattern, similar to how Microsoft's Patterns & Practices team has done with Prism. There you have an INavigationAware interface which your ViewModels can implement. It has 2 methods, NavigateTo and NavigateFrom.
And there is a navigation service. The navigation service will switch the views and before switching calling NavigateFrom in the current ViewModel (if it implements it. One can use it to check if data is saved and if necessary cancel the navigation. After the new View has been loaded and the ViewModel assigned to it, call NavigateTo in the newly navigated ViewModel.
Here you'd pass the parameters required for the ViewModel, in your case invoiceId. Try avoid passing whole models or complex objects. Use the invoiceid to fetch the invoice data and to populate your editing ViewModel.
A basinc implementation from my former answer (can be found here):
public interface INavigationService
{
// T is whatever your base ViewModel class is called
void NavigateTo<T>() where T ViewModel;
void NavigateToNewWindow<T>();
void NavigateToNewWindow<T>(object parameter);
void NavigateTo<T>(object parameter);
}
public class NavigationService : INavigationService
{
private IUnityContainer container;
public NavigationService(IUnityContainer container)
{
this.container = container;
}
public void NavigateToWindow<T>(object parameter) where T : IView
{
// configure your IoC container to resolve a View for a given ViewModel
// i.e. container.Register<IPlotView, PlotWindow>(); in your
// composition root
IView view = container.Resolve<T>();
Window window = view as Window;
if(window!=null)
window.Show();
INavigationAware nav = view as INavigationAware;
if(nav!= null)
nav.NavigatedTo(parameter);
}
}
// IPlotView is an empty interface, only used to be able to resolve
// the PlotWindow w/o needing to reference to it's concrete implementation as
// calling navigationService.NavigateToWindow<PlotWindow>(userId); would violate
// MVVM pattern, where navigationService.NavigateToWindow<IPlotWindow>(userId); doesn't. There are also other ways involving strings or naming
// convention, but this is out of scope for this answer. IView would
// just implement "object DataContext { get; set; }" property, which is already
// implemented Control objects
public class PlotWindow : Window, IView, IPlotView
{
}
public class PlotViewModel : ViewModel, INotifyPropertyChanged, INavigationAware
{
private int plotId;
public void NavigatedTo(object parameter) where T : IView
{
if(!parameter is int)
return; // Wrong parameter type passed
this.plotId = (int)parameter;
Task.Start( () => {
// load the data
PlotData = LoadPlot(plotId);
});
}
private Plot plotData;
public Plot PlotData {
get { return plotData; }
set
{
if(plotData != value)
{
plotData = value;
OnPropertyChanged("PlotData");
}
}
}
}
An example of the INavigationAware interface used in Prism can be found on the projects github repository.
This makes it easy to pass parameter and async load your data (where there isn't any clean way to do this via constructor, as you can't await an async operation inside the constructor without locking, and doing this kind of things in the constructor is very discouraged).

.NET Login and Splash Screens what to put in Application.Run

In .NET (language: C#) when writing Windows Applications we start the application with a form passed to Application.Run.
But when you have an Application where you don't have a single window to keep it alive...
But rather if any of one type of forms is active you wan't the application to remain.
What I have is e.g. a Login Screen that when the user successfully log's in, I wan't to close.
However calling "Close()" obviously closes the entire app since the login screen is the one passed to the Application.Run.
The next screen has the same "fate" so can't use that either, that will be a screen where the user selects something and then it closes.
Anyways... long story short. I have a few ideas, but all involving some not so neat things.
So what I am asking for here is sort of a "Best Practice" in these cases.
This is not something with a definitive awnser, I know that, so please all good ideas are welcome and discussions around them.
Create a main function where you create the windows (and show them).
For this you must define that the application should not be ended when the main or the last window is closed
The Code Project article ended up being the basis for the solution (as it is now).
There is still some parts to sort out in the "Navigator" part, but the override of the context works perfectly.
The essence of the implementation:
public class ApplicationContextNavigator : ApplicationContext, INavigator
{
private readonly IWindsorContainer container;
private IView Current { ... }
public ApplicationContextNavigator(IWindsorContainer container) {...}
public void Start<TView>() where TView : IView { ... }
public void Start<TView>(IViewInitializer initializer) where TView : IView
{
Current = InitializeView<TView>(initializer);
Application.Run(this);
}
public void Navigate<TView>() where TView : IView { ... }
public void Navigate<TView>(IViewInitializer initializer) where TView : IView
{
IView closing = Current;
IView showing = InitializeView<TView>(initializer);
showing.Location = closing.Location;
showing.Show();
Current = showing;
closing.Close();
}
private IView InitializeView<TView>(IViewInitializer initializer) where TView : IView
{
IView view = container.Resolve<TView>();
initializer.Initialize(view);
return view;
}
protected override void OnMainFormClosed(object sender, EventArgs e)
{
if(sender == Current)
{
base.OnMainFormClosed(sender, e);
}
}
}

How should Application.Run() be called for the main presenter of a MVP WinForms app?

I'm learning to apply MVP to a simple WinForms app (only one form) in C# and encountered an issue while creating the main presenter in static void Main(). Is it a good idea to expose a View from the Presenter in order to supply it as a parameter to Application.Run()?
Currently, I've implemented an approach which allows me to not expose the View as a property of Presenter:
static void Main()
{
IView view = new View();
Model model = new Model();
Presenter presenter = new Presenter(view, model);
presenter.Start();
Application.Run();
}
The Start and Stop methods in Presenter:
public void Start()
{
view.Start();
}
public void Stop()
{
view.Stop();
}
The Start and Stop methods in View (a Windows Form):
public void Start()
{
this.Show();
}
public void Stop()
{
// only way to close a message loop called
// via Application.Run(); without a Form parameter
Application.Exit();
}
The Application.Exit() call seems like an inelegant way to close the Form (and the application). The other alternative would be to expose the View as a public property of the Presenter in order to call Application.Run() with a Form parameter.
static void Main()
{
IView view = new View();
Model model = new Model();
Presenter presenter = new Presenter(view, model);
Application.Run(presenter.View);
}
The Start and Stop methods in Presenter remain the same. An additional property is added to return the View as a Form:
public void Start()
{
view.Start();
}
public void Stop()
{
view.Stop();
}
// New property to return view as a Form for Application.Run(Form form);
public System.Windows.Form View
{
get { return view as Form(); }
}
The Start and Stop methods in View (a Windows Form) would then be written as below:
public void Start()
{
this.Show();
}
public void Stop()
{
this.Close();
}
Could anyone suggest which is the better approach and why? Or there even better ways to resolve this issue?
What about the following:
// view
public void StartApplication() // implements IView.StartApplication
{
Application.Run((Form)this);
}
// presenter
public void StartApplication()
{
view.StartApplication();
}
// main
static void Main()
{
IView view = new View();
Model model = new Model();
Presenter presenter = new Presenter(view, model);
presenter.StartApplication();
}
That way, you don't need to expose the view to the outside. In addition, the view and the presenter know that this view has been started as a "main form", which might be a useful piece of information.
I would go for the second approach.
You could also get rid of the extra property by simply casting view to form in the void Main, since you know it is a form anyway at that point (I see no reason to make it more generic than that since it just starts the winform app)
Application.Run(view as Form);
Things get a bit more complex if you allow more than one way to exit the application (e.g.: a menu item for exiting), or if you prevent closing of the application under certain conditions. In either case, the actual invocation of application closing should usually be invoked from presenter code rather than by simply closing the concrete view. This can be accomplished by using either the Application.Run() or Application.Run(ApplicationContext) overloads and exposing the application exit action via inversion of control.
The exact approach to registering and using the application exit action would depend on the IoC mechanism (e.g.: service locator and/or dependency injection) that you are using. Since you haven't mentioned what your current IoC approach might be, here's a sample that's independent of any particular IoC frameworks:
internal static class Program
{
[STAThread]
private static void Main()
{
ApplicationActions.ExitApplication = Application.Exit;
MainPresenter mainPresenter = new MainPresenter(new MainView(), new Model());
mainPresenter.Start();
Application.Run();
}
}
public static class ApplicationActions
{
public static Action ExitApplication { get; internal set; }
}
public class MainPresenter : Presenter
{
//...
public override void Stop()
{
base.Stop();
ApplicationActions.ExitApplication();
}
}
This basic approach could be adapted quite easily to your preferred IoC approach. For example, if you're using a service locator, you would probably want to consider removing at least the setter on the ApplicationActions.ExitApplication property, and storing the delegate in the service locator instead. If the ExitApplication getter were to remain, it would provide a simple façade to the service locator instance retriever. e.g.:
public static Action ExitApplication
{
get
{
return ServiceLocator.GetInstance<Action>("ExitApplication");
}
}
You could do it in a hundred ways to achieve the ultimate goal of separability of concerns. There is no hard and fast rule here, the basic idea is that presenter deals with presentation logic of the view, while the view has only the dumb knowledge of its own GUI specific classes and stuffs. Some ways I can think of (to broadly put):
1) View kick-starts things and let it decide its presenter. You start like, new View().Start();
// your reusable MVP framework project
public interface IPresenter<V>
{
V View { get; set; }
}
public interface IView<P>
{
P Presenter { get; }
}
public static class PresenterFactory
{
public static P Presenter<P>(this IView<P> view) where P : new()
{
var p = new P();
(p as dynamic).View = view;
return p;
}
}
// your presentation project
public interface IEmployeeView : IView<EmployeePresenter>
{
void OnSave(); // some view method
}
public class EmployeePresenter : IPresenter<IEmployeeView>
{
public IEmployeeView View { get; set; } // enforced
public void Save()
{
var employee = new EmployeeModel
{
Name = View.Bla // some UI element property on IEmployeeView interface
};
employee.Save();
}
}
// your view project
class EmployeeView : IEmployeeView
{
public EmployeePresenter Presenter { get; } // enforced
public EmployeeView()
{
Presenter = this.Presenter(); // type inference magic
}
public void OnSave()
{
Presenter.Save();
}
}
A variant of the above approach would be to enforce stronger generic constraint on view and presenter, but I dont think the complexity outweighs the benefits. Something like this:
// your reusable MVP framework project
public interface IPresenter<P, V> where P : IPresenter<P, V> where V : IView<P, V>
{
V View { get; set; }
}
public interface IView<P, V> where P : IPresenter<P, V> where V : IView<P, V>
{
P Presenter { get; }
}
public static class PresenterFactory
{
public static P Presenter<P, V>(this IView<P, V> view)
where P : IPresenter<P, V>, new() where V : IView<P, V>
{
return new P { View = (V)view };
}
}
// your presentation project
public interface IEmployeeView : IView<EmployeePresenter, IEmployeeView>
{
//...
}
public class EmployeePresenter : IPresenter<EmployeePresenter, IEmployeeView>
{
//...
}
Disadvantages
interacting between forms are less intuitive to me.
Steps involved:
implement IEmployeeView
instantiate presenter by calling PresenterFactory and passing this from the view constructor
ensure view events are wired to their corresponding presenter methods
start off, like new EmployeeView()....
2) Presenter kick-starts things and let it decide its view. You start like, new Presenter().Start();
In this approach presenter instantiates its own view (like approach 1) by means of some dependenchy injection or so, or view can be passed to presenter's constructor. E.g.
// your reusable MVP framework project
public abstract class IPresenter<V> // OK may be a better name here
{
protected V View { get; }
protected IPresenter()
{
View = ...; // dependenchy injection or some basic reflection, or pass in view to ctor
(View as dynamic).Presenter = this;
}
}
public interface IView<P>
{
P Presenter { get; set; }
}
// your presentation project
public interface IEmployeeView : IView<EmployeePresenter>
{
void OnSave(); // some view method
}
public class EmployeePresenter : IPresenter<IEmployeeView>
{
public void Save()
{
var employee = new EmployeeModel
{
Name = View.Bla // some UI element property on IEmployeedView interface
};
employee.Save();
}
}
// your view project
class EmployeeView : IEmployeeView
{
public EmployeePresenter Presenter { get; set; } // enforced
public void OnSave()
{
Presenter.Save();
}
}
Steps involved:
implement IEmployeeView
ensure view events are wired to their corresponding presenter methods
start off, like new EmployeePresenter(....
3) Event based, observer style
Here you could either encapsulate presenter in view (instantiate presenter in view) like approach 1 or encapsulate view in presenter (instantiate view in presenter) like approach 2 but in my experience latter will always be the cleaner design to work with. An e.g. of latter:
// your reusable MVP framework project
public abstract class IPresenter<V> where V : IView
{
protected V View { get; }
protected IPresenter()
{
View = ...; // dependenchy injection or some basic reflection, or pass in view to ctor
WireEvents();
}
protected abstract void WireEvents();
}
// your presentation project
public interface IEmployeeView : IView
{
// events helps in observing
event Action OnSave; // for e.g.
}
public class EmployeePresenter : IPresenter<IEmployeeView>
{
protected override void WireEvents()
{
View.OnSave += OnSave;
}
void OnSave()
{
var employee = new EmployeeModel
{
Name = View.Bla // some UI element property on IEmployeedView interface
};
employee.Save();
}
}
// your view project
class EmployeeView : IEmployeeView
{
public event Action OnSave;
void OnClicked(object sender, EventArgs e) // some event handler
{
OnSave();
}
}
// you kick off like new EmployeePresenter()....
Disadvantage:
You have to wire events on both view and presenter sides - double the work
Steps involved:
implement IEmployeeView
ensure iview events are called from view event handler methods
ensure iview event members are initialized from presenter
start off, like new EmployeePresenter()....
Limitations of language sometimes make design patterns more difficult. For e.g, had multiple inheritance been possible in C#, it was only a matter of having an abstract base view class with all the implementation details except UI specific components which could be then implemented by view class. No presenters, classic polymorphism and dead simple! Unfortunately this is not possible since most view classes in .NET (like Form of WinForms) already inherits from a super view class. So we have to implement an interface and go for composition. Also, C# doesnt let you have non-public members in an interface implementation, so we are forced to make all members specified in IEmployeeView public which breaks the natural encapsulation rules of the view class (i.e. other views in the view project can see details of EmployeeView irrelevant to them). Anyway, using power of C#'s extension methods a much simpler but very limited approach can be taken.
4) Extension method approach
This is just silly.
// your presentation project
public interface IEmployeeView
{
void OnSave(); // some view method
}
public static class EmployeePresenter // OK may need a better name
{
public void Save(this IEmployeeView view)
{
var employee = new EmployeeModel
{
Name = view.Bla // some UI element property on IEmployeedView interface
};
employee.Save();
}
}
// your view project
class EmployeeView : IEmployeeView
{
public void OnSave()
{
this.Save(); // that's it. power of extensions.
}
}
Disadvantages:
fairly unusable for anything remotely complex
Steps involved:
implement IEmployeeView
ensure this.... extension method is called from view events
kick off things by calling new View...
Of all 2 and 3 look better to me.

Categories

Resources