我要模拟一个30*90的点阵显示。 打开
的点 (LED) 为红色,关闭
的点 (LED) 为黑色。
我玩弄了代码,创建了 2700 个椭圆,还使用了 this technique , 在任何情况下,该程序使用过多的 RAM(大约 80mb)并且存在性能问题(更新所有点时存在延迟)。
我的问题是:我可以用什么方式有效地处理这个问题?
最佳答案
下面的确切代码需要 C#6,但它使用的少数 C#6 内容可以用具有更烦人代码的较低版本来完成。在 Windows 7 x64 上使用 .NET 4.6.1 进行测试。
在大多数情况下,这对于 WPF 开发人员来说应该是非常标准的东西。绑定(bind)模式设置为我们使用的,以挤出更多。我们使用 PropertyChangedEventArgs
的单个实例,而不是您通常看到的“newing one up”,因为这会给 GC 增加更多不必要的压力,而您说 80 MB 是“很多”,所以这个已经在插入它了。
对棘手的内容进行内联评论,主要关注 XAML。
唯一有点“有趣”的是 ItemsControl
/Canvas
技巧。我想我是从 over here 捡到的.
此处驱动程序的早期编辑在每次更新时立即翻转所有点,但这并没有真正展示保留绑定(bind)时可以执行的操作。它还让转换器将 Color
值公开为 SolidColorBrush
对象的包装器(忘了我可以将它们公开为 Brush
属性,而 TypeConverter
将使它在 XAML 中设置起来同样容易)。
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);
}
}
}
}
点.cs
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
}
}
BooleanToBrushConverter.cs
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
}
}
关于c# - 如何高效画百点,C#/XAML,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36192457/