1

I want to write a block diagram design tool (something similar to Simulink or Modelica). I have done something like that already in C++/Qt and C#/WinForms/GDI+, but now I want to go WPF.

Take a block, which can be a rectangle or a square that contains several other smaller shapes as "input/output/bidirectional ports" probably labelled or not, some text and probably a bitmap or vector image. It should provide context menus, basic mouse or drag events (for moving the block, pulling the connections between blocks, etc.) and should allow manual rearrangement of its graphical constituents, maybe in a different editor.

Now imagine a diagram with say 1000 of such blocks (I am exaggerating a bit to allow enough headroom in the future) and corresponding connections. Given that scale, I wonder if I should fall back on to the visual level of WPF and model the interaction part manually or if it is sufficient to use Drawings, Shapes or even Controls for it (like a block being a button).

I am getting a little nervous when I see the ~50 event types a Button supplies and multiply this with the number of blocks times the average number of ports per block. Many elements will just point to the same event handlers or context menus, so these handlers could also be redirected to a management class.

I read through the respective WPF chapters in the book "Pro C# 5.0" and that actually did not allay my fears.

So what level of WPF (visual, drawing, shape, control) is advisable when it comes to speed and memory performance under these requirements?

Sidenote: I am just starting with WPF, so I am a bit stunned about its versatility. It makes strategic decisions a bit difficult for me, which is why I am asking before comprehensively researching.

oliver
  • 2,771
  • 15
  • 32
  • when you did this in C# and windows forms did you use controls or did you draw line by line all objects using GDI? I believe there are frameworks to do this so that you neither have to draw line by line nor you should use UI controls like buttons or panels... – Davide Piras Feb 22 '18 at 11:53
  • @Davide: in WinForms and Qt I actually did manual drawing and event handling. But it is tedious and so I was hoping that WPF's rich infrastructure combined with its hardware acceleration could save me a lot of development time for mechanisms that any other UI already has. Sure there are most probably such frameworks already, but my goal is also to learn WPF by doing. – oliver Feb 22 '18 at 12:01
  • Maybe I should just try generating 1000 Buttons and monitor speed & memory... – oliver Feb 22 '18 at 12:06
  • https://stackoverflow.com/a/15821573/643085 – Federico Berasategui Feb 22 '18 at 20:34

2 Answers2

2

You could try to create a custom layout with virtualization or use an existing one VirtualizationCanvas. You need a custom panel for placing your scheme items at correct places in your scheme.

Also, you should create a custom control, based on ItemsControl with custom ItemsControlItems for handling items creating and so on. Then apply your layout as ItemPanleTemplate for ItemsControl or ListView, where ItemsSource would be bounded to your viewModel with scheme collection.

That's the easiest option, as for me.

WinterMute
  • 131
  • 7
  • Looks interesting. However I admit that I hardly understand anything. I will definitely give it a more concentrated read before I start coding sincerely. – oliver Feb 22 '18 at 13:56
0

So now I have made a small feasibility study on the topic. Add 1800 Buttons to a Canvas, add two mouse events to each of them individually and further add a MouseWheel event to the window to allow basic zooming of the "scene". Observations are on my 10-year old Intel Core2Quad Q6600 + NVidia GTX 460 based machine.

Application starts up fast. Exe is very small (as expected), 8k. Resizing the window (to see more or less of the docked canvas' contents) feels quite snappy. However near fullscreen (all Buttons visible) redraw becomes a little heavy (like ~5Hz refresh rate). App grabs itself 20M of memory instead of 10M with only 1 Button. Zooming is fast enough. Moreover, when zoomed in, the rendering gets noticeably faster (so clipping and such works well). This is good because being in the closely zoomed-in state is the most frequent case when working with larger diagrams. Response to button events is unimpaired in every respect.

Conclusion: using Buttons for diagramming seems doable. It is certainly suboptimal as a graphics app gauged against the capabilities of my machine, but it is still fast enough for the job. Maybe using Shapes instead of Buttons gets a little more out of it. But definitely no case for falling back on low-level graphics.

<?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>

...and...

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);
    }
}
oliver
  • 2,771
  • 15
  • 32