Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 用户控件中的WPF高效绘图_C#_Wpf_User Controls_Naudio - Fatal编程技术网

C# 用户控件中的WPF高效绘图

C# 用户控件中的WPF高效绘图,c#,wpf,user-controls,naudio,C#,Wpf,User Controls,Naudio,我在WPF中绘制usercontrol时遇到性能问题。这就是我要做的。 基本上,应用程序播放音频文件。播放音频文件的类每秒钟发送几次有关其位置的信息 我有一个Usercontrol来呈现文件的位置。我是这样做的: protected override void OnRender(DrawingContext drawingContext) { // lots of drawing happens here like drawingContext.DrawRect

我在WPF中绘制usercontrol时遇到性能问题。这就是我要做的。 基本上,应用程序播放音频文件。播放音频文件的类每秒钟发送几次有关其位置的信息

我有一个Usercontrol来呈现文件的位置。我是这样做的:

    protected override void OnRender(DrawingContext drawingContext)
    {

        // lots of drawing happens here like drawingContext.DrawRectangle,
        // drawingContext.DrawLine ....

        base.OnRender(drawingContext);
    }
    protected virtual void OnBytesPlayed(BytesPlayedEventArgs e)
    {

        if (playheadCallback != null)
            playheadCallback(fileStream.CurrentTime.TotalSeconds, fileStream.TotalTime.TotalSeconds);

    }
DrawingGroup backingStore = new DrawingGroup();

protected override void OnRender(DrawingContext drawingContext) {      
    base.OnRender(drawingContext);            

    Render(); // put content into our backingStore
    drawingContext.DrawDrawing(backingStore);
}

// I can call this anytime, and it'll update my visual drawing
// without ever triggering layout or OnRender()
private void Render() {            
    var drawingContext = backingStore.Open();
    Render(drawingContext);
    drawingContext.Close();            
}
现在,我在应用程序中有了另一个线程,它执行一些处理(基本上是读取音频数据),并通过委托每秒向usercontrol发送几次信息。第二个进程声明一个委托,如下所示:

    protected override void OnRender(DrawingContext drawingContext)
    {

        // lots of drawing happens here like drawingContext.DrawRectangle,
        // drawingContext.DrawLine ....

        base.OnRender(drawingContext);
    }
    protected virtual void OnBytesPlayed(BytesPlayedEventArgs e)
    {

        if (playheadCallback != null)
            playheadCallback(fileStream.CurrentTime.TotalSeconds, fileStream.TotalTime.TotalSeconds);

    }
DrawingGroup backingStore = new DrawingGroup();

protected override void OnRender(DrawingContext drawingContext) {      
    base.OnRender(drawingContext);            

    Render(); // put content into our backingStore
    drawingContext.DrawDrawing(backingStore);
}

// I can call this anytime, and it'll update my visual drawing
// without ever triggering layout or OnRender()
private void Render() {            
    var drawingContext = backingStore.Open();
    Render(drawingContext);
    drawingContext.Close();            
}
usercontrol已注册到该事件并将其自身重新绘制为:

    public void PlayheadPositionUpdate(double currentFrame, double allFrames)
    {

        Application.Current.Dispatcher.BeginInvoke(
           DispatcherPriority.Background,
           new System.Action(() =>
           {
               this.InvalidateVisual();
           }));
    }
这一切都是可行的,但是一旦我启用了回调,它每秒钟会绘制几次usercontrol,一切都会变得缓慢,性能就会下降。所以我想我处理绘图或重画的方式是没有效率的。有人能给我指出正确的方向吗?如何重新绘制usercontrol,使其不会影响播放性能或应用程序的整体性能

请让我知道,如果我需要解释更多或提供更多的代码。我希望尽可能简单地概括问题

多谢各位

编辑:

调用NAudio的代码是

            var inputStream = new AudioFileReader(filename);

            fileStream = inputStream;

            var aggregator = new SampleAggregator(inputStream);

            aggregator.NotificationCount = inputStream.WaveFormat.SampleRate;

            aggregator.BytesPLayed += (s, a) => OnBytesPlayed(a);

            playbackDevice.Init(aggregator);

您应该改用数据绑定和INotifyPropertyChanged,并让框架控制渲染。我有一个简单的例子来展示如何显示一个简单的图形。

