Je n'ai rien trouvé d'utile sur Google ou Stack Overflow ou simplement pas de réponse (ou peut-être que je ne sais pas quoi chercher) - la question la plus proche que je peux obtenir Est-ce que c'est un: The reason behind slow performance in WPFWPF MouseMove InvalidateVisual OnRender mise à jour très lente
Mais je veux aller au fond de ce décalage dans ce programme simple, peut-être que je ne fais pas juste quelque chose de bien.
Je rends environ 2000 points avec des lignes entre eux dans le OnRender() d'un élément d'interface utilisateur, créant essentiellement un graphique linéaire. C'est bon, mais je veux faire un panoramique sur le graphique avec MouseMove. Cela fonctionne bien, mais c'est le LAG qui pose problème. Chaque fois que je glisse avec la souris, je m'attendrais à une mise à jour en douceur, je pense que redessiner 2000 points avec des lignes entre eux serait une promenade dans le parc pour un processeur i5. Mais c'est incroyablement lent, même à basse résolution sur mon ordinateur portable à la maison. J'ai donc vérifié le Performance Profiler. La fonction OnRender() utilise à peine n'importe quel CPU.
Il se trouve que c'est la mise en page qui change et en utilisant tant CPU.
« Mise en page » prend le plus de temps pour compléter
Maintenant, je l'ai entendu le terme Arbre visuel piétinant, mais il n'y a guère de supports visuels ce projet simple. Juste un élément d'interface utilisateur sur une fenêtre principale. Et en utilisant un contexte de dessin, j'aurais pensé que le contexte du dessin dessinait comme un bitmap, ou est-ce qu'il dessine des éléments de l'interface utilisateur avec leurs propres événements/boîtes à boutons, etc.? Parce que tout ce que je veux, c'est que l'UIElement agisse comme une image mais aussi manipule les événements de la souris pour que je puisse faire glisser le tout (ou zoomer avec la molette de la souris).
Alors Questions:
- Si la mise en page est à l'origine de la lenteur/lag, comment puis-je empêcher cela?
- Je remarque également beaucoup de garbage collection qui est logique, mais je ne veux pas que cela se produise pendant le rendu. Je préfère faire ça pendant que c'est inactif. mais comment?
Voici la source:
.cs fichier
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Media;
namespace SlowChart
{
public class SlowChartClass : UIElement
{
List<Point> points = new List<Point>();
double XAxis_Width = 2000;
double XAxis_LeftMost = 0;
double YAxis_Height = 300;
double YAxis_Lowest = -150;
Point mousePoint;
double XAxis_LeftMostPan = 0;
double YAxis_LowestPan = 0;
public SlowChartClass()
{
for (int i = 0; i < 2000; i++)
{
double cos = (float)Math.Cos(((double)i/100) * Math.PI * 2);
cos *= 100;
points.Add(new Point(i, cos));
}
MouseDown += SlowChartClass_MouseDown;
MouseUp += SlowChartClass_MouseUp;
MouseMove += SlowChartClass_MouseMove;
}
private void SlowChartClass_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
{
if (IsMouseCaptured)
{
XAxis_LeftMost = XAxis_LeftMostPan - (e.GetPosition(this).X - mousePoint.X);
YAxis_Lowest = YAxis_LowestPan + (e.GetPosition(this).Y - mousePoint.Y);
InvalidateVisual();
}
}
private void SlowChartClass_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
ReleaseMouseCapture();
}
private void SlowChartClass_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
mousePoint = e.GetPosition(this);
XAxis_LeftMostPan = XAxis_LeftMost;
YAxis_LowestPan = YAxis_Lowest;
CaptureMouse();
}
double translateYToScreen(double Y)
{
double y = RenderSize.Height - (RenderSize.Height * ((Y - YAxis_Lowest)/YAxis_Height));
return y;
}
double translateXToScreen(double X)
{
double x = (RenderSize.Width * ((X - XAxis_LeftMost)/XAxis_Width));
return x;
}
protected override void OnRender(DrawingContext drawingContext)
{
bool lastPointValid = false;
Point lastPoint = new Point();
Rect window = new Rect(RenderSize);
Pen pen = new Pen(Brushes.Black, 1);
// fill background
drawingContext.DrawRectangle(Brushes.White, null, window);
foreach (Point p in points)
{
Point screenPoint = new Point(translateXToScreen(p.X), translateYToScreen(p.Y));
if (lastPointValid)
{
// draw from last to this one
drawingContext.DrawLine(pen, lastPoint, screenPoint);
}
lastPoint = screenPoint;
lastPointValid = true;
}
// draw axis
drawingContext.DrawText(new FormattedText(XAxis_LeftMost.ToString("0.0") + "," + YAxis_Lowest.ToString("0.0"),CultureInfo.InvariantCulture,FlowDirection.LeftToRight,new Typeface("Arial"),12,Brushes.Black),new Point(0,RenderSize.Height-12));
}
}
}
fichier XAML
<Window x:Class="SlowChart.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SlowChart"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<local:SlowChartClass/>
</Grid>
</Window>
Mieux utiliser un TranslateTransform au lieu de tout re-rendre tout en panoramique. Vous pouvez également dessiner une polyligne au lieu de 2000 lignes. – Clemens
J'ai trouvé une solution plus pertinente dans cette question/réponse https://stackoverflow.com/questions/25450979/terrible-performance-of-custom-drawn-control (utilise Translate Transform). à votre santé – pm101