How to Populate Combobox Based on User Input Using MVP? - c#

How To Populate a ComboBox Based on User Input Using C# and WinForms?
I have a combobox which I need to populate based on user input (2 different options), while also using the MVP pattern (Model View Presenter). This is in a C# WinForms project.
For example purposes, let's say I have 2 different categories: Fruits and Shoes. If the user chooses Fruits, then my combobox should populate with fruits, if my user chooses Shoes, then my combobox should populate with shoes.
My category data (Fruits and Shoes) are both stored in separate tables in my database.
Currently I have a Model, View, and Presenter for both categories, which works great, but seems very repetitive (especially when dealing with more than 2 categories). Is there an additional piece/design pattern I can implement to cut down on the repetitiveness?
I have provided some example code below, in the following order:
1. Model, View, and Presenter for Fruits category
2. Model, View, and Presenter for Shoes category
3. WinForms Code Behind Page
////////FRUITS MVP////////
//Model
public class FruitsComboBoxModel
{
public List<Fruits> Fruits { get; set; }
}
//View
public interface IFruitsComboBoxModel
{
void ShowFruitsComboBox(FruitsComboBoxModel fruitsComboBoxModel);
}
//Presenter
public class FruitsComboBoxPresenter
{
IFruitsComboBoxView fruitsComboBoxView;
public FruitsComboBoxPresenter(IFruitsComboBoxView view)
{
fruitsComboBoxView = view;
}
public void Init()
{
var model = GetModel();
fruitsComboBoxView.ShowFruitsComboBox(model);
}
private FruitsComboBoxModel GetModel()
{
var dbFruits = GetFruitsFromDataBase(); //fake call to DB for fruits
var fruitsComboBoxModel = new FruitsComboBoxModel
{
Fruits = dbFruits;
}
return fruitsComboBoxModel;
}
}
////////SHOES MVP////////
//Model
public class ShoesComboBoxModel
{
public List<Shoes> Shoes { get; set; }
}
//View
public interface IShoesComboBoxModel
{
void ShowShoesComboBox(ShoesComboBoxModel shoesComboBoxModel);
}
//Presenter
public class ShoesComboBoxPresenter
{
IShoesComboBoxView shoesComboBoxView;
public ShoesComboBoxPresenter(IShoesComboBoxView view)
{
shoesComboBoxView = view;
}
public void Init()
{
var model = GetModel();
shoesComboBoxView.ShowShoesComboBox(model);
}
private ShoesComboBoxModel GetModel()
{
var dbShoes = GetShoesFromDataBase(); //fake call to DB for shoes
var shoesComboBoxModel = new ShoesComboBoxModel
{
Shoes = dbShoes;
}
return shoesComboBoxModel;
}
}
////////Code Behind Page////////
public partial class ExampleForm : Form, IFruitsComboBoxView, IShoesComboBoxView
{
public CategoryType categoryType { get; set; }
FruitsComboBoxPresenter fruitsComboBoxPresenter;
ShoesComboBoxPresenter shoesComboBoxPresenter;
public ExampleForm(CategoryType type)
{
categoryType = type; //user category selection
}
private void ExampleForm_Load(object sender, EventArgs e)
{
if (categoryType == CategoryType.Fruits)
{
fruitsComboBoxPresenter = new FruitsComboBoxPresenter(this);
fruitsComboBoxPresenter.Init();
}
else if (categoryType == CategoryType.Shoes)
{
shoesComboBoxPresenter = new ShoesComboBoxPresenter(this);
shoesComboBoxPresenter.Init();
}
else
{
throw new Exception("Invalid type detected");
}
}
public void ShowFruitsComboBox(FruitsComboBoxModel fruitsComboBoxModel)
{
comboBox.DataSource = fruitsComboBoxModel.Fruits.Select(x => x.Name).ToList();
}
public void ShowShoesComboBox(ShoesComboBoxModel shoesComboBoxModel)
{
comboBox.DataSource = shoesComboBoxModel.Shoes.Select(x => x.Name).ToList();
}
}
I have debated using only one Model, View, and Presenter for the combobox and on my model having a fruits property (list) and having a shoes property (shoes). But this means I will have a lot of if/else logic inside my model, and have to pass down the user selection.