图形控制.xaml

  <Border Height="50" Width="200" BorderBrush="Black" CornerRadius="1" BorderThickness="0.1">
    <Canvas x:Name="canvas" Background="White" Margin="0" ClipToBounds="True" />
  </Border>
  DataContext="{Binding RelativeSource={RelativeSource Self}}"
  <StackPanel >
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
  </StackPanel>

GraphControl.xaml.cs

  public partial class GraphControl : UserControl
  {
    public IEnumerable<double> Data
    {
      get { return (IEnumerable<double>)GetValue(DataProperty); }
      set { SetValue(DataProperty, value); }
    }
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(IEnumerable<double>), typeof(GraphControl), new PropertyMetadata(null, OnDataChanged));

    private static void OnDataChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
    {
      var control = (GraphControl)dependencyObject;
      control.HandleDataChanged();
    }

    public Brush Color
    {
      get { return (Brush)GetValue(ColorProperty); }
      set { SetValue(ColorProperty, value); }
    }
    public static readonly DependencyProperty ColorProperty =
        DependencyProperty.Register("Color", typeof(Brush), typeof(GraphControl), new PropertyMetadata(Brushes.Green));

    public double Maximum
    {
      get { return (double)GetValue(MaximumProperty); }
      set { SetValue(MaximumProperty, value); }
    }
    public static readonly DependencyProperty MaximumProperty =
        DependencyProperty.Register("Maximum", typeof(double), typeof(GraphControl), new PropertyMetadata(100.0));

    private void HandleDataChanged()
    {
      var points = new PointCollection();
      double x = 0;


      points.Add(new Point(0, canvas.ActualHeight));


      var xScale = canvas.ActualWidth / (Data.Count() -1);
      var yScale = canvas.ActualHeight / Maximum;
      foreach (var dataPoint in Data)
      {
        var y = canvas.ActualHeight - dataPoint * yScale;

        points.Add(new Point(x,y));
        x += xScale;

      }

      points.Add(new Point(canvas.ActualWidth, canvas.ActualHeight));

      var fill = Color.Clone();
      fill.Opacity = 0.2;
      polygon.Stroke = Color;
      polygon.Fill = fill;

      polygon.Points = points;

    }

    Polygon polygon = new Polygon() ;

    public GraphControl()
    {
      InitializeComponent();

      var fill = Color.Clone();
      fill.Opacity = 0.2;
      polygon.Stroke = Color;
      polygon.Fill = fill;

      polygon.StrokeThickness = 1;

      canvas.Children.Add(polygon);
    }
  }
private Random random = new Random();

public List<double> Data { get; set; }

public MainWindow()
{
  InitializeComponent();

  var temp = new List<double>();
  for (int i = 0; i < 100; i++)
  {
    temp.Add(random.NextDouble() * 100);
  }
  Data = new List<double>(temp);
  RaisePropertyChanged("Data");

  Timer timer = new Timer(1);
  timer.Elapsed += (sender, args) =>
  {
    UpdateData();
  };
  timer.Start();
}

private void UpdateData()
{
  var data = Data.ToArray();
  ShiftLeft(data, 1);


  var value = random.NextDouble() * 100;
  data[99] = value;

  Data = new List<double>(data); 

  RaisePropertyChanged("Data");
}

public void ShiftLeft<T>(T[] array, int shifts)
{
  Array.Copy(array, shifts, array, 0, array.Length - shifts);
  Array.Clear(array, array.Length - shifts, shifts);
}

private void RaisePropertyChanged(string property)
{
  if (PropertyChanged != null)
    PropertyChanged(this, new PropertyChangedEventArgs(property));
}

