按惯例/reflection动态连接视图,模型和演示者

我正在尝试使用MVP模式开发应用程序。

问题是手动连接所有代码。 我希望减少所需的代码。 我试图复制另一个解决方案,但我无法开始工作。 我正在使用Winforms,我使用的解决方案是使用WPF。

它会在某些约定上汇总事物:

视图事件按名称连接。 例如:视图上的“已加载”事件将连接到演示者上的“OnLoaded()”方法按钮命令按名称连接。 例如:MoveNext“按钮连接到”OnMoveNext()“方法。网格双击按名称连接。例如:双击”Actions“将连接到”OnActionsChoosen(ToDoAction)“

WPF中的工作代码是:

private static void WireListBoxesDoubleClick(IPresenter presenter) { var presenterType = presenter.GetType(); var methodsAndListBoxes = from method in GetActionMethods(presenterType) where method.Name.EndsWith("Choosen") where method.GetParameters().Length == 1 let elementName = method.Name.Substring(2, method.Name.Length - 2 /*On*/- 7 /*Choosen*/) let matchingListBox = LogicalTreeHelper.FindLogicalNode(presenter.View, elementName) as ListBox where matchingListBox != null select new {method, matchingListBox}; foreach (var methodAndEvent in methodsAndListBoxes) { var parameterType = methodAndEvent.method.GetParameters()[0].ParameterType; var action = Delegate.CreateDelegate(typeof(Action).MakeGenericType(parameterType), presenter, methodAndEvent.method); methodAndEvent.matchingListBox.MouseDoubleClick += (sender, args) => { var item1 = ((ListBox)sender).SelectedItem; if(item1 == null) return; action.DynamicInvoke(item1); }; } } private static void WireEvents(IPresenter presenter) { var viewType = presenter.View.GetType(); var presenterType = presenter.GetType(); var methodsAndEvents = from method in GetParameterlessActionMethods(presenterType) let matchingEvent = viewType.GetEvent(method.Name.Substring(2)) where matchingEvent != null where matchingEvent.EventHandlerType == typeof(RoutedEventHandler) select new { method, matchingEvent }; foreach (var methodAndEvent in methodsAndEvents) { var action = (Action)Delegate.CreateDelegate(typeof(Action), presenter, methodAndEvent.method); var handler = (RoutedEventHandler)((sender, args) => action()); methodAndEvent.matchingEvent.AddEventHandler(presenter.View, handler); } } private static IEnumerable GetActionMethods(Type type) { return from method in type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance) where method.Name.StartsWith("On") select method; } private static IEnumerable GetParameterlessActionMethods(Type type) { return from method in GetActionMethods(type) where method.GetParameters().Length == 0 select method; } 

无论如何,我试图将它移植到WinForm应用程序,但我没有成功。 我将RoutedEventHandlers更改为EventHandlers ,但无法找到有关LogicalTreeHelper

我有点坚持这个。 我可以手动做但我发现这个迷你框架如此天真,以至于它几乎是疯了。

PS:来源是http://msdn.microsoft.com/en-us/magazine/ee819139.aspx

编辑

我刚刚意识到了什么。 我不是傻了,上面的代码不是非常考试,是吗?

好。 我让它自己工作了。 我只是发布答案,因为至少有一个人觉得有趣。

一,观点

 public interface IBaseView { void Show(); C Get(string controlName) where C : Control; //Needed to later wire the events } public interface IView : IBaseView { TextBox ClientId { get; set; } //Need to expose this Button SaveClient { get; set; } ListBox MyLittleList { get; set; } } public partial class View : Form, IView { public TextBox ClientId //since I'm exposing it, my "concrete view" the controls are camelCased { get { return this.clientId; } set { this.clientId = value; } } public Button SaveClient { get { return this.saveClient; } set { this.saveClient = value; } } public ListBox MyLittleList { get { return this.myLittleList; } set { this.myLittleList = value; } } //The view must also return the control to be wired. public C Get(string ControlName) where C : Control { var controlName = ControlName.ToLower(); var underlyingControlName = controlName[0] + ControlName.Substring(1); var underlyingControl = this.Controls.Find(underlyingControlName, true).FirstOrDefault(); //It is strange because is turning PascalCase to camelCase. Could've used _Control for the controls on the concrete view instead return underlyingControl as C; } 

现在主持人:

 public class Presenter : BasePresenter  { Client client; IView view; ViewModel viewModel; public Presenter(int clientId, IView viewParam, ViewModel viewModelParam) { this.view = viewParam; this.viewModel = viewModelParam; client = viewModel.FindById(clientId); BindData(client); wireEventsTo(view); //Implement on the base class } public void OnSaveClient(object sender, EventArgs e) { viewModel.Save(client); } public void OnEnter(object sender, EventArgs e) { MessageBox.Show("It works!"); } public void OnMyLittleListChanged(object sender, EventArgs e) { MessageBox.Show("Test"); } } 

“魔法”发生在基类。 在wireEventsTo(IBaseView视图)中

 public abstract class BasePresenter  where VM : BaseViewModel where V : IBaseView, new() { protected void wireEventsTo(IBaseView view) { Type presenterType = this.GetType(); Type viewType = view.GetType(); foreach (var method in presenterType.GetMethods()) { var methodName = method.Name; if (methodName.StartsWith("On")) { try { var presenterMethodName = methodName.Substring(2); var nameOfMemberToMatch = presenterMethodName.Replace("Changed", ""); //ListBoxes wiring var matchingMember = viewType.GetMember(nameOfMemberToMatch).FirstOrDefault(); if (matchingMember == null) { return; } if (matchingMember.MemberType == MemberTypes.Event) { wireMethod(view, matchingMember, method); } if (matchingMember.MemberType == MemberTypes.Property) { wireMember(view, matchingMember, method); } } catch (Exception ex) { continue; } } } } private void wireMember(IBaseView view, MemberInfo match, MethodInfo method) { var matchingMemberType = ((PropertyInfo)match).PropertyType; if (matchingMemberType == typeof(Button)) { var matchingButton = view.Get 

我已经在这里工作了。 它会将Presenter上的EventHandler自动assembly到IView上的控件的默认事件中。

另外,在旁注中,我想分享BindData方法。

  protected void BindData(Client client) { string nameOfPropertyBeingReferenced; nameOfPropertyBeingReferenced = MVP.Controller.GetPropertyName(() => client.Id); view.ClientId.BindTo(client, nameOfPropertyBeingReferenced); nameOfPropertyBeingReferenced = MVP.Controller.GetPropertyName(() => client.FullName); view.ClientName.BindTo(client, nameOfPropertyBeingReferenced); } public static void BindTo(this TextBox thisTextBox, object viewModelObject, string nameOfPropertyBeingReferenced) { Bind(viewModelObject, thisTextBox, nameOfPropertyBeingReferenced, "Text"); } private static void Bind(object sourceObject, Control destinationControl, string sourceObjectMember, string destinationControlMember) { Binding binding = new Binding(destinationControlMember, sourceObject, sourceObjectMember, true, DataSourceUpdateMode.OnPropertyChanged); //Binding binding = new Binding(sourceObjectMember, sourceObject, destinationControlMember); destinationControl.DataBindings.Clear(); destinationControl.DataBindings.Add(binding); } public static string GetPropertyName(Expression> exp) { return (((MemberExpression)(exp.Body)).Member).Name; } 

这消除了绑定中的“魔术字符串”。 我认为它也可以在INotificationPropertyChanged上使用。

无论如何,我希望有人发现它有用。 如果你想指出代码味道,我完全没问题。