C# 如何以每秒60帧的速度在WPF中快速绘制文本或图示符?

C# 如何以每秒60帧的速度在WPF中快速绘制文本或图示符?,c#,wpf,C#,Wpf,我想在WPF中创建一个用户控件,它可以使用单空格字体绘制一个n x m字符矩阵。 控件将尽快接受字符串[](目标为60 fps),并在屏幕上绘制它 我需要类似的性能 所有字符都使用相同的单空格字体绘制,但根据某些规则可能具有不同的颜色和背景(类似于VS中的语法高亮显示) 我在C#WinForms中实现了这个解决方案,没有任何问题,速度达到了60 FPS,但是当我想在WPF中学习如何做到这一点时,我只找到了几篇文章和帖子,描述了WPF性能的问题和相互冲突的信息 那么,在这种情况下,实现最高性能的最

我想在WPF中创建一个用户控件,它可以使用单空格字体绘制一个n x m字符矩阵。 控件将尽快接受字符串[](目标为60 fps),并在屏幕上绘制它

我需要类似的性能

所有字符都使用相同的单空格字体绘制,但根据某些规则可能具有不同的颜色和背景(类似于VS中的语法高亮显示)

我在C#WinForms中实现了这个解决方案,没有任何问题,速度达到了60 FPS,但是当我想在WPF中学习如何做到这一点时,我只找到了几篇文章和帖子,描述了WPF性能的问题和相互冲突的信息

那么,在这种情况下,实现最高性能的最佳方法是什么