public event PropertyChangedEventHandler PropertyChanged;
公共部分类GraphControl:UserControl
{
公共IEnumerable数据
{
获取{return(IEnumerable)GetValue(DataProperty);}
set{SetValue(DataProperty,value);}
}
公共静态只读DependencyProperty DataProperty=
DependencyProperty.Register(“数据”、typeof(IEnumerable)、typeof(GraphControl)、new PropertyMetadata(null、OnDataChanged));
私有静态void OnDataChanged(DependencyObject DependencyObject,DependencyPropertyChangedEventArgs DependencyPropertyChangedEventArgs)
{
变量控制=(GraphControl)dependencyObject;
control.HandleDataChanged();
}
公共画笔颜色
{
获取{return(画笔)GetValue(ColorProperty);}
set{SetValue(ColorProperty,value);}
}
公共静态只读DependencyProperty ColorProperty=
DependencyProperty.Register(“颜色”、typeof(画笔)、typeof(图形控制)、新PropertyMetadata(画笔.绿色));
公共双倍最大值
{
获取{return(double)GetValue(MaximumProperty);}
set{SetValue(MaximumProperty,value);}
}
公共静态只读从属属性MaximumProperty=
DependencyProperty.Register(“最大值”、typeof(双精度)、typeof(图形控制)、新PropertyMetadata(100.0));
私有无效HandleDataChanged()
{
var points=new PointCollection();
双x=0;
添加(新点(0,canvas.ActualHeight));
var xScale=canvas.ActualWidth/(Data.Count()-1);
var yScale=画布实际高度/最大值;
foreach(数据中的var数据点)
{
变量y=canvas.ActualHeight-数据点*yScale;
点。添加(新点(x,y));
x+=xScale;
}
添加(新点(canvas.ActualWidth、canvas.ActualHeight));
var fill=Color.Clone();
填充。不透明度=0.2;
多边形。笔划=颜色;
多边形填充=填充;
多边形。点=点;
}
多边形=新多边形();
公共图形控制()
{
初始化组件();
var fill=Color.Clone();
填充。不透明度=0.2;
多边形。笔划=颜色;
多边形填充=填充;
polygon.StrokeThickness=1;
canvas.Children.Add(多边形);
}
}
为了测试。。。 MainWindows.xaml

  <Border Height="50" Width="200" BorderBrush="Black" CornerRadius="1" BorderThickness="0.1">
    <Canvas x:Name="canvas" Background="White" Margin="0" ClipToBounds="True" />
  </Border>
  DataContext="{Binding RelativeSource={RelativeSource Self}}"
  <StackPanel >
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
    <simpleGraph:GraphControl Data="{Binding Data}" Margin="4" />
  </StackPanel>
