Reputation: 9527
Here's a Windows Forms program which draws a two-dimensional grid of squares that are randomly colored black or red:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace Forms_Panel_Random_Squares
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Width = 350;
Height = 350;
var panel = new Panel() { Dock = DockStyle.Fill };
Controls.Add(panel);
var random = new Random();
panel.Paint += (sender, e) =>
{
e.Graphics.Clear(Color.Black);
for (int i = 0; i < 30; i++)
for (int j = 0; j < 30; j++)
{
if (random.Next(2) == 1)
e.Graphics.FillRectangle(
new SolidBrush(Color.Red),
i * 10,
j * 10,
10,
10);
}
};
}
}
}
The resulting program looks something like this:
Here's a (naive) translation to WPF using Rectangle
objects for each square:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
namespace WPF_Canvas_Random_Squares
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Width = 350;
Height = 350;
var canvas = new Canvas();
Content = canvas;
Random random = new Random();
Rectangle[,] rectangles = new Rectangle[30, 30];
for (int i = 0; i < rectangles.GetLength(0); i++)
for (int j = 0; j < rectangles.GetLength(1); j++)
{
rectangles[i, j] =
new Rectangle()
{
Width = 10,
Height = 10,
Fill = random.Next(2) == 0 ? Brushes.Black : Brushes.Red,
RenderTransform = new TranslateTransform(i * 10, j * 10)
};
canvas.Children.Add(rectangles[i, j]);
}
}
}
}
The WPF version seems to be way more memory inefficient due to the fact that each cell in the world has the overhead of a Rectangle
object.
Is there a way to write this program in a style that's as efficient as the Forms version? Or is there no way around creating all those Rectangle
objects?
Upvotes: 2
Views: 2453
Reputation: 9527
Here's a pure WPF solution. FrameworkElement
is subclassed. This new subclass (DrawingVisualElement
) exposes a DrawingVisual
object which can be used to draw.
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace DrawingVisualSample
{
public class DrawingVisualElement : FrameworkElement
{
private VisualCollection _children;
public DrawingVisual drawingVisual;
public DrawingVisualElement()
{
_children = new VisualCollection(this);
drawingVisual = new DrawingVisual();
_children.Add(drawingVisual);
}
protected override int VisualChildrenCount
{
get { return _children.Count; }
}
protected override Visual GetVisualChild(int index)
{
if (index < 0 || index >= _children.Count)
throw new ArgumentOutOfRangeException();
return _children[index];
}
}
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Width = 350;
Height = 350;
var stackPanel = new StackPanel();
Content = stackPanel;
var drawingVisualElement = new DrawingVisualElement();
stackPanel.Children.Add(drawingVisualElement);
var drawingContext = drawingVisualElement.drawingVisual.RenderOpen();
var random = new Random();
for (int i = 0; i < 30; i++)
for (int j = 0; j < 30; j++)
drawingContext.DrawRectangle(
random.Next(2) == 0 ? Brushes.Black : Brushes.Red,
(Pen)null,
new Rect(i * 10, j * 10, 10, 10));
drawingContext.Close();
}
}
}
Upvotes: 2
Reputation: 9527
It is possible to mix WPF and Forms. So instead of going the pure Forms route, the Panel can be embedded into the WPF Window via WindowsFormsHost
. Here's a WPF program which demonstrates this:
using System;
using System.Windows;
using System.Windows.Forms.Integration;
using System.Drawing;
namespace WindowsFormsHost_Random_Squares
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Width = 350;
Height = 350;
Random random = new Random();
var windowsFormsHost = new WindowsFormsHost();
Content = windowsFormsHost;
var panel = new System.Windows.Forms.Panel()
{ Dock = System.Windows.Forms.DockStyle.Fill };
windowsFormsHost.Child = panel;
panel.Paint += (sender, e) =>
{
e.Graphics.Clear(System.Drawing.Color.Black);
for (int i = 0; i < 30; i++)
for (int j = 0; j < 30; j++)
{
if (random.Next(2) == 1)
e.Graphics.FillRectangle(
new SolidBrush(System.Drawing.Color.Red),
i * 10,
j * 10,
10,
10);
}
};
}
}
}
Upvotes: 0