I think you can make use of some interfaces and abstract classes to reduce some of your duplicate code.
This will require some tweaking, but if you have some compile-able code I can take another look.
public interface IComboItem {
string Name {get; set;}
object Value {get; set;}
}
public class Fruit : IComboItem {
//fruit stuff
}
public class Shoe : IComboItem {
//shoe stuff
}
//View
public interface IComboBoxModel
{
void ShowComboBox(List<IComboItem> comboItems);
}
//Presenter
public abstract class ComboBoxPresenter {
IComboBoxView comboBoxView;
public ComboBoxPresenter(IComboBoxView view){
comboBoxView = view;
}
public void Init(){
var model = GetModel();
comboBoxView.ShowComboBox(model);
}
//force implementors to get the model
private abstract List<IComboItem> GetModel();
}
public class FruitsComboBoxPresenter : ComboBoxPresenter
{
private override List<Fruit> GetModel()
{
return GetFruitsFromDataBase(); //fake call to DB for fruits
}
}
public class ShoeComboBoxPresenter : ComboBoxPresenter
{
private override List<Shoe> GetModel()
{
return GetShoesFromDataBase(); //fake call to DB for fruits
}
}
////////Code Behind Page////////
public partial class ExampleForm : Form, IComboBoxView
{
public CategoryType categoryType { get; set; }
IComboBoxPresenter comboBoxPresenter;
public ExampleForm(CategoryType type)
{
categoryType = type; //user category selection
}
private void ExampleForm_Load(object sender, EventArgs e)
{
if (categoryType == CategoryType.Fruits)
{
comboBoxPresenter = new FruitsComboBoxPresenter(this);
}
else if (categoryType == CategoryType.Shoes)
{
comboBoxPresenter = new ShoesComboBoxPresenter(this);
}
else
{
throw new Exception("Invalid type detected");
}
comboBoxPresenter.Init();
}
public void ShowComboBox(List<IComboItem> comboItems)
{
comboBox.DataSource = comboItems.Select(x => x.Name).ToList();
}
}

I will suggest one view for one presenter. If you need use the multi-model write the code into presenter.
Create the Contract class then define the View and Presenter Interface
public class ExampleFormContract
{
public interface IView
{
// Display the item to the view
void DisplayComboBoxItems(IEnumerable<string> items);
// set the presneter
IPresenter Presenter { set; }
}
public interface IPresenter
{
// init form load
void Init();
}
}
Create ExampleFormPresenter and implement the ExampleFormContract.IPresenter
public class ExampleFormPresenter : ExampleFormContract.IPresenter
{
private ExampleFormContract.IView View { get; set; }
private CategoryType Type { get; set; }
public ExampleFormPresenter(ExampleFormContract.IView view, CategoryType type)
{
// set the view and use the Dependency Injection (IoC)
View = view;
View.Presenter = this;
Type = type;
}
public void Init()
{
// you can write your logic code to here.
if (Type == CategoryType.Fruits)
{
var dbFruits = GetFruitsFromDataBase(); //fake call to DB for fruits
var fruitsLists = dbFruits.Select(x => x.Name).ToList();
// update the item to the view
View.DisplayComboBoxItems(fruitsLists);
}
else if (Type == CategoryType.Shoes)
{
var dbShoes = GetShoesFromDataBase(); //fake call to DB for shoes
var shoesLists = dbShoes.Select(x => x.Name).ToList();
// update the item to the view
View.DisplayComboBoxItems(shoesLists);
}
else
{
throw new Exception("Invalid type detected");
}
}
}
Modify the ExampleForm.cs implement ExampleFormContract.IView
public partial class ExampleForm : Form, ExampleFormContract.IView
{
// set the presenter
public ExampleFormContract.IPresenter Presenter { private get; set; }
public ExampleForm()
{
InitializeComponent();
}
private void ExampleForm_Load(object sender, EventArgs e)
{
Presenter.Init();
}
public void DisplayComboBoxItems(IEnumerable<string> items)
{
// update the view
comboBox.DataSource = items;
}
}
Demo
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
// create the form
var form = new ExampleForm();
// use the IoC and Inject the CategoryType what you want
var presneter = new ExampleFormPresenter(form, CategoryType.Fruits);
Application.Run(form);
}
This MVP architecture work fine for me into C# winform. I think it will help you.

Related

C# MVVM How to update viewmodel string from the model

