【问题标题】:Should Model-View-Presenter Pattern Allow for Nested Presenters in WPF/Winforms模型-视图-演示者模式是否应该允许 WPF/Winforms 中的嵌套演示者
【发布时间】:2014-10-31 13:23:51
【问题描述】:

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

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

  • 在运行时添加到设计器的控件/连接/覆盖/等是否具有它们的 拥有“演示者”,或者他们应该共享一个演示者(很可能是 DesignerPresenter)。
  • 如果控件/连接/叠加/等有自己的演示者,它们是否嵌套在 DesignerPresenter 中?
  • 将控件添加到设计器画布时,我触发了一个通知演示者的事件。设计师应该在这里参考画布吗?我不明白它是怎么做到的,因为我认为它负责将新组件添加到 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)
    }
}

【问题讨论】:

  • 出于好奇,为什么在使用 WPF 时使用 MVP 而不是 MVVM?

标签: c# wpf winforms architecture mvp


【解决方案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>

可以在画布上添加或拖动组件,更改它们的 X、Y 值(如果允许,还可以更改高度/宽度),并且在这些属性的 OnPropertyChange 中,您可以找到任何关联的组件并更新它们位置和/或大小。

您的模型/视图模型根本不需要关心实际的 UI 组件,或者它们是如何被 UI 绘制的。他们只关心彼此之间的 X、Y 关系。

【讨论】:

    【解决方案2】:

    几个月前我刚刚写了类似的东西。这是一个带有插件的系统,用户可以选择插件并将它们添加到画布中,然后调整大小、移动它们等...... >

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

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

    【讨论】:

      猜你喜欢
      • 2011-06-15
      • 1970-01-01
      • 2010-10-05
      • 2015-07-23
      • 2011-04-18
      • 2011-02-25
      • 2020-01-22
      • 1970-01-01
      相关资源
      最近更新 更多