我必须模拟30 * 90的点阵显示。 on
的点(LED)为红色,off
的点(LED)为黑色。
我玩代码,创建2700 Ellipse,也使用this technique,无论如何程序使用太多RAM(大约80mb)并且存在性能问题(更新所有点时都有延迟)。 / p>
我的问题是:我能以何种方式有效处理此问题?
答案 0 :(得分:2)
下面的确切代码需要C#6,但它使用的少数C#6内容可以使用更低版本和更烦人的代码完成相同的操作。在Windows 7 x64上使用.NET 4.6.1进行测试。
在大多数情况下,对于WPF开发人员来说,这应该是非常标准的东西。绑定模式设置为我们使用的,以挤出更多。我们使用PropertyChangedEventArgs
的单个实例代替您通常看到的"新增一个",因为这会给GC增加更多不必要的压力,而您说80 MB是" a很多",所以这已经推动了它。
评论内容较为棘手,主要关注XAML。
唯一有点"有趣"事情是ItemsControl
/ Canvas
伎俩。我想我是从over here中选择的。
此处的驱动程序的早期编辑在每次更新时都会立即翻转所有点,但这并没有真正展示您在保留绑定时可以执行的操作。它还有转换器公开Color
值作为SolidColorBrush
对象周围的包装(忘了我可以将它们公开为Brush
属性,而TypeConverter
会让它变得如此简单在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
}
}
答案 1 :(得分:1)
您可能需要考虑使用DrawingVisual类,这是在WPF中绘制矢量图像的最高性能方法 - 请参阅MSDN link。这种方法最像是在Winforms中使用GDI +。请注意,此级别不提供命中测试/自动布局/绑定等功能 - 这对您来说可能重要,也可能不重要。