I am really new to mvvm and wpf in c# and got stuck at some very basic stuff.In this example I am using Fody.PropertyChanged. I have a basic viewmodel that holds a string called Test which is binded to a textblock.
public class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
public string Test { get; set; }
}
Then,in a separate file and class called Data,I have a simple function that increments an int and converts it to a string.
public class Data
{
public static int i = 0;
public static string IncTest { get; set; }
public static void Inc()
{
i++;
IncTest = i.ToString();
}
}
How do I update the Test variable inside the viewmodel when calling the Inc() function? For example, when clicking a button
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new Model();
Data.Inc();
}
private void Increment_Click(object sender, RoutedEventArgs e)
{
Data.Inc();
}
In MVVM, the model does not update the view model, its actually opposite, The view model updates the model properties.
Here is an example.
MODEL:
public class Model
{
public string Test
{
get;
set;
}
}
View Model:
public class ViewModel : INotifyPropertyChanged
{
private Model _model;
public string Test
{
get
{
return _model.Test;
}
set
{
if(string.Equals(value, _model.Test, StringComparison.CurrentCulture))
{
return;
}
_model.Test = value;
OnPropertyChanged();
}
}
public ViewModel(Model model)
{
_model = model;
}
}
Your views will bind to your view models.
UPDATE: In regards to your question
public class SomeClass
{
public static void Main(string [] args)
{
Model model = new Model();
ViewModel viewModel = new ViewModel(model);
//Now setting the viewmodel.Test will update the model property
viewModel.Test = "This is a test";
}
}

Avoiding usage of Enums for abstraction and dependency injection, struggling at concept