DataContext=“{Binding RelativeSource={RelativeSource Self}”
MainWindows.xaml.cs

  public partial class GraphControl : UserControl
  {
    public IEnumerable<double> Data
    {
      get { return (IEnumerable<double>)GetValue(DataProperty); }
      set { SetValue(DataProperty, value); }
    }
    public static readonly DependencyProperty DataProperty =
        DependencyProperty.Register("Data", typeof(IEnumerable<double>), typeof(GraphControl), new PropertyMetadata(null, OnDataChanged));

    private static void OnDataChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
    {
      var control = (GraphControl)dependencyObject;
      control.HandleDataChanged();
    }

    public Brush Color
    {
      get { return (Brush)GetValue(ColorProperty); }
      set { SetValue(ColorProperty, value); }
    }
    public static readonly DependencyProperty ColorProperty =
        DependencyProperty.Register("Color", typeof(Brush), typeof(GraphControl), new PropertyMetadata(Brushes.Green));

    public double Maximum
    {
      get { return (double)GetValue(MaximumProperty); }
      set { SetValue(MaximumProperty, value); }
    }
    public static readonly DependencyProperty MaximumProperty =
        DependencyProperty.Register("Maximum", typeof(double), typeof(GraphControl), new PropertyMetadata(100.0));

    private void HandleDataChanged()
    {
      var points = new PointCollection();
      double x = 0;


      points.Add(new Point(0, canvas.ActualHeight));


      var xScale = canvas.ActualWidth / (Data.Count() -1);
      var yScale = canvas.ActualHeight / Maximum;
      foreach (var dataPoint in Data)
      {
        var y = canvas.ActualHeight - dataPoint * yScale;

        points.Add(new Point(x,y));
        x += xScale;

      }

      points.Add(new Point(canvas.ActualWidth, canvas.ActualHeight));

      var fill = Color.Clone();
      fill.Opacity = 0.2;
      polygon.Stroke = Color;
      polygon.Fill = fill;

      polygon.Points = points;

    }

    Polygon polygon = new Polygon() ;

    public GraphControl()
    {
      InitializeComponent();

      var fill = Color.Clone();
      fill.Opacity = 0.2;
      polygon.Stroke = Color;
      polygon.Fill = fill;

      polygon.StrokeThickness = 1;

      canvas.Children.Add(polygon);
    }
  }
private Random random = new Random();

public List<double> Data { get; set; }

public MainWindow()
{
  InitializeComponent();

  var temp = new List<double>();
  for (int i = 0; i < 100; i++)
  {
    temp.Add(random.NextDouble() * 100);
  }
  Data = new List<double>(temp);
  RaisePropertyChanged("Data");

  Timer timer = new Timer(1);
  timer.Elapsed += (sender, args) =>
  {
    UpdateData();
  };
  timer.Start();
}

private void UpdateData()
{
  var data = Data.ToArray();
  ShiftLeft(data, 1);


  var value = random.NextDouble() * 100;
  data[99] = value;

  Data = new List<double>(data); 

  RaisePropertyChanged("Data");
}

public void ShiftLeft<T>(T[] array, int shifts)
{
  Array.Copy(array, shifts, array, 0, array.Length - shifts);
  Array.Clear(array, array.Length - shifts, shifts);
}

private void RaisePropertyChanged(string property)
{
  if (PropertyChanged != null)
    PropertyChanged(this, new PropertyChangedEventArgs(property));
}

public event PropertyChangedEventHandler PropertyChanged;
private Random=new Random();
公共列表数据{get;set;}
公共主窗口()
{
初始化组件();
var temp=新列表();
对于(int i=0;i<100;i++)
{
临时添加(random.NextDouble()*100);
}
数据=新列表(临时);
RaisePropertyChanged(“数据”);
定时器=新定时器(1);
计时器运行时间+=(发送方,参数)=>
{
更新数据();
};
timer.Start();
}
私有void UpdateData()
{
var data=data.ToArray();
ShiftLeft(数据,1);
var值=random.NextDouble()*100;
数据[99]=数值;
数据=新列表(数据);
RaisePropertyChanged(“数据”);
}
public void ShiftLeft(T[]数组,int移位)
{
复制(数组,移位,数组,0,数组。长度-移位);
Array.Clear(数组,数组,长度-移位,移位);
}
私有void RaisePropertyChanged(字符串属性)
{
if(PropertyChanged!=null)
PropertyChanged(此,新PropertyChangedEventArgs(property));
}
公共事件属性更改事件处理程序属性更改;

它非常粗糙,但希望它能有所帮助

您不想调用
InvalidateVisual()
,因为这会导致UI的重新布局非常缓慢。然而,您也不需要使用数据绑定(尽管它是一个很好的解决方案)

要使您尝试使用的方法快速,请创建一个DrawingGroup“backingStore”来保存您的绘图命令。在OnRender()方法中,将DrawingGroup输出到DrawingContext。然后,您可以随时更新backingStore,并更新您的视觉效果。将现有的渲染代码移动到另一个“Render()”方法中,该方法仅绘制到backingStore中

代码如下所示:

    protected override void OnRender(DrawingContext drawingContext)
    {

        // lots of drawing happens here like drawingContext.DrawRectangle,
        // drawingContext.DrawLine ....

        base.OnRender(drawingContext);
    }
    protected virtual void OnBytesPlayed(BytesPlayedEventArgs e)
    {

        if (playheadCallback != null)
            playheadCallback(fileStream.CurrentTime.TotalSeconds, fileStream.TotalTime.TotalSeconds);

    }
DrawingGroup backingStore = new DrawingGroup();

protected override void OnRender(DrawingContext drawingContext) {      
    base.OnRender(drawingContext);            

    Render(); // put content into our backingStore
    drawingContext.DrawDrawing(backingStore);
}

// I can call this anytime, and it'll update my visual drawing
// without ever triggering layout or OnRender()
private void Render() {            
    var drawingContext = backingStore.Open();
    Render(drawingContext);
    drawingContext.Close();            
}

你在哪个线程上播放音频?我正在使用一个名为naudio的库。不确定它是如何具体实现的,但假设是单独的线程,因为主屏幕的其余部分减去usercontrol可以正常反应。只有当我每秒重新绘制用户控件好几次时,问题才会出现。据我记忆所及,它确实与您的身份有关