我尝试过的一种天真的方法是:

 /// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    Random rand = new Random();

    public MainWindow()
    {
        InitializeComponent();

        DispatcherTimer timer = new DispatcherTimer();
        timer.Interval = TimeSpan.FromMilliseconds(1);
        timer.Tick += timer_Tick;
        timer.Start();
    }

    string GenerateRandomString(int length)
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++)
        {
            sb.Append(rand.Next(10));
        }
        return sb.ToString();
    }
    void timer_Tick(object sender, EventArgs e)
    {
        myTextBlock.Inlines.Clear();

        for (int i = 0; i < 30; i++)
        {
            var run = new Run();
            run.Text = GenerateRandomString(800);
            run.Foreground = new SolidColorBrush(Color.FromArgb((byte)rand.Next(256),(byte)rand.Next(256),(byte)rand.Next(256),(byte)rand.Next(256)));
            run.Background = new SolidColorBrush(Color.FromArgb((byte)rand.Next(256),(byte)rand.Next(256),(byte)rand.Next(256),(byte)rand.Next(256)));
            myTextBlock.Inlines.Add(run);
        }

    }
}
//
///MainWindow.xaml的交互逻辑
/// 
公共部分类主窗口:窗口
{
Random rand=新的Random();
公共主窗口()
{
初始化组件();
调度程序计时器=新调度程序();
timer.Interval=TimeSpan.fromMillimes(1);
timer.Tick+=定时器_Tick;
timer.Start();
}
字符串生成器域字符串(整数长度)
{
StringBuilder sb=新的StringBuilder();
for(int i=0;i
问题是:你能在WPF中做得更好吗

附言。
是的,我可以直接使用DirectX,但这个问题是关于WPF而不是DX的。

在WPF中绘制文本的最快方法可能是使用

下面是一个示例代码:

main window.xaml

<Window x:Class="WpfApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="300" Width="300">
    <Image>
        <Image.Source>
            <DrawingImage x:Name="drawingImage"/>
        </Image.Source>
    </Image>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;

namespace WpfApplication
{
    public partial class MainWindow : Window
    {
        Random rand = new Random();

        Stopwatch stopwatch;
        long frameCounter = 0;

        GlyphTypeface glyphTypeface;
        double renderingEmSize, advanceWidth, advanceHeight;
        Point baselineOrigin;

        public MainWindow()
        {
            InitializeComponent();

            new Typeface("Consolas").TryGetGlyphTypeface(out this.glyphTypeface);
            this.renderingEmSize = 10;
            this.advanceWidth = this.glyphTypeface.AdvanceWidths[0] * this.renderingEmSize;
            this.advanceHeight = this.glyphTypeface.Height * this.renderingEmSize;
            this.baselineOrigin = new Point(0, this.glyphTypeface.Baseline * this.renderingEmSize);

            CompositionTarget.Rendering += CompositionTarget_Rendering;

            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(1000);
            timer.Tick += timer_Tick;
            timer.Start();
        }

        void CompositionTarget_Rendering(object sender, EventArgs e)
        {
            if (this.stopwatch == null)
                this.stopwatch = Stopwatch.StartNew();

            ++this.frameCounter;

            this.drawingImage.Drawing = this.Render();
        }

        string GenerateRandomString(int length)
        {
            var chars = new char[length];
            for (int i = 0; i < chars.Length; ++i)
                chars[i] = (char)rand.Next('A', 'Z' + 1);

            return new string(chars);
        }

        void timer_Tick(object sender, EventArgs e)
        {
            var seconds = this.stopwatch.Elapsed.TotalSeconds;
            Trace.WriteLine((long)(this.frameCounter / seconds));

            if (seconds > 10)
            {
                this.stopwatch.Restart();
                this.frameCounter = 0;
            }
        }

        private Drawing Render()
        {
            var lines = new string[30];
            for (int i = 0; i < lines.Length; ++i)
                lines[i] = GenerateRandomString(100);

            var drawing = new DrawingGroup();
            using (var drawingContext = drawing.Open())
            {
                // TODO: draw rectangles which represent background.

                // TODO: group of glyphs which has the same color should be drawn together.
                // Following code draws all glyphs in Red color.
                var glyphRun = ConvertTextLinesToGlyphRun(this.glyphTypeface, this.renderingEmSize, this.advanceWidth, this.advanceHeight, this.baselineOrigin, lines);
                drawingContext.DrawGlyphRun(Brushes.Red, glyphRun);
            }

            return drawing;
        }

        static GlyphRun ConvertTextLinesToGlyphRun(GlyphTypeface glyphTypeface, double renderingEmSize, double advanceWidth, double advanceHeight, Point baselineOrigin, string[] lines)
        {
            var glyphIndices = new List<ushort>();
            var advanceWidths = new List<double>();
            var glyphOffsets = new List<Point>();

            var y = baselineOrigin.Y;
            for (int i = 0; i < lines.Length; ++i)
            {
                var line = lines[i];

                var x = baselineOrigin.X;
                for (int j = 0; j < line.Length; ++j)
                {
                    var glyphIndex = glyphTypeface.CharacterToGlyphMap[line[j]];
                    glyphIndices.Add(glyphIndex);
                    advanceWidths.Add(0);
                    glyphOffsets.Add(new Point(x, y));

                    x += advanceWidth;

                }

                y += advanceHeight;
            }

            return new GlyphRun(
                glyphTypeface,
                0,
                false,
                renderingEmSize,
                glyphIndices,
                baselineOrigin,
                advanceWidths,
                glyphOffsets,
                null,
                null,
                null,
                null,
                null);
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用系统诊断;
使用System.Windows;
使用System.Windows.Media;
使用System.Windows.Threading;
命名空间WpfApplication
{
公共部分类主窗口:窗口
{
Random rand=新的Random();
秒表;
长帧计数器=0;
字形字体字形字体;
双渲染EMSIZE、advanceWidth、advanceHeight;
点基线原点;
公共主窗口()
{
初始化组件();
新字体(“Consolas”).TryGetGlyphTypeface(去掉这个.glyphTypeface);
此.renderingEmSize=10;
this.advanceWidth=this.glyphtype.AdvanceWidths[0]*this.renderingEmSize;
this.advanceHeight=this.glyphtype.Height*this.renderingEmSize;
this.baselineOrigin=新点(0,this.glyphtype.Baseline*this.renderingEmSize);
CompositionTarget.Rendering+=CompositionTarget\u Rendering;
调度程序计时器=新调度程序();
timer.Interval=TimeSpan.From毫秒(1000);
timer.Tick+=定时器_Tick;
timer.Start();
}
void CompositionTarget_呈现(对象发送方,事件参数e)
{
如果(this.stopwatch==null)
this.stopwatch=stopwatch.StartNew();
++这个.帧计数器;
this.drawingImage.Drawing=this.Render();
}
字符串生成器域字符串(整数长度)
{
var chars=新字符[长度];
对于(int i=0;i10)
{
这个.stopwatch.Restart();
this.frameCounter=0;
}
}
私人绘图渲染()
{
变量行=新字符串[30];
对于(int i=0;i