模型 - 视图 - 演示者模式是否允许WPF / Winforms中的嵌套演示者

时间:2014-09-06 22:31:33

标签: c# wpf winforms architecture mvp

我最近向SO和其他人建议我可以利用Model-View-Presenter模式来重构我在WPF和Winforms(主要是WPF)中构建的图/流程图设计器。模型如下所示。

enter image description here

我不明白这种模式如何在运行时添加到设计器表面的控件中起作用。这对我提出的问题如下:

  • 在运行时添加到设计器的Controls / Connections / Overlays / etc是否具有它们 拥有'演示者'或者他们应该共享演示者(很可能是DesignerPresenter)。
  • 如果Controls / Connections / Overlays / etc有自己的演示者,它们是否嵌套在DesignerPresenter中?
  • 当控件添加到Designer Canvas时,我触发了一个事件通知演示者。设计师应该在这里参考画布吗?我不知道怎么回事,因为我认为它负责将新组件添加到Canvas Control Childen。

我一直在玩一些虚拟的'演示者代码,我有以下内容:

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)
    }
}

2 个答案:

答案 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; }
    ...
}

然后,我会使用ItemsControlCanvas绘制此集合,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)

几个月前我刚写了类似的东西。它是带有插件的系统,用户可以选择插件并将它们添加到Canvas,然后调整大小,移动它们等等...我没有像你的那样有它们之间的连接,所以那部分我不能说话。

关于使用共享演示者或每个演示者的问题:它实际上取决于您的场景。你需要评估它是多么“沉重”。即:内存占用,CPU资源等。您还需要考虑线程。如果需要同时更新多个对象。确定您计划支持的最大对象数量,以及单个与多个演示者的对比情况。 (我选择了多个演示者,因为每个人都是一个自定义插件,插件的作者为它编写了视图和演示者,因此我在这方面没有太多选择。)

关于让ViewModel访问Canvas的问题:我讨厌说出来,但我确实让我的VM可以访问Canvas。我尝试了一下,但是除非我编写了自己的Canvas,接受了一个可以与ObservableCollection一起使用的ItemsSource控件,但是找不到一个干净的方法来避免它。最后,阻力最小的路径是让ViewModel可以访问Canvas。如果你的MVVM最纯净我肯定听起来很糟糕,但替代方案太耗时了。