积极答复者
WPF 画 心电图

问题
-
从串口实时接收到的数据同步画出来,现在用的PathGeometry 动态往里添加LineSegment,但是感觉效率有点低,不希望使用第三方插件如 dynamicdatadisplay等,
有一个思路,就是重写onRender()方法,但是这会把窗体里的其他控件清掉,不知道该如何重写Canvas的onRender()方法。
请问如何像GDI+那样实时的画呢?难道WPF解决不了这样的问题吗?
- 已移动 ThankfulHeart 2013年8月18日 8:49 WPF问题
答案
-
终于明白啥意思了,多谢指教,可否能贴出完整代码,MinuteQuoteViewModel 这个没有。。。谢谢。
public class MinuteQuoteViewModel : INotifyPropertyChanged { private int ordinal; public int Ordinal { get { return this.ordinal; } set { if (this.ordinal != value) { this.ordinal = value; this.OnPropertyChanged("Ordinal"); } } } private DateTime quoteTime; public DateTime QuoteTime { get { return this.quoteTime; } set { if (this.quoteTime != value) { this.quoteTime = value; this.OnPropertyChanged("QuoteTime"); } } } private double lastPx = double.NaN; public double LastPx { get { return this.lastPx; } set { if (this.lastPx != value) { this.lastPx = value; this.OnPropertyChanged("LastPx"); } } } private double avgPx = double.NaN; public double AvgPx { get { return this.avgPx; } set { if (this.avgPx != value) { this.avgPx = value; this.OnPropertyChanged("AvgPx"); } } } private int volume; public int Volume { get { return this.volume; } set { if (this.volume != value) { this.volume = value; this.OnPropertyChanged("Volume"); } } } private double amount = double.NaN; public double Amount { get { return this.amount; } set { if (this.amount != value) { this.amount = value; this.OnPropertyChanged("Amount"); } } } #region INotifyPropertyChanged 成员 public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } #endregion }
- 已标记为答案 Leo (Apple) YangModerator 2014年2月28日 9:49
-
Hi waleswood,
欢迎来到MSDN中文论坛。
你可以尝试下面这个办法异步刷新WPF。
1 /// <summary> 2 /// Designates a Windows Presentation Foundation application model with added functionalities. 3 /// </summary> 4 public class UIHelper : Application 5 { 6 private static DispatcherOperationCallback exitFrameCallback = new DispatcherOperationCallback(ExitFrame); 7 8 /// <summary> 9 /// Processes all UI messages currently in the message queue. 10 /// </summary> 11 public static void DoEvents() 12 { 13 // Create new nested message pump. 14 DispatcherFrame nestedFrame = new DispatcherFrame(); 15 16 // Dispatch a callback to the current message queue, when getting called, 17 // this callback will end the nested message loop. 18 // note that the priority of this callback should be lower than the that of UI event messages. 19 DispatcherOperation exitOperation = Dispatcher.CurrentDispatcher.BeginInvoke( 20 DispatcherPriority.Background, exitFrameCallback, nestedFrame); 21 22 // pump the nested message loop, the nested message loop will 23 // immediately process the messages left inside the message queue. 24 Dispatcher.PushFrame(nestedFrame); 25 26 // If the "exitFrame" callback doesn't get finished, Abort it. 27 if (exitOperation.Status != DispatcherOperationStatus.Completed) 28 { 29 exitOperation.Abort(); 30 } 31 } 32 33 private static Object ExitFrame(Object state) 34 { 35 DispatcherFrame frame = state as DispatcherFrame; 36 // Exit the nested message loop. 37 if (frame != null) 38 { 39 frame.Continue = false; 40 } 41 return null; 42 } 43 44 }
Jason Wang
MSDN Community Support | Feedback to us
Develop and promote your apps in Windows Store
Please remember to mark the replies as answers if they help and unmark them if they provide no help.- 已标记为答案 Leo (Apple) YangModerator 2013年8月26日 9:41
-
用 GDI、GDI+或 DirectX2D 来画。
可以做成 ActiveX 控件,也可以做成 Winform 控件,反正方法很多,也可以直接绘制,比如绘制在 BMP 上,然后绑定到 UI。
方法很多,你可以上网搜下。对于这种高频数据,一定不能用 WPF 中的对象来做,或者说不能走 WPF 的 UI 渲染,需要程序自己来画。
- 已标记为答案 Leo (Apple) YangModerator 2013年8月26日 9:41
-
-
您好,GDI+的BMP对象不能在wpf控件下直接用吧,我程序的功能是描绘出由很多点组成的波形,然后实现拖动这个波形的功能,绑定的话是否合理?我试过DrawingVisual画,卡。用InKcanvas.strokes.add(new Stroke(XXX))的方法。拖动效果比 DrawingVisual好一点,还是会卡。但是每次clear()的时候感觉内存没有释放掉,运行一段时间之后内存都占满了。。纯粹用GDI+画的话cpu占用过高,还是会有一点闪,是不是我的数据量太大了,是心电图数据,每2毫秒一个点。这样大的数据量该如何处理才合理,还请不吝赐教~
1、我得先纠正你的错误观点,BMP 就是 BMP,没有什么 GDI+ 的和 WPF 的之分,只要是 BMP,就算是 C++ /JAVA 都能用;
2、你用过 WPF 的 Picture 控件吧,我将 1 秒钟的电影转换成 24 张 BMP 图片,然后在 1 秒之内连续、顺序的将这 24 张图片装入到 Picture 控件中,是不是就实现了播放 1 秒钟的电影片段?
3、GDI+ 比 GDI、DirectX 占用更多的资源,这是不争的事实,虽然你是 2 毫秒的一个点,但是图像的变化并不大,通过局部刷新,可以优化绘制和显式的性能。
4、我给你提供一段代码,使用 GDI+ 和 WriteableBitmap 的趋势线控件:
public class WriteableBitmapTrendLine : FrameworkElement { #region DependencyProperties public static readonly DependencyProperty LatestQuoteProperty = DependencyProperty.Register("LatestQuote", typeof(MinuteQuoteViewModel), typeof(WriteableBitmapTrendLine), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, OnLatestQuotePropertyChanged)); private static void OnLatestQuotePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { WriteableBitmapTrendLine trendLine = (WriteableBitmapTrendLine)d; MinuteQuoteViewModel latestQuote = (MinuteQuoteViewModel)e.NewValue; if (latestQuote != null) { trendLine.DrawTrendLine(latestQuote.Ordinal, (float)latestQuote.LastPx); } } public MinuteQuoteViewModel LatestQuote { get { return (MinuteQuoteViewModel)GetValue(LatestQuoteProperty); } set { SetValue(LatestQuoteProperty, value); } } #endregion private const int COLS = 723; private const int ROWS = 41; private WriteableBitmap bitmap; private float maxPrice = 0.0F; private static int dx = 3; private float[] prices = new float[COLS / dx]; public WriteableBitmapTrendLine() { this.bitmap = new WriteableBitmap(COLS, ROWS, 96, 96, PixelFormats.Rgb24, null); this.bitmap.Lock(); using (Bitmap backBufferBitmap = new Bitmap(COLS, ROWS, this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, this.bitmap.BackBuffer)) { using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap)) { backBufferGraphics.Clear(System.Drawing.Color.WhiteSmoke); backBufferGraphics.Flush(); } } this.bitmap.AddDirtyRect(new Int32Rect(0, 0, COLS, ROWS)); this.bitmap.Unlock(); } private void DrawTrendLine(int ordinal, float latestPrice) { if (double.IsNaN(latestPrice)) return; this.prices[ordinal] = latestPrice; bool redraw = false; if (ordinal == 0) { this.maxPrice = latestPrice; } else { if (latestPrice > this.maxPrice) { this.maxPrice = latestPrice; redraw = true; } } if (ordinal == 0) { int width = this.bitmap.PixelWidth; int height = this.bitmap.PixelHeight; this.bitmap.Lock(); using (Bitmap backBufferBitmap = new Bitmap(width, height, this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, this.bitmap.BackBuffer)) { using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap)) { backBufferGraphics.Clear(System.Drawing.Color.WhiteSmoke); backBufferGraphics.Flush(); } } this.bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height)); this.bitmap.Unlock(); } else { System.Drawing.Point[] points = new System.Drawing.Point[ordinal + 1]; float dy = (float)(ROWS / (this.maxPrice * 1.3)); for (int i = 0; i <= ordinal; i++) { points[i].X = i * dx; points[i].Y = (int)(this.prices[i] * dy); } int width = ordinal * dx + 1; int height = this.bitmap.PixelHeight; this.bitmap.Lock(); using (Bitmap backBufferBitmap = new Bitmap(width, height, this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, this.bitmap.BackBuffer)) { using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap)) { backBufferGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed; backBufferGraphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed; if (redraw) backBufferGraphics.Clear(System.Drawing.Color.WhiteSmoke); backBufferGraphics.DrawLines(System.Drawing.Pens.Green, points); backBufferGraphics.Flush(); } } this.bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height)); this.bitmap.Unlock(); } } private void DrawTrendLineF(int ordinal, float latestPrice) { if (double.IsNaN(latestPrice)) return; this.prices[ordinal] = latestPrice; if (ordinal == 0) { this.maxPrice = latestPrice; } else { if (latestPrice > this.maxPrice) { this.maxPrice = latestPrice; } } if (ordinal == 0) { int width = this.bitmap.PixelWidth; int height = this.bitmap.PixelHeight; this.bitmap.Lock(); using (Bitmap backBufferBitmap = new Bitmap(width, height, this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, this.bitmap.BackBuffer)) { using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap)) { backBufferGraphics.Clear(System.Drawing.Color.WhiteSmoke); backBufferGraphics.Flush(); } } this.bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height)); this.bitmap.Unlock(); } else { int count = this.prices.Length; PointF[] points = new PointF[ordinal + 1]; float dy = (float)(ROWS / this.maxPrice); for (int i = 0; i <= ordinal; i++) { points[i].X = i; points[i].Y = (float)Math.Floor(this.prices[i] * dy); if (float.IsNaN(points[i].Y)) points[i].Y = 0.0F; } int width = ordinal + 1; int height = this.bitmap.PixelHeight; this.bitmap.Lock(); using (Bitmap backBufferBitmap = new Bitmap(width, height, this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, this.bitmap.BackBuffer)) { using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap)) { backBufferGraphics.DrawLines(System.Drawing.Pens.Green, points); backBufferGraphics.Flush(); } } this.bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height)); this.bitmap.Unlock(); } } protected override void OnRender(DrawingContext drawingContext) { drawingContext.PushTransform(new ScaleTransform(1, -1, 0, RenderSize.Height / 2)); drawingContext.DrawImage(bitmap, new Rect(0, 0, RenderSize.Width, RenderSize.Height)); } }
- 已标记为答案 Leo (Apple) YangModerator 2014年2月28日 9:49
全部回复
-
Hi waleswood,
欢迎来到MSDN中文论坛。
你可以尝试下面这个办法异步刷新WPF。
1 /// <summary> 2 /// Designates a Windows Presentation Foundation application model with added functionalities. 3 /// </summary> 4 public class UIHelper : Application 5 { 6 private static DispatcherOperationCallback exitFrameCallback = new DispatcherOperationCallback(ExitFrame); 7 8 /// <summary> 9 /// Processes all UI messages currently in the message queue. 10 /// </summary> 11 public static void DoEvents() 12 { 13 // Create new nested message pump. 14 DispatcherFrame nestedFrame = new DispatcherFrame(); 15 16 // Dispatch a callback to the current message queue, when getting called, 17 // this callback will end the nested message loop. 18 // note that the priority of this callback should be lower than the that of UI event messages. 19 DispatcherOperation exitOperation = Dispatcher.CurrentDispatcher.BeginInvoke( 20 DispatcherPriority.Background, exitFrameCallback, nestedFrame); 21 22 // pump the nested message loop, the nested message loop will 23 // immediately process the messages left inside the message queue. 24 Dispatcher.PushFrame(nestedFrame); 25 26 // If the "exitFrame" callback doesn't get finished, Abort it. 27 if (exitOperation.Status != DispatcherOperationStatus.Completed) 28 { 29 exitOperation.Abort(); 30 } 31 } 32 33 private static Object ExitFrame(Object state) 34 { 35 DispatcherFrame frame = state as DispatcherFrame; 36 // Exit the nested message loop. 37 if (frame != null) 38 { 39 frame.Continue = false; 40 } 41 return null; 42 } 43 44 }
Jason Wang
MSDN Community Support | Feedback to us
Develop and promote your apps in Windows Store
Please remember to mark the replies as answers if they help and unmark them if they provide no help.- 已标记为答案 Leo (Apple) YangModerator 2013年8月26日 9:41
-
你好,你的意思是还是使用PathGeometry这个方法,然后使用异步刷新吗?
- 已标记为答案 Leo (Apple) YangModerator 2013年8月26日 9:41
- 取消答案标记 Leo (Apple) YangModerator 2013年8月26日 9:41
-
用 GDI、GDI+或 DirectX2D 来画。
可以做成 ActiveX 控件,也可以做成 Winform 控件,反正方法很多,也可以直接绘制,比如绘制在 BMP 上,然后绑定到 UI。
方法很多,你可以上网搜下。对于这种高频数据,一定不能用 WPF 中的对象来做,或者说不能走 WPF 的 UI 渲染,需要程序自己来画。
- 已标记为答案 Leo (Apple) YangModerator 2013年8月26日 9:41
-
-
您好,GDI+的BMP对象不能在wpf控件下直接用吧,我程序的功能是描绘出由很多点组成的波形,然后实现拖动这个波形的功能,绑定的话是否合理?我试过DrawingVisual画,卡。用InKcanvas.strokes.add(new Stroke(XXX))的方法。拖动效果比 DrawingVisual好一点,还是会卡。但是每次clear()的时候感觉内存没有释放掉,运行一段时间之后内存都占满了。。纯粹用GDI+画的话cpu占用过高,还是会有一点闪,是不是我的数据量太大了,是心电图数据,每2毫秒一个点。这样大的数据量该如何处理才合理,还请不吝赐教~
-
您好,GDI+的BMP对象不能在wpf控件下直接用吧,我程序的功能是描绘出由很多点组成的波形,然后实现拖动这个波形的功能,绑定的话是否合理?我试过DrawingVisual画,卡。用InKcanvas.strokes.add(new Stroke(XXX))的方法。拖动效果比 DrawingVisual好一点,还是会卡。但是每次clear()的时候感觉内存没有释放掉,运行一段时间之后内存都占满了。。纯粹用GDI+画的话cpu占用过高,还是会有一点闪,是不是我的数据量太大了,是心电图数据,每2毫秒一个点。这样大的数据量该如何处理才合理,还请不吝赐教~
1、我得先纠正你的错误观点,BMP 就是 BMP,没有什么 GDI+ 的和 WPF 的之分,只要是 BMP,就算是 C++ /JAVA 都能用;
2、你用过 WPF 的 Picture 控件吧,我将 1 秒钟的电影转换成 24 张 BMP 图片,然后在 1 秒之内连续、顺序的将这 24 张图片装入到 Picture 控件中,是不是就实现了播放 1 秒钟的电影片段?
3、GDI+ 比 GDI、DirectX 占用更多的资源,这是不争的事实,虽然你是 2 毫秒的一个点,但是图像的变化并不大,通过局部刷新,可以优化绘制和显式的性能。
4、我给你提供一段代码,使用 GDI+ 和 WriteableBitmap 的趋势线控件:
public class WriteableBitmapTrendLine : FrameworkElement { #region DependencyProperties public static readonly DependencyProperty LatestQuoteProperty = DependencyProperty.Register("LatestQuote", typeof(MinuteQuoteViewModel), typeof(WriteableBitmapTrendLine), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.None, OnLatestQuotePropertyChanged)); private static void OnLatestQuotePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { WriteableBitmapTrendLine trendLine = (WriteableBitmapTrendLine)d; MinuteQuoteViewModel latestQuote = (MinuteQuoteViewModel)e.NewValue; if (latestQuote != null) { trendLine.DrawTrendLine(latestQuote.Ordinal, (float)latestQuote.LastPx); } } public MinuteQuoteViewModel LatestQuote { get { return (MinuteQuoteViewModel)GetValue(LatestQuoteProperty); } set { SetValue(LatestQuoteProperty, value); } } #endregion private const int COLS = 723; private const int ROWS = 41; private WriteableBitmap bitmap; private float maxPrice = 0.0F; private static int dx = 3; private float[] prices = new float[COLS / dx]; public WriteableBitmapTrendLine() { this.bitmap = new WriteableBitmap(COLS, ROWS, 96, 96, PixelFormats.Rgb24, null); this.bitmap.Lock(); using (Bitmap backBufferBitmap = new Bitmap(COLS, ROWS, this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, this.bitmap.BackBuffer)) { using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap)) { backBufferGraphics.Clear(System.Drawing.Color.WhiteSmoke); backBufferGraphics.Flush(); } } this.bitmap.AddDirtyRect(new Int32Rect(0, 0, COLS, ROWS)); this.bitmap.Unlock(); } private void DrawTrendLine(int ordinal, float latestPrice) { if (double.IsNaN(latestPrice)) return; this.prices[ordinal] = latestPrice; bool redraw = false; if (ordinal == 0) { this.maxPrice = latestPrice; } else { if (latestPrice > this.maxPrice) { this.maxPrice = latestPrice; redraw = true; } } if (ordinal == 0) { int width = this.bitmap.PixelWidth; int height = this.bitmap.PixelHeight; this.bitmap.Lock(); using (Bitmap backBufferBitmap = new Bitmap(width, height, this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, this.bitmap.BackBuffer)) { using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap)) { backBufferGraphics.Clear(System.Drawing.Color.WhiteSmoke); backBufferGraphics.Flush(); } } this.bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height)); this.bitmap.Unlock(); } else { System.Drawing.Point[] points = new System.Drawing.Point[ordinal + 1]; float dy = (float)(ROWS / (this.maxPrice * 1.3)); for (int i = 0; i <= ordinal; i++) { points[i].X = i * dx; points[i].Y = (int)(this.prices[i] * dy); } int width = ordinal * dx + 1; int height = this.bitmap.PixelHeight; this.bitmap.Lock(); using (Bitmap backBufferBitmap = new Bitmap(width, height, this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, this.bitmap.BackBuffer)) { using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap)) { backBufferGraphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighSpeed; backBufferGraphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed; if (redraw) backBufferGraphics.Clear(System.Drawing.Color.WhiteSmoke); backBufferGraphics.DrawLines(System.Drawing.Pens.Green, points); backBufferGraphics.Flush(); } } this.bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height)); this.bitmap.Unlock(); } } private void DrawTrendLineF(int ordinal, float latestPrice) { if (double.IsNaN(latestPrice)) return; this.prices[ordinal] = latestPrice; if (ordinal == 0) { this.maxPrice = latestPrice; } else { if (latestPrice > this.maxPrice) { this.maxPrice = latestPrice; } } if (ordinal == 0) { int width = this.bitmap.PixelWidth; int height = this.bitmap.PixelHeight; this.bitmap.Lock(); using (Bitmap backBufferBitmap = new Bitmap(width, height, this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, this.bitmap.BackBuffer)) { using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap)) { backBufferGraphics.Clear(System.Drawing.Color.WhiteSmoke); backBufferGraphics.Flush(); } } this.bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height)); this.bitmap.Unlock(); } else { int count = this.prices.Length; PointF[] points = new PointF[ordinal + 1]; float dy = (float)(ROWS / this.maxPrice); for (int i = 0; i <= ordinal; i++) { points[i].X = i; points[i].Y = (float)Math.Floor(this.prices[i] * dy); if (float.IsNaN(points[i].Y)) points[i].Y = 0.0F; } int width = ordinal + 1; int height = this.bitmap.PixelHeight; this.bitmap.Lock(); using (Bitmap backBufferBitmap = new Bitmap(width, height, this.bitmap.BackBufferStride, System.Drawing.Imaging.PixelFormat.Format24bppRgb, this.bitmap.BackBuffer)) { using (Graphics backBufferGraphics = Graphics.FromImage(backBufferBitmap)) { backBufferGraphics.DrawLines(System.Drawing.Pens.Green, points); backBufferGraphics.Flush(); } } this.bitmap.AddDirtyRect(new Int32Rect(0, 0, width, height)); this.bitmap.Unlock(); } } protected override void OnRender(DrawingContext drawingContext) { drawingContext.PushTransform(new ScaleTransform(1, -1, 0, RenderSize.Height / 2)); drawingContext.DrawImage(bitmap, new Rect(0, 0, RenderSize.Width, RenderSize.Height)); } }
- 已标记为答案 Leo (Apple) YangModerator 2014年2月28日 9:49
-
终于明白啥意思了,多谢指教,可否能贴出完整代码,MinuteQuoteViewModel 这个没有。。。谢谢。
public class MinuteQuoteViewModel : INotifyPropertyChanged { private int ordinal; public int Ordinal { get { return this.ordinal; } set { if (this.ordinal != value) { this.ordinal = value; this.OnPropertyChanged("Ordinal"); } } } private DateTime quoteTime; public DateTime QuoteTime { get { return this.quoteTime; } set { if (this.quoteTime != value) { this.quoteTime = value; this.OnPropertyChanged("QuoteTime"); } } } private double lastPx = double.NaN; public double LastPx { get { return this.lastPx; } set { if (this.lastPx != value) { this.lastPx = value; this.OnPropertyChanged("LastPx"); } } } private double avgPx = double.NaN; public double AvgPx { get { return this.avgPx; } set { if (this.avgPx != value) { this.avgPx = value; this.OnPropertyChanged("AvgPx"); } } } private int volume; public int Volume { get { return this.volume; } set { if (this.volume != value) { this.volume = value; this.OnPropertyChanged("Volume"); } } } private double amount = double.NaN; public double Amount { get { return this.amount; } set { if (this.amount != value) { this.amount = value; this.OnPropertyChanged("Amount"); } } } #region INotifyPropertyChanged 成员 public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } #endregion }
- 已标记为答案 Leo (Apple) YangModerator 2014年2月28日 9:49