询问者
WPF 绘制XY坐标

问题
全部回复
-
你是如何添加的?
每次都是一个吗?、
最好是可以有代码
有没有可能是VS的内存升高,误认为是程序了?
我这面写了个一次添加一个,随即删除的例子
未见内存会升高,基本稳定在43
public MainWindow() { InitializeComponent(); DispatcherTimer timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromMilliseconds(25); timer.Tick += Timer_Tick; timer.Start(); } private Random r = new Random(); private void Timer_Tick(object sender, EventArgs e) { c.Children.Clear(); SetEl(c, r.Next(0, (int)this.ActualWidth), r.Next(0, (int)this.ActualHeight)); } private void SetEl(Canvas F, int X, int Y) { Ellipse ellipse = new Ellipse { Height = 20, Width = 20 }; Canvas.SetLeft(ellipse, X); Canvas.SetTop(ellipse, Y); ellipse.Fill = new SolidColorBrush(Colors.Red); F.Children.Add(ellipse); }
-
大哥你好,感谢你的回复。
我现在把我的整个需求给你描述一下,希望能够得到你的一些指点,不胜感激。
1.从下位机每隔75ms,读取一帧数据。每帧数据包含64个点。我需要实时的将这个64个点绘制到XY坐标系上。
2.XY坐标系是我做的自定义控件,在这个控件内部定义了一个依赖属性Itemsource,每次itemsource变化都会调用一个callback,通过这个callback函数在坐标系上绘制出这64个点
3.这64个数据不仅需要显示在坐标系上,还需要通过表格的形式显示出来。于是我定义了一个observablecollection 集合。然后将这个集合binding 到一个datagridview上。想通过这种方法实时显示。
4.程序中,我定义了一个backgroundworker,在dowork事件里不停的采集下位机的数据。一旦采集完毕,就将这个数据reportprogress 到progresschanged 方法中显示(也就是上面描述的既要在坐标系上显示,又要做表格里显示)
5.显示部分我用秒表测了下,大概绘制完毕需要20ms左右的时间。
现在我的问题出现在:
1.我有两个版本这个程序,在采集和显示这部分的代码几乎相同。我的显示只有20ms,按理说progresschanged方法中的代码应该每隔75ms执行一次。但其中一个程序只要我加入表格显示,就发现进程内存会不停的增大。而且显示那块是每隔10几ms就会执行,长时间占用UI线程,导致界面有点卡顿。另外一个版本的程序,表格显示那块没问题,但是只要加入坐标系绘制点,progresschanged 方法每隔100多ms才运行一次,导致无法实时刷新。
//这个一个backworker,从下位机采集到的数据会存到一个队列里,然后这个负责从队列里不停的读取数据 private void Display_DoWork(object sender, DoWorkEventArgs e) { while (true) { if (!DispQueue.IsEmpty)//一旦队列中有数据就逐个显示 { General temp = new General(); bool success = DispQueue.TryDequeue(out temp); //temp=DataProcess.UpdateGeneralData(temp); if (success )//只显示数据,开始标志不显示 { if (temp.ID == 0x60A || temp.ID == 0x600) { DispFlagOccNum = DispFlagOccNum + 1; if (DispFlagOccNum == 1) { DispGeneralList.Clear(); } else { if (DispGeneralList.Count > 1) { List<General> datatemp = new List<General>(); for (int i = 0; i < DispGeneralList.Count; i++) { datatemp.Add(DispGeneralList[i]); } ; DispDataQueue.Enqueue(datatemp); } } else { DispGeneralList.Add(temp); } } } } }
//这是显示部分的代码 private void Display_ProgressChanged(object sender, ProgressChangedEventArgs e) { while(true) { if(Global.TotalDataFlag) { List<General> tempGeneralList = new List<General>(); bool success= DispDataQueue.TryDequeue(out tempGeneralList); if(success) { GeneralList.Clear(); ObservableCollection<PointDisp> tempPointList = new ObservableCollection<PointDisp>(); for (int i = 0; i < tempGeneralList.Count; i++) { PointDisp data = new PointDisp(); System.Windows.Point pointTemp = new System.Windows.Point(); pointTemp = DataProcess.PointDispProcess(tempGeneralList[i], 25); data.Position = pointTemp; data.ID = tempGeneralList[i].ID; tempPointList.Add(data); GeneralList.Add(tempGeneralList[i]); } pointDispList = tempPointList; Disp.ItemsSource = pointDispList; pointDispList.Clear(); } } } }
还有如果您还是不太明白需求,18005203228我的手机号,有偿咨询,不胜感激,谢谢
-
我这面照你的描述
1利用定时器 每75毫秒执行获取XY坐标点方法,其中使用线程等待10毫秒,模拟链接
2验证数据是否64个,以及创建两个集合,分别是坐标点和DataGrid数据表
3使用自定义事件模拟progresschanged 进行绘制坐标和数据表
编写代码中 第一次确实有随时间内存逐渐增高的情况。
是DataGrid的数据源问题。
修改后 程序运行10分钟左右
未见内存增高 在50~64之间徘徊
代码如下:
namespace Canvas_El { public class PointData { public double PointX { get; set; } public double PointY { get; set; } } /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DispatcherTimer timer = new DispatcherTimer(); timer.Interval = TimeSpan.FromMilliseconds(75); timer.Tick += Timer_Tick; timer.Start(); OnGetData += MainWindow_OnGetData; } private ObservableCollection<PointData> pointDatas = new ObservableCollection<PointData>(); private ObservableCollection<PointData> points; private Random r = new Random(); private event Action<int> OnGetData; private void MainWindow_OnGetData(int obj) { CD.Text = points.Count.ToString(); SD.Text = pointDatas.Count.ToString(); if (obj >= 63) { c.Children.Clear(); for (var item =0;item<points.Count;item++) { SetEl(c, points[item].PointX, points[item].PointY); } } } private void Timer_Tick(object sender, EventArgs e) { points = BackGroundWorker(); foreach(var item in points) pointDatas .Add(item); ShowData.ItemsSource = pointDatas; var pu = pointDatas.Count; for (var i = 0; i < pu / 2; i++) pointDatas.RemoveAt(i); OnGetData?.Invoke(points.Count); // c.Children.Clear(); // SetEl(c, r.Next(0, (int)this.ActualWidth), r.Next(0, (int)this.ActualHeight)); } private ObservableCollection<PointData> BackGroundWorker() { Thread.Sleep(10); ObservableCollection<PointData> list = new ObservableCollection<PointData>(); for (var i = 0; i < 64; i++) { PointData point = new PointData(); point.PointX = r.Next(0, (int)c.ActualWidth); point.PointY = r.Next(0, (int)c.ActualHeight); list.Add(point); } return list; } private void SetEl(Canvas F, double X, double Y) { Ellipse ellipse = new Ellipse { Height = 20, Width = 20 }; Canvas.SetLeft(ellipse, X); Canvas.SetTop(ellipse, Y); ellipse.Fill = new SolidColorBrush(Colors.Red); F.Children.Add(ellipse); } }
-
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using canlibCLSNET; using Kvaser.Kvadblib; namespace PointCollection { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { BackgroundWorker CanReceive = new BackgroundWorker(); BackgroundWorker Display = new BackgroundWorker(); Kvadblib.Hnd dbhandle;//dbc文件操作句柄 ConcurrentQueue<General> DispQueue = new ConcurrentQueue<General>();//用于显示采集到的object数据 int DispFlagOccNum = 0; ObservableCollection<General> DispGeneralList = new ObservableCollection<General>(); ObservableCollection<PointDisp> pointDispList = new ObservableCollection<PointDisp>(); ObservableCollection<General> GeneralList = new ObservableCollection<General>();//用于存储采集到的数据,binding 到datagrid显示控件 List<string> currenttimeB = new List<string>(); List<string> currenttimeA = new List<string>(); List<float> timeWork = new List<float>(); public MainWindow() { Canlib.canInitializeLibrary(); InitializeComponent(); CanReceive.DoWork += CanReceive_DoWork; CanReceive.WorkerReportsProgress = true; CanReceive.ProgressChanged += CanReceive_ProgressChanged; CanReceive.RunWorkerAsync(); Display.DoWork += Display_DoWork; Display.WorkerReportsProgress = true; Display.ProgressChanged += Display_ProgressChanged; Display.RunWorkerAsync(); string filename = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, @"Data\radar CAN20190627.dbc"); ;//dbc文件的路径 dbhandle = new Kvadblib.Hnd(); Kvadblib.Status dbstatus; dbstatus = Kvadblib.Open(out dbhandle); dbstatus = Kvadblib.ReadFile(dbhandle, filename); dgDetailData.ItemsSource = GeneralList; } private void Display_ProgressChanged(object sender, ProgressChangedEventArgs e) { try { //b = b + 1; //List<General> GeneralListTemp = new List<General>(); List<General> GeneralListTemp = e.UserState as List<General>; if (true) { string ct = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss fff"); currenttimeA.Add(ct); if (GeneralListTemp.Count != 0) { GeneralList.Clear(); Stopwatch watch = Stopwatch.StartNew();//创建一个监听器 for (int i = 0; i < GeneralListTemp.Count; i++) { GeneralList.Add(GeneralListTemp[i]); } watch.Stop(); float sec = watch.ElapsedMilliseconds; timeWork.Add(sec); //Disp.CreatePointandLine(pointDispList); pointDispList.Clear(); } else MessageBox.Show("Error"); } } catch (Exception ex) { MessageBox.Show(ex.ToString()); } } private void Display_DoWork(object sender, DoWorkEventArgs e) { while (true) { if (!DispQueue.IsEmpty)//一旦队列中有数据就逐个显示 { General temp = new General(); bool success = DispQueue.TryDequeue(out temp); if (success)//只显示数据,开始标志不显示 { if (temp.ID == 0x60A || temp.ID == 0x600) { DispFlagOccNum = DispFlagOccNum + 1; if (DispFlagOccNum == 1) { DispGeneralList.Clear(); } else { if (DispGeneralList.Count > 1) { List<General> datatemp = new List<General>(); for(var i=0;i<DispGeneralList.Count;i++) { datatemp.Add(DispGeneralList[i]); } //Stopwatch watch = Stopwatch.StartNew();//创建一个监听器 Display.ReportProgress(0, datatemp); string ct = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss fff"); currenttimeB.Add(ct); DispGeneralList.Clear(); //watch.Stop(); //float sec = watch.ElapsedMilliseconds; //timeWork.Add(sec); } } } else { DispGeneralList.Add(temp); } } } } } private void CanReceive_ProgressChanged(object sender, ProgressChangedEventArgs e) { throw new NotImplementedException(); } private void CanReceive_DoWork(object sender, DoWorkEventArgs e) { int channel = 0; int readHandle = Canlib.canOpenChannel(channel, Canlib.canOPEN_ACCEPT_VIRTUAL); Canlib.canStatus status = Canlib.canSetBusParams(readHandle, Canlib.BAUD_500K, 0, 0, 0, 0, 0); bool noError = true; status = Canlib.canBusOn(readHandle); int id; int dlc; int flags; long time; string msg; while ((status == Canlib.canStatus.canOK) && noError && readHandle >= 0) { try { byte[] receiveData = new byte[8]; status = Canlib.canReadWait(readHandle, out id, receiveData, out dlc, out flags, out time, 10000); if (status == Canlib.canStatus.canOK) { if (id == 0x60B || id == 0x60A || id == 0x701 || id == 0x600) { General General = new General(); if (id == 0x60A || id == 0x600) { General.ID = id; DispQueue.Enqueue(General); //string ct = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss fff"); //currenttimeA.Add(ct); } else { General = DumpGeneralMessage(id, receiveData, dlc, flags, time); try { //Stopwatch watch = Stopwatch.StartNew();//创建一个监听器 //CSVQueue.Enqueue(General); //watch.Stop(); //float sec = watch.ElapsedMilliseconds; DispQueue.Enqueue(General);//用于数据显示 } catch (Exception ex) { MessageBox.Show(ex.ToString()); } } } } } catch (Exception ex) { MessageBox.Show(ex.ToString()); System.Environment.Exit(0); } } Canlib.canBusOff(readHandle); MessageBox.Show("TimeOut"); } private General DumpGeneralMessage(int id, byte[] data, int dlc, int flags, long time) { Kvadblib.Status status; Kvadblib.MessageHnd mh = new Kvadblib.MessageHnd(); Kvadblib.SignalHnd sh = new Kvadblib.SignalHnd(); //Flips the EXT bit if the EXT flag is set if ((flags & Canlib.canMSG_EXT) != 0) { id ^= -2147483648; } //Find the database message whose id matches the one //from the incoming message status = Kvadblib.GetMsgById(dbhandle, id, out mh); //Console.WriteLine("Reading message with id " + id); //DisplayDBError(status, "Reading message with id " + id); //Print the message info General Data = new General(); if (status == Kvadblib.Status.OK) { string msgName; status = Kvadblib.GetMsgName(mh, out msgName); //Console.WriteLine("Message received: {0}", msgName); //StatusUpdate("", "Message received: " + msgName); int msgId; Kvadblib.MESSAGE msgFlags; status = Kvadblib.GetMsgId(mh, out msgId, out msgFlags); //Console.WriteLine("Id: {0}, flags: {1}", msgId, msgFlags); //StatusUpdate("", "Id: " + msgId + "flags: " + msgFlags); //Iterate through all the signals and print their name, value and unit status = Kvadblib.GetFirstSignal(mh, out sh); while (status == Kvadblib.Status.OK) { Data.TimeStamp= DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss fff"); //Data.TimeStamp = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss") + " " + DataProcess.ConvertLongToDateTime(time).ToString("fff"); string signalname; status = Kvadblib.GetSignalName(sh, out signalname); string unit; status = Kvadblib.GetSignalUnit(sh, out unit); double value; status = Kvadblib.GetSignalValueFloat(sh, out value, data, dlc); switch (signalname) { case "Object_ID_60B": { Data.ID = Convert.ToInt16(value); break; } case "Cluster_ID_701": { Data.ID = Convert.ToInt32(value); break; } case "Object_DistLog": { Data.DistLong = Convert.ToSingle(value); DataProcess.UpdateGeneralData(Data); break; } case "Cluster_DistLong": { Data.DistLong = Convert.ToSingle(value); DataProcess.UpdateGeneralData(Data); break; } case "Object_DistLat": { Data.DistLat = Convert.ToSingle(value); DataProcess.UpdateGeneralData(Data); break; } case "Cluster_DistLat": { Data.DistLat = Convert.ToSingle(value); DataProcess.UpdateGeneralData(Data); break; } case "Object_VrelLog": { Data.VrelLong = Convert.ToSingle(value); DataProcess.UpdateGeneralData(Data); break; } case "Cluster_VrelLong": { Data.VrelLong = Convert.ToSingle(value); DataProcess.UpdateGeneralData(Data); break; } case "Object_DynProp": { Data.DynProp = (DynProp)value; break; } case "Cluster_DynProp": { Data.DynProp = (DynProp)value; break; } case "Object_VrelLat": { Data.VrelLat = Convert.ToSingle(value); DataProcess.UpdateGeneralData(Data); break; } case "Cluster_VrelLat": { Data.VrelLat = Convert.ToSingle(value); DataProcess.UpdateGeneralData(Data); break; } case "Object_RCS": { Data.RCS = Convert.ToSingle(value); break; } case "Cluster_RCS": { Data.RCS = Convert.ToSingle(value); break; } } //Console.WriteLine("Signal - {0}: {1} {2}", signalname, value, unit); //StatusUpdate("", "Signal: " + signalname + " " + value+" "+unit); status = Kvadblib.GetNextSignal(mh, out sh); } return Data; } else { MessageBox.Show(status.ToString()); return null; } } public class PointDisp : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private int id; public int ID { get { return id; } set { id = value; if (this.PropertyChanged != null) { this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("ID")); } } } private Point position; public Point Position { get { return position; } set { position = value; if (this.PropertyChanged != null) { this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs("Position")); } } } } } }
代码中几个函数的作用:
1.CanReceive_DoWork:一直从下位机接受Can报文,将接受到的报文传入一个队列
2.Display_DoWork:从队列里取数据,然后处理,满足64个就通过reportprogress 传入到progresschanged中显示
3.Display_ProgressChanged:就是不停的将数据add到绑定的集合里,绑定之前要先将集合clear掉显示最新的数据(我刚刚试验,如果不clear,不停的添加,就不会出现内存增加和界面卡顿的现象,但是我解释不了其中的原因)
4.主要就是这三个函数,其他函数没什么关系。
谢谢
-
你要不要试试不要直接clear
和我一样使用remove这样的方式?
等待数据全部ADD
获取集合当前count
利用循环删除0-当前count/2所有数据,只保留当前加载的?
for (int i = 0; i < GeneralListTemp.Count; i++) { GeneralList.Add(GeneralListTemp[i]); } var ct = GeneralList.Count;
if(ct>64) for (var i = 0; i < ct / 2; i++) GeneralList.RemoveAt(i);
- 已编辑 ARM830 2019年8月1日 6:17
-
1
如果remove这个方法管用,就算是数量不一样也只是数学问题。
2
clear这个操作内部操作涉及很多,具体也可参考
3
UI线程有两个,一个渲染,一个管理,我觉你想表达是 管理这个线程,这个是只有一个。
具体参考WPF线程模型
4
如果觉得progresschanged方法有卡,可以尝试使用UI线程的异步方法。
或者将DataGrid改成其他控件ListBox,ListView都可以,轻微改动Style/ItemTemplate就可以成为一个轻型DataGrid表格,
Litsbox和LitsView内部代码,原理与DataGrid更简单,方便