So right now I am trying to design a new hire program that grants access to active directory groups, generates documents with their information and location.
Right now I am doing this with an enumeration, with a switch statement that sets the details on the ViewModel like this:
case CaneRidgeSettings.Departments.SCSC:
Model.ScannerFolder = #"scan1\Supply Chain Service Center\" + Model.UserId;
Model.ExtensionRanges = "list station 8000 to-ext 8349";
Model.AdministrativeAssistant = Loader.SCSCAdminAssistant;
Model.DuoCode = "Franklin TN - 8175";
Model.PrinterSelectedIndex = (int)CaneRidgeSettings.PrinterGroups.Cane_Ridge_5th_Floor_West;
return await find.FindNextComputer("800SCSC");
The problem I have with this design is that if I ever add more departments to this building, I have to manually update this switch. So I tried a few things around this such as a dictionary, but it didn't seem to bind to a combo-box very well (even when implementing my own INotifyCollectionChanged).
So instead I created an interface that contains this information, for simplicity and length lets just say the interface does this:
public interface IDepartmentInfo
{
string DepartmentName { get; }
List<string> ActiveDirectoryGroups { get; }
string AdministrativeAssistant { get; }
string Floor { get; }
}
I then created a new class that implements this interface
public class SCSC : IDepartmentInfo
{
public string DepartmentName { get; } = "Shared Services";
public List<string> ActiveDirectoryGroups { get; } = new List<string>() {"Example_AD_GRP","Domain_Users"};
public string AdministrativeAssistant { get; } = "Lisa_Smith#outlook.com";
public string Floor { get; } = "5th Floor East";
public override string ToString() => DepartmentName;
}
Then, on my main Building Class I have an observable collection that expects an IDepartmentInfo and initializes those departments
public class CaneRidgeBuilding : IBuilding
{
public ObservableCollection<IDepartmentInfo> Departments { get; set; } = new ObservableCollection<IDepartmentInfo>() {new SCSC(), new ARS()};
public override string ToString()
{
return "CaneRidge";
}
}
On my View Model I implemented a few properties, mainly the BuildingSelectedIndex and the DepartmentSelectedIndex.
I also have an IDepartmentInfo property that notifies when it is changed because it is databound to several labels on my UI.
public class MainWindowViewModel : BindableBase
{
public ObservableCollection<IBuilding> Buildings { get; set; } = new ObservableCollection<IBuilding>() { new CaneRidgeBuilding() };
private ObservableCollection<IDepartmentInfo> _departmentInfos = new ObservableCollection<IDepartmentInfo>();
public ObservableCollection<IDepartmentInfo> DepartmentInfos
{
get { return _departmentInfos; }
set { SetProperty(ref _departmentInfos, value); }
}
private int _buildingIndex = -1;
public int BuildingIndex
{
get { return _buildingIndex; }
set
{
SetProperty(ref _buildingIndex, value);
SetDepartments();
}
}
private void SetDepartments()
{
if (BuildingIndex != -1)
DepartmentInfos = Buildings[BuildingIndex].Departments;
}
private int _departmentIndex = -1;
public int DepartmentIndex
{
get { return _departmentIndex; }
set
{
SetProperty(ref _departmentIndex, value);
LoadDepartmentSettings();
}
}
private IDepartmentInfo _departmentInformation;
public IDepartmentInfo DepartmentInformation
{
get { return _departmentInformation; }
set { SetProperty(ref _departmentInformation, value); }
}
private void LoadDepartmentSettings()
{
if (DepartmentIndex != -1)
DepartmentInformation = DepartmentInfos[DepartmentIndex];
}
private string _title = "Prism Application";
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public MainWindowViewModel()
{
}
}
And it works exactly the way I want it to, however to problem I am running into now is how would I handle dependency injection? If I have 10 departments implementing IDepartmentInfo, how exactly could I pass this to an observable collection?
Because the moment I introduce a new building, if I tell Unity to resolve all IDepartmentInfos, what is going to happen is I'll get every single department even if it doesn't belong to CaneRidge.
If I split the departments to each building, then I run into issues where I can't easily load the departments into the ViewModel, because it is expecting an IDepartmentInfo collection. If I limited it to just one type of collection, then it wouldn't work.
Am I over-complicating things?
Here is an idea.
Custom attribute
Introduce a BuilingAttribute so each IDepartmentInfo implementation can declare Type of the building it belongs to (allow multiple if one department can belong to multiple buildings, I got the idea it can't).
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class BuildingAttribute : Attribute
{
public Type BuildingType { get; private set; }
public BuildingAttribute(Type buildingType)
{
this.BuildingType = buildingType;
}
}
DepartmentInfo Collection Factory
An interface that knows how to create a collection of DepartmentInfo for each building Type.
public interface IDepartmentInfoCollectionFactory
{
void RegisterDepartment<T>(Func<IDepartmentInfo> departmentCreator) where T : class, IBuilding;
ObservableCollection<IDepartmentInfo> GetDepartments<T>() where T : class, IBuilding;
}
And the implementation (will be registered as singleton).
public class DepartmentInfoCollectionFactory : IDepartmentInfoCollectionFactory
{
private readonly Dictionary<Type, List<Func<IDepartmentInfo>>> departmentCreators =
new Dictionary<Type, List<Func<IDepartmentInfo>>>();
void IDepartmentInfoCollectionFactory.RegisterDepartment<T>(Func<IDepartmentInfo> departmentCreator)
{
Type buildingType = typeof(T);
if (!this.departmentCreators.ContainsKey(buildingType))
this.departmentCreators.Add(buildingType, new List<Func<IDepartmentInfo>>());
if (!this.departmentCreators[buildingType].Contains(departmentCreator))
this.departmentCreators[buildingType].Add(departmentCreator);
}
ObservableCollection<IDepartmentInfo> IDepartmentInfoCollectionFactory.GetDepartments<T>()
{
Type buildingType = typeof(T);
if (!this.departmentCreators.ContainsKey(buildingType))
throw new InvalidOperationException(
string.Format("No departments have been registered for {0}.", buildingType.ToString()));
ObservableCollection<IDepartmentInfo> departmentInfos = new ObservableCollection<IDepartmentInfo>();
foreach(Func<IDepartmentInfo> creator in this.departmentCreators[buildingType])
{
departmentInfos.Add(creator());
}
return departmentInfos;
}
}
Configuring the factory, so it knows how to create IDepartmentInfo collections.
protected override void ConfigureContainer()
{
Container.RegisterType<IDepartmentInfoCollectionFactory, DepartmentInfoCollectionFactory>(
new ContainerControlledLifetimeManager());
this.ConfigureDepartmentInfoCollectionFactory(Container.Resolve<IDepartmentInfoCollectionFactory>());
}
private void ConfigureDepartmentInfoCollectionFactory(IDepartmentInfoCollectionFactory factory)
{
// Types implementing IDepartmentInfo
var deptInfoTypes = AppDomain.CurrentDomain
.GetAssemblies()
.SelectMany(s => s.GetTypes())
.Where(t => typeof(IDepartmentInfo).IsAssignableFrom(t) && !t.IsInterface);
foreach(Type type in deptInfoTypes)
{
// Get collection of BuildingAttribute for the type
var buildingAttributes = type.GetCustomAttributes(typeof(BuildingAttribute), false)
.OfType<BuildingAttribute>();
if (buildingAttributes.Count() < 1)
throw new InvalidOperationException(
string.Format("The type {0} didn't declare BuildingArgument.", type.ToString()));
var buildingType = buildingAttributes.First().BuildingType;
if (buildingType == null || !buildingType.GetInterfaces().Contains(typeof(IBuilding)))
throw new InvalidOperationException(
string.Format("{0}: BuildingType is not an IBuilding.", type.ToString()));
var registerMethod = typeof(IDepartmentInfoCollectionFactory).GetMethod("RegisterDepartment")
.MakeGenericMethod(new Type[] { buildingType });
registerMethod.Invoke(factory, new object[]
{
new Func<IDepartmentInfo>(() => (IDepartmentInfo)Container.Resolve(type))
});
}
}
Inject the factory.
public class FooBuilding : IBuilding
{
private IDepartmentInfoCollectionFactory factory;
private readonly ObservableCollection<IDepartmentInfo> departmentInfos;
public string Name { get; } = "FooBuilding";
public ObservableCollection<IDepartmentInfo> DepartmentInfos
{
get { return this.departmentInfos; }
}
public FooBuilding(IDepartmentInfoCollectionFactory factory)
{
this.factory = factory;
this.departmentInfos = factory.GetDepartments<FooBuilding>();
}
}
Adding new department
It doesn't require any editing, just create new class with the attribute.
[Building(typeof(FooBuilding))]
public class BarDepartment : IDepartmentInfo
{
public string Name { get; } = "Bar department";
}
I was able to figure out how to inject different buildings and departments, probably not the best way
EDIT: Updated it to use reflection to make it less maintenance
protected override void ConfigureContainer()
{
base.ConfigureContainer();
Container.RegisterTypes(AllClasses.FromLoadedAssemblies()
.Where(type => typeof(IDepartment).IsAssignableFrom(type)), WithMappings.FromAllInterfaces, WithName.TypeName, WithLifetime.None);
ObservableCollection<IBuilding> Buildings = new ObservableCollection<IBuilding>()
{
Container.Resolve<Building1>(new ParameterOverride("departments",GetDepartmentCollection("Building1"))),
Container.Resolve<Building2>(new ParameterOverride("departments",GetDepartmentCollection("Building2")))
};
Container.RegisterInstance(typeof(ObservableCollection<IBuilding>), Buildings,
new ExternallyControlledLifetimeManager());
}
private ObservableCollection<IDepartment> GetDepartmentCollection(string buildingName)
{
var departments = new List<IDepartment>();
foreach (var registration in Container.Registrations.Where( s => s.MappedToType.Namespace.Contains(buildingName)))
{
departments.Add((IDepartment)Container.Resolve(registration.MappedToType));
}
return new ObservableCollection<IDepartment>(departments);
}
Now I am able to completely eliminate the enumeration and it can be extended in the future without breaking any code or requiring me to change anything.

Create game inventory system, without casting to derived

I am trying to implement a high-performance game inventory system. I have This abstract base class to store different type of items in Inventory, for example, Coin, Flashlight, Knife etc..
public abstract class ObtainableItem
{
public string Name { get; private set; }
public ObtainableItem(string name)
{
Name = name;
}
}
For example, I have a DoorKey which opens a door. DoorKey has a property KeyCode which will be used for opening a door.
public class DoorKey : ObtainableItem
{
public int KeyCode { get; private set; }
public DoorKey() : base("key")
{
KeyCode = 1234;
}
}
All ObtainableItem are stored in Inventory
public class Inventory
{
const int slotCount = 2;
ObtainableItem[] slots = new ObtainableItem[slotCount];
public Inventory()
{
slots[0] = new DoorKey();
}
}
Now imagine user drags DoorKey from his Inventory on a Door and triggers Open method
public class Door
{
public void Open(ObtainableItem key)
{
if (key is DoorKey)
{
DoorKey doorKey = (DoorKey)key;
if (doorKey.KeyCode == 1234)
{
// Open door
}
}
else
{
// "can't use this item on a door"
}
}
}
How to avoid cast from ObtainableItem to a DoorKey? I have read that using casting is bad practice and it points at a bad code oop design. Ideally, a Door class should look like this. Is there any pattern I should for my inventory system?
public class Door
{
public void Open(DoorKey key)
{
if (key.KeyCode == 1234)
{
// Open door
}
}
}
There are always exceptions that can be made for ease of implementation and readability. What you describe is common, if not typical.
An alternative would be to have the "control" logic in the class that calls Door.Open. This could be easily achieved with a touch of reflection:
public abstract class ObtainableItem
{
public string Name { get; private set; }
public ObtainableItem(string name)
{
Name = name;
}
}
public abstract class WorldItem
{
}
public interface IActsOn<in TWorldItem>
where TWorldItem : WorldItem
{
void ApplyTo(TWorldItem worldItem);
}
public class World
{
// If profiling shows that this is a performance issue, a cache keyed by tWorldItem, tInvItem
// should fix it. No expiry or invalidation should be needed.
private Action<ObtainableItem, WorldItem> GetApplyTo(Type tWorldItem, Type tInvItem)
{
var tActOn = typeof(IActsOn<>).MakeGenericType(tWorldItem);
if (!tActOn.IsAssignableFrom(tInvItem))
{
return null;
}
var methodInfo = tActOn.GetMethod(nameof(IActsOn<WorldItem>.ApplyTo));
return new Action<ObtainableItem, WorldItem>((invItem, worldItem) =>
{
methodInfo.Invoke(invItem, new object[] { worldItem });
});
}
public bool IsDropTarget(WorldItem worldItem, ObtainableItem item)
=> GetApplyTo(worldItem.GetType(), item.GetType()) != null;
public void ActOn(WorldItem worldItem, ObtainableItem item)
{
var actOn = GetApplyTo(worldItem.GetType(), item.GetType());
if (actOn == null)
{
throw new InvalidOperationException();
}
actOn(item, worldItem);
}
}
While this slightly complicates the implementation of World, it simplifies the implementation of various objects:
class Door : WorldItem
{
public void Unlock(string bitting)
{
if (bitting == "1234")
{
Console.WriteLine("Door Opened");
}
else
{
Console.WriteLine("Door could not unlock");
}
}
}
class DoorKey : ObtainableItem, IActsOn<Door>
{
private readonly string Bitting;
public DoorKey(string bitting)
: base("Key")
{
this.Bitting = bitting;
}
public void ApplyTo(Door worldItem)
{
worldItem.Unlock(this.Bitting);
}
}
class RubberChicken : ObtainableItem
{
public RubberChicken()
: base("Rubber chicken")
{
}
}
Example usage:
class Program
{
static void Main(string[] args)
{
var key1 = new DoorKey("1234");
var key2 = new DoorKey("4321");
var rubberChicken = new RubberChicken();
var door = new Door();
var world = new World();
Debug.Assert(!world.IsDropTarget(door, rubberChicken));
Debug.Assert(world.IsDropTarget(door, key1));
world.ActOn(door, key2);
world.ActOn(door, key1);
Console.ReadLine();
}
}

Using delegate and inheritance to define which method is run inside the child class

I have a child and a parent class in C#. I want to point the Calc delegate to one of several methods. this is determined each time the Reset() method is called.
Below is a working example.
However, I want this functionality of pointing the delegate to reside on the Parent class. Since the parent does not contain the methods, I don't know how to do it...
class Program
{
static void Main(string[] args)
{
Model Model = new Model();
Model.Env1 = true;
Child Ch = new Child(Model);
Ch.Reset();
Ch.Calc(); Ch.Calc(); Ch.Calc();
Console.WriteLine();
Model.Env1 = false;
Ch.Reset();
Ch.Calc(); Ch.Calc(); Ch.Calc();
Console.ReadLine();
}
}
public class Parent
{
public Model Model { get; set; }
public Parent(Model model)
{
Model = model;
}
public delegate void StateHandler();
public StateHandler Calc;
}
public class Model
{
public bool Env1 { get; set; }
}
public class Child : Parent, IChild
{
public Child (Model model)
: base (model)
{
}
public void Reset()
{
if (Model.Env1)
Calc = CalcHeavy;
else
Calc = CalcLight;
}
public void CalcHeavy()
{
Console.WriteLine("CalcHeavy is active");
}
public void CalcLight()
{
Console.WriteLine("CalcLight is active");
}
}
public interface IChild
{
void CalcHeavy();
void CalcLight();
}
One way to do that would be to have the Child class inject its methods into the Parent at construction time. That way, the Parent class makes the choice, but the Child class defines the functionality:
public class Parent
{
private readonly Action _env1Method;
private readonly Action _notEnv1Method;
private readonly Model _model;
public Parent(Model model,
Action env1Method,
Action notEnv1Method)
{
_model = model;
_env1Method = env1Method;
_notEnv1Method = notEnv1Method;
Reset();
}
public Action Calc { get; private set; }
public void Reset()
{
Calc = _model.Env1 ? _env1Method : _notEnv1Method;
}
}
public class Model
{
public bool Env1 { get; set; }
}
public class Child : Parent
{
public Child (Model model) : base (model, CalcHeavy, CalcLight) {}
private static void CalcHeavy()
{
Console.WriteLine("CalcHeavy is active");
}
private static void CalcLight()
{
Console.WriteLine("CalcLight is active");
}
}
Also, I'd get rid of StateHandler and just use Action instead. Don't create a new delegate when a standard one exists already.
While trying to get the answer I liked to work, I managed to solve it in a different way by making the Parent abstract, and it works.
class Program
{
static void Main(string[] args)
{
Model Model = new Model();
Model.Env1 = true;
Child Ch = new Child(Model);
Ch.Calc(); Ch.Calc(); Ch.Calc();
Console.WriteLine();
Model.Env1 = false;
Ch = new Child(Model);
Ch.Calc(); Ch.Calc(); Ch.Calc();
Console.ReadLine();
}
}
public abstract class Parent
{
public Model Model { get; set; }
public Parent(Model model)
{
Model = model;
if (Model.Env1)
Calc = CalcHeavy;
else
Calc = CalcLight;
}
public Action Calc;
public abstract void CalcHeavy();
public abstract void CalcLight();
}
public class Model
{
public bool Env1 { get; set; }
}
public class Child : Parent
{
public Child(Model model) : base(model) { }
public override void CalcHeavy()
{
Console.WriteLine("CalcHeavy is active");
}
public override void CalcLight()
{
Console.WriteLine("CalcLight is active");
}
}
One way is to move the "Reset" method to the parent class and do something like this:
public void Reset()
{
var child = this as IChild;
if (child != null && Model.Env1)
Calc = child.CalcHeavy;
else
Calc = child.CalcLight;
}

Event not triggered in MVP Supervising Controller

I have create abstract class for Presenter
public abstract class MvpPresenter<T>
{
public T View { get; set; }
public MvpPresenter()
{
}
public MvpPresenter(T view)
{
View = view;
}
}
and UserPresenter class that inherit from abstract Presenter
public class UserPresenter2 : MvpPresenter<IUserView>
{
private void OnUserSave(object sender, EventArgs e)
{
if (View.ContextData.IsDirty)
{
User user = new User();
User domainUser = DataMapper.Translate(View.ContextData);
new UserServiceStub().SaveUser(domainUser);
}
}
private void OnUserSearch(object sender, SearchEventArgs e)
{
if (string.IsNullOrEmpty(e.SearchCriteria))
{
View.StatusMessage = "User name can not be null";
return;
}
User user = new UserServiceStub().GetUser(e.SearchCriteria);
if (user == null)
{
View.StatusMessage = String.Format(
"There's no user found for user name:{0}", e.SearchCriteria);
return;
}
UserDTO userDTO = DataMapper.Translate(user);
View.ContextData = userDTO;
}
}
code for view:
public abstract class MvpView<TPresenter, TView> : Page
where TPresenter : MvpPresenter<TView>
{
public TPresenter Presenter { get; set; }
public MvpView()
: base()
{
if (!(this is TView))
throw new Exception("MvpView must implement the interface provider as generic TView type");
// Create and initialize presenter
Presenter = Activator.CreateInstance<TPresenter>();
Presenter.View = (TView)((object)this);
}
}
the problem is when user click search, the page didn't response. I know that search event has not registered yet with the View, but I cannot do it in constructor.

Categories