Reputation: 922
I have to simulate a dot matrix display of 30*90. Dots (LEDs) that are on
are colored red and dots (LEDs) that are off
are colored black.
I played with the code, creating 2700 Ellipse, also using this technique, in any case the program uses too much RAM (about 80mb) and has performance issues (there is a delay when updating all points).
My question is: In what way can I deal whit this problem efficiently?
Upvotes: 3
Views: 547
Reputation: 5211
The exact code below requires C#6, but the few C#6 things it uses could be done the same with a lower version with more annoying code. Tested with .NET 4.6.1 on Windows 7 x64.
For the most part, this should be pretty standard stuff for a WPF developer. Binding modes are set to just what we use, to squeeze out a little more. We use a single instance of PropertyChangedEventArgs
instead of what you usually see "newing one up", because that would add more unnecessary pressure on the GC, and you said 80 MB is "a lot", so this is already pushing it.
Comments inline on the trickier stuff, mainly focusing on the XAML.
The only somewhat "interesting" thing is the ItemsControl
/ Canvas
trick. I think I picked this up from over here.
An earlier edit of the driver here had all dots being flipped at once every update, but that wasn't really showcasing the stuff you can do when you keep the bindings. It also had the converter expose Color
values as wrappers around SolidColorBrush
objects (forgot that I could just expose these as Brush
properties, and the TypeConverter
would make it just as easy to set in XAML).
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:me="clr-namespace:WpfApplication1"
Title="MainWindow"
DataContext="{Binding RelativeSource={RelativeSource Self}, Mode=OneTime}"
SizeToContent="WidthAndHeight">
<Window.Resources>
<!-- A value converter that we can use to convert a bool property
to one of these brushes we specify, depending on its value. -->
<me:BooleanToBrushConverter x:Key="converter"
TrueBrush="Red"
FalseBrush="Black" />
</Window.Resources>
<!-- The main "screen". Its items are the Dots, which we only need to read
once (the collection itself doesn't change), so we set Mode to OneTime. -->
<ItemsControl Width="300"
Height="900"
ItemsSource="{Binding Dots, Mode=OneTime}">
<!-- We use just a single Canvas to draw dots on. -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- On each presenter, set the Canvas.Left and Canvas.Top attached
properties to the X / Y values. Again, the dots themselves don't
move around after being initialized, so Mode can be OneTime. -->
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left"
Value="{Binding Path=XPos, Mode=OneTime}" />
<Setter Property="Canvas.Top"
Value="{Binding Path=YPos, Mode=OneTime}" />
</Style>
</ItemsControl.ItemContainerStyle>
<!-- Now, we just need to tell the ItemsControl how to draw each Dot.
Width and Height are 10 (this is the same 10 that we multiplied x and
y by when the Dots were created). The outline is always black.
As for Fill, we use IsOn to tell us which Brush to use. Since IsOn
is a bool, we use our converter to have it toggle the Brush. -->
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type me:Dot}">
<Ellipse Width="10"
Height="10"
Stroke="Black"
Fill="{Binding IsOn,
Mode=OneWay,
Converter={StaticResource converter}}" />
</DataTemplate>
</ItemsControl.Resources>
</ItemsControl>
</Window>
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : Window
{
public MainWindow()
{
// Initialize all the dots.
var dotSeq = from x in Enumerable.Range(0, 30)
from y in Enumerable.Range(0, 90)
select new Dot(x * 10, y * 10);
Dot[] allDots = dotSeq.ToArray();
this.Dots = new ReadOnlyCollection<Dot>(allDots);
// Start a dedicated background thread that picks a random dot,
// flips its state, and then waits a little while before repeating.
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += delegate { RandomlyToggleAllDots(allDots); };
bw.RunWorkerAsync();
this.InitializeComponent();
}
public ReadOnlyCollection<Dot> Dots { get; }
private static void RandomlyToggleAllDots(Dot[] allDots)
{
Random random = new Random();
while (true)
{
Dot dot = allDots[random.Next(allDots.Length)];
dot.IsOn = !dot.IsOn;
Thread.Sleep(1);
}
}
}
}
using System.ComponentModel;
namespace WpfApplication1
{
public sealed class Dot : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
internal Dot(double xPos, double yPos)
{
this.XPos = xPos;
this.YPos = yPos;
}
public double XPos { get; }
public double YPos { get; }
#region IsOn
// use a single event args instance
private static readonly PropertyChangedEventArgs IsOnArgs =
new PropertyChangedEventArgs(nameof(IsOn));
private bool isOn;
public bool IsOn
{
get
{
return this.isOn;
}
set
{
if (this.isOn == value)
{
return;
}
this.isOn = value;
this.PropertyChanged?.Invoke(this, IsOnArgs);
}
}
#endregion IsOn
}
}
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace WpfApplication1
{
[ValueConversion(typeof(bool), typeof(Brush))]
public sealed class BooleanToBrushConverter : IValueConverter
{
public Brush TrueBrush { get; set; }
public Brush FalseBrush { get; set; }
public object Convert(object value, Type _, object __, CultureInfo ___) =>
(bool)value ? this.TrueBrush : this.FalseBrush;
public object ConvertBack(object _, Type __, object ___, CultureInfo ____) =>
DependencyProperty.UnsetValue; // unused
}
}
Upvotes: 3
Reputation: 1475
You may want to consider using DrawingVisual class which is the most high performance way of drawing vector images in WPF - see MSDN link. This approach is most like using GDI+ in Winforms. Be aware that things like hit testing / automatic layout / binding etc aren't available at this level - which may or may not be important to you.
Upvotes: 1