在WPF中建模块图的最佳方法

时间:2018-02-22 11:20:22

标签: c# wpf

我想编写一个方框图设计工具(类似于Simulink或Modelica)。我已经在C ++ / Qt和C#/ WinForms / GDI +中做过类似的事情,但现在我想去WPF。

取一个块,可以是一个矩形或一个包含其他几个较小形状的正方形作为"输入/输出/双向端口"可能标记或不标记,一些文本,可能是位图或矢量图像。它应该提供上下文菜单,基本鼠标或拖动事件(用于移动块,拉动块之间的连接等),并且应该允许手动重新排列其图形组件,可能在不同的编辑器中。

现在想象一个图表,其中包含1000个这样的块(我夸大了一点以便将来留有足够的空间)和相应的连接。鉴于这个比例,我想知道我是否应该回到WPF的视觉级别并手动建模交互部分,或者是否足以使用图纸,形状甚至控件(就像一个块作为一个按钮)。

当我看到按钮提供的~50种事件类型并将其乘以块数乘以每块的平均端口数时,我有点紧张。许多元素只指向相同的事件处理程序或上下文菜单,因此这些处理程序也可以重定向到管理类。

我仔细阅读了书中的各个WPF章节" Pro C#5.0"而这实际上并没有减轻我的担忧。

那么在这些要求下速度和内存性能方面,建议使用什么级别的WPF(视觉,绘图,形状,控制)?

旁注:我刚刚开始使用WPF,所以我对它的多功能性感到有些震惊。它使我的战略决策变得有点困难,这就是我在全面研究之前提出的要求。

2 个答案:

答案 0 :(得分:1)

您可以尝试使用虚拟化创建自定义布局或使用现有布局VirtualizationCanvas。 您需要一个自定义面板,用于将方案项目放置在方案中的正确位置。

此外,您应该创建一个自定义控件,基于具有自定义ItemsControlItems的ItemsControl来处理项目创建等。 然后将您的布局应用于ItemsControl或ListView的ItemPanleTemplate,其中ItemsSource将与带有方案集合的viewModel绑定。

对我而言,这是最简单的选择。

答案 1 :(得分:0)

所以现在我对这个话题做了一个小的可行性研究。向画布添加1800个按钮,分别向它们添加两个鼠标事件,并进一步向窗口添加MouseWheel事件,以允许基本缩放"场景"。我的10年历史的基于英特尔Core2Quad Q6600 + NVidia GTX 460的机器都有观察结果。

应用程序快速启动。 Exe非常小(如预期的那样),8k。调整窗口大小(以查看更多或更少的停靠画布和内容)感觉非常活泼。然而,接近全屏(所有按钮可见)重绘变得有点沉重(如〜5Hz刷新率)。应用程序只需1个按钮即可获取20M内存,而不是10M内存。缩放足够快。此外,在放大时,渲染速度明显加快(因此裁剪等效果很好)。这很好,因为处于紧密放大状态是处理较大图表时最常见的情况。对按钮事件的响应在各个方面都没有受到影响。

结论:使用按钮进行图表显示似乎可行。当图形应用程序针对我的机器的功能进行测量时,它当然不是最理想的,但它仍然足够快。也许使用Shapes代替Buttons可以从中获得更多。但绝对没有回到低级别图形的情况。

<?xml version="1.0" encoding="utf-8"?>
<Window
    x:Class="wpf1.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="wpf1"
    Width="513"
    Height="385"
    x:Name="window1"
    MouseWheel="window1_MouseWheel">
    <Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" x:Name="canvas1" Background="#FFFFE8E8"/>
</Window>

...和...

public partial class Window1 : Window
{
    double totalScale = 1;

    public Window1()
    {
        InitializeComponent();

        for (int i=0; i<60; i++)
        {
            for (int j=0; j<30; j++)
            {
                Button newButton = new Button();
                canvas1.Children.Add(newButton);
                newButton.Width = 25;
                newButton.Height = 25;
                Canvas.SetTop(newButton, j*30);
                Canvas.SetLeft(newButton, i*30);
                newButton.Click += new System.Windows.RoutedEventHandler(button1_Click);
                newButton.MouseMove += new System.Windows.Input.MouseEventHandler(button1_MouseMove);
            }   
        }
    }

    Button lastButton;

    void button1_Click(object sender, RoutedEventArgs e)
    {
        lastButton = sender as Button;
        lastButton.Background = Brushes.Blue;
    }

    void button1_MouseMove(object sender, MouseEventArgs e)
    {
        Button butt = sender as Button;
        if (lastButton != butt) butt.Background = Brushes.Yellow;
    }

    void window1_MouseWheel(object sender, MouseWheelEventArgs e)
    {
        double scaleFactor = Math.Pow(1.0005,e.Delta);
        totalScale *= scaleFactor;
        canvas1.LayoutTransform = new ScaleTransform(totalScale, totalScale);
    }
}