[英]Should Model-View-Presenter Pattern Allow for Nested Presenters in WPF/Winforms
最近有人在SO和其他工具上向我建议,我可以利用Model-View-Presenter模式来重构我在WPF和Winforms(主要是WPF)中构建的图/流程图设计器。模型如下所示。
我不明白的是,这种模式如何与在运行时将控件添加到设计器图面中一起使用。 这为我提出了以下问题:
我一直在为此使用一些“虚拟”演示者代码,并且具有以下内容:
public interface IDesignerView : IView
{
Guid Id { get; set; }
Canvas Canvas { get; set; }
event EventHandler<MouseEventArgs> MouseDown;
event EventHandler<MouseEventArgs> ControlDropped;
}
public interface IControlView : IView
{
Guid Id { get; set; }
event EventHandler<MouseEventArgs> MouseDown;
}
public class DesignerView : IDesignerView
{
public Guid Id { get; set; }
public Canvas Canvas { get; set; }
public event EventHandler<MouseEventArgs> MouseDown;
public event EventHandler<MouseEventArgs> ControlDropped;
}
public class DesignerPresenter :Presenter<IDesignerView>
{
public DesignerPresenter(IDesignerView view) : base(view)
{
}
public override void Initialize()
{
View.ControlDropped += View_ControlDropped;
View.MouseDown += View_MouseDown;
}
private void View_MouseDown(object sender, MouseEventArgs e)
{
//Might need to unselect selected controls
}
private void View_ControlDropped(object sender, MouseEventArgs e)
{
IControlView view = ControlBuilder.Build(...)
View.Canvas.Children.Add(view)
}
}
我使用MVVM做过类似的事情,在这里使用它看不到问题。 我对MVP的了解还不够,无法谈论它。
(此外,我查看了您的其他问题 ,但由于您使用的是WPF,因此并没有真正看到您为什么要在MVVM上使用MVP这样的功能)
理想情况下,画布上的每个项目(“组件”,“覆盖”和“连接器”)都将由一个数据模型来表示,该数据模型应包含属性,该属性包含对象的大小和位置
public interface IDesignerComponent
{
int X { get; set; }
int Y { get; set; }
int Height { get; set; }
int Width { get; set; }
}
public class ComponentModel : IDesignerComponent { ... }
public class ConnectorModel: IDesignerComponent { ... }
public class OverlayModel: IDesignerComponent { ... }
您将拥有这些对象的集合以供UI绑定到设计器视图模型中
public class DesignerViewModel
{
public ObservableCollection<IDesignerComponent> Components { get; set; }
...
}
然后,我将使用ItemsControl
绘制此集合,该ItemsControl
具有一个用于ItemsPanelTemplate
的Canvas
,并使用隐式DataTemplates
定义如何绘制每个项目。
<ItemsControl ItemsSource="{Binding Components}">
<!-- // DataTemplates for all 3 types of objects -->
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:ComponentModel}">
<local:MyComponentControl
Height="{Binding Height}"
Width="{Binding Width}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ConnectorModel}">
<local:MyConnectorControl
Height="{Binding Height}"
Width="{Binding Width}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:OverlayModel}">
<local:MyOverlayControl
Height="{Binding Height}"
Width="{Binding Width}" />
</DataTemplate>
</ItemsControl.Resources>
<!-- // ItemsPanelTemplate - May need to set or bind Canvas Height/Width too-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- // ItemContainerStyle - Sets x,y position of items -->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
可以在画布上添加或拖动组件,更改其X,Y值(如果允许,还可以更改其高度/宽度),在这些属性的OnPropertyChange
,您可以找到任何关联的组件并更新其位置和/或大小也一样
您的Models / ViewModels根本不需要关心实际的UI组件,也不用担心它们如何被UI绘制。 他们只关心彼此的X,Y关系。
我几个月前刚刚写了类似的东西。 它是带有插件的系统,用户可以选择插件并将其添加到“画布”,然后调整大小,移动它们等...我之间没有像您一样的连接,因此我无法与之交谈。
关于使用共享演示者还是每个演示者的问题:这实际上取决于您的方案。 您需要评估它的“重量”。 即:内存占用量,CPU资源等。另外,您还需要考虑线程。 如果多个对象需要同时更新。 确定您计划支持的最大对象数是多少,以及对单个或多个演示者的处理方式。 (我和多个演示者一起去了,因为每个演示者都是一个自定义插件,并且插件的作者为此编写了视图和演示者,所以在这方面我没有太多选择。)
关于让ViewModel可以访问Canvas的问题:我不想这么说,但是我确实让我的VM可以访问Canvas。 我尝试了一下,但是除非我编写自己的Canvas接受可以与ObservableCollection一起使用的控件的ItemsSource,否则找不到避免它的好方法。 最后,阻力最小的路径是让ViewModel可以访问Canvas。 如果您的MVVM最纯净,我相信这听起来很糟糕,但是替代方法却很费时。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.