我最近向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)
}
}
答案 0 :(得分:1)
我已经使用MVVM做了类似的事情,并且在这里使用它时没有看到问题。虽然我对MVP的了解不够充分。
(另外,我看了你的other question,并且真的不明白为什么你想要使用MVP而不是MVVM这样的事情,因为你正在使用WPF)
理想情况下,画布上的每个项目(组件,叠加层和连接器)都将由包含包含boejct大小和位置的属性的数据模型表示
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
来Canvas
绘制此集合,ItemsPanelTemplate
使用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>
来定义每个项目的绘制方式。< / p>
OnPropertyChange
可以在画布上添加或拖动组件,更改其X,Y值(如果允许,可以更改高度/宽度),在{{1}}中可以找到任何关联的组件和更新他们的位置和/或大小。
您的模型/ ViewModel根本不需要关心实际的UI组件,或者UI如何绘制它们。他们所关心的只是他们彼此的X,Y关系。
答案 1 :(得分:0)
关于使用共享演示者或每个演示者的问题:它实际上取决于您的场景。你需要评估它是多么“沉重”。即:内存占用,CPU资源等。您还需要考虑线程。如果需要同时更新多个对象。确定您计划支持的最大对象数量,以及单个与多个演示者的对比情况。 (我选择了多个演示者,因为每个人都是一个自定义插件,插件的作者为它编写了视图和演示者,因此我在这方面没有太多选择。)
关于让ViewModel访问Canvas的问题:我讨厌说出来,但我确实让我的VM可以访问Canvas。我尝试了一下,但是除非我编写了自己的Canvas,接受了一个可以与ObservableCollection一起使用的ItemsSource控件,但是找不到一个干净的方法来避免它。最后,阻力最小的路径是让ViewModel可以访问Canvas。如果你的MVVM最纯净我肯定听起来很糟糕,但替代方案太耗时了。