c# - 如何高效画百点,C#/XAML

标签 c# wpf xaml

我要模拟一个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/

相关文章:

java - 从 C# 中的 List 获取对象变量

c# - 在 LINQ 中合并记录

c# - TcpListener.AcceptSocket()在Windows服务中失败

wpf - GroupStyles 是如何工作的?

wpf - 如何在控件内引用用户控件主机的 StaticResource?

c# - 为什么我们应该避免公共(public)方法?封装的好处

c# - Datagrid 我无法刷新字段

c# - C# 开发人员在使用 WPF 之前应该了解什么

c# - WPF Databinding DataTrigger 根据 bool 值改变形状的颜色

c# - Image ComboBox XAML 在运行时无法正确显示