How expensive is using a System.Action? - c#

I am in a situation where I rely on a System.Action to clean up my UI when a method finishes running. Because this method can be called from different points in the application the cleanup actions may be vastly different.
I'm worried they may be expensive, and that I should find another way to handle the situation.
MoveObject(myControl, new Thickness(0, 1, 2, 3), a =>
{
myButton.IsEnabled = true;
myGrid.IsEnabled = true;
myComboBox.SelectedIndex = 0;
Mouse.OverrideCursor = null;
});
private void MoveObject(FrameworkElement control, Thickness margins, Action<wMain> action, double speedCoefficient = 1)
{
var speed = new TimeSpan(Convert.ToInt64(_defaultAnimationSpeed.Ticks*speedCoefficient));
var animation = new ThicknessAnimation
{
From = control.Margin,
To = margins,
Duration = speed
};
var finalAction = new Action<wMain>(a =>
{
action(this);
control.BeginAnimation(MarginProperty, null); //Clear the animation so the property can be set manually.
control.Margin = margins;
});
animation.Completed += (sender, args) => { Dispatcher.BeginInvoke(finalAction, this); };
control.BeginAnimation(MarginProperty, animation);
}
I sometimes also execute actions by writing action(this); instead of calling the Dispatcher. Is one better than the other?

Delegate invocations are rather cheap; don't worry about it.
You only need the dispatcher if you aren't on the UI thread.

Related

Real Time chart performance issue c# winform

I am currently using liveChart to plot a real time graph of 3 values: a position, a load and a deformation. The program is based on the Doli.DoPE library (a proprietary dll)
In MainForm.cs, there is an event that is triggered everytime the sensor records a new value (every millisecond or so).
public void Initialisation()
{
//...
MyEdc.Eh.OnDataHdlr += new DoPE.OnDataHdlr(OnData)
//...
}
with
private int OnData(ref DoPE.OnData Data, object Parameter)
{
DoPE.Data Sample = Data.Data;
if (Data.DoPError == DoPE.ERR.NOERROR)
{
Int32 Time = Environment.TickCount;
if ((Time - LastTime) >= 250 /*ms*/)
{
// Send the data from the ondata handler inside of a global list
ListData.time.Add(Sample.Time);
ListData.position.Add(Sample.Sensor[(int)DoPE.SENSOR.SENSOR_S]);
ListData.load.Add(Sample.Sensor[(int)DoPE.SENSOR.SENSOR_F]);
ListData.extend.Add(Sample.Sensor[(int)DoPE.SENSOR.SENSOR_E]);
Thread ThForUpdateChart = new Thread(() =>
{
if (NewINstanceOfChart != null)
{ NewINstanceOfChart.UpdateValues(ListData.time.Last(), ListData.position.Last(),ListData.load.Last(), ListData.extend.Last()); }
});
ThForUpdateChart.Start();
LastTime = Time;
}
}
return 0;
}
The function UpdateValues is part of a second form RealTimeChart.cs called in the MainForm through a button click event:
private void btnGraph_Click(object sender, EventArgs e)
{
var thread = new Thread(() =>
{
NewINstanceOfChart = new RealTimeChart(ListData);
NewINstanceOfChart.Show();
});
thread.Start();
}
the form RealTimeCharts.cs is initalised this way:
public RealTimeChart(Globals ListData)
{
InitializeComponent();
//measures = ListData;
ListPosition = new ChartValues<ObservablePoint>();
for (int i = 0; i < measures.load.Count(); i++)
{
ListPosition.Add(new ObservablePoint
{
X = measures.time[i],
Y = measures.position[i]
});
}
ListLoad = new ChartValues<ObservablePoint>();
for (int i = 0; i < measures.load.Count(); i++)
{
ListLoad.Add(new ObservablePoint
{
X = measures.time[i],
Y = measures.load[i]
});
}
ListExtend = new ChartValues<ObservablePoint>();
for (int i = 0; i < measures.load.Count(); i++)
{
ListExtend.Add(new ObservablePoint
{
X = measures.time[i],
Y = measures.extend[i]
});
}
resultChart.Series.Add(new LineSeries
{
LineSmoothness = 0,
Values = ListPosition,
PointGeometrySize = 2,
StrokeThickness = 4
});
SetXAxisLimits();
}
And the UpdateValues function is defined as followed:
public void UpdateValues(double time, double position, double load, double extend)
{
measures.time.Add(time-measures.TareTime);
measures.position.Add(position);
measures.load.Add(load);
measures.extend.Add(extend);
UpdateEnabledSequencialPartToTrue();
}
public void UpdateEnabledSequencialPartToTrue()
{
if (this.InvokeRequired)
BeginInvoke(new System.Action(() => this.InternalUpdateEnabledSequencialPartToTrue()));
else
InternalUpdateEnabledSequencialPartToTrue();
}
private void InternalUpdateEnabledSequencialPartToTrue()
{
try
{
ListPosition.Add(new ObservablePoint
{
X = measures.time.Last(),
Y = measures.position.Last()
});
ListLoad.Add(new ObservablePoint
{
X = measures.time.Last(),
Y = measures.load.Last()
});
ListExtend.Add(new ObservablePoint
{
X = measures.time.Last(),
Y = measures.extend.Last()
});
//LineSeries plot = new LineSeries();
SetXAxisLimits();
// lets only use the last 14400 values (1h long recording, 14400 values at frequency of 1 record very 250ms, see OnData function MainForm
if (measures.time.Count > 14400)
{
ListPosition.RemoveAt(0);
ListLoad.RemoveAt(0);
ListExtend.RemoveAt(0);
}
}
catch (NullReferenceException) { }
}
After a minute, the programme starts to be really laggy. I tried putting the second winform (RealTimeCharts) on another thread so the MainForm does not lag (it is piloting a machine, it has to be responsive), but no success.
I would like to know if the whole thing is laggy because the code is way too bad, or if it is liveChart that reached its (free) limits. Would you advice another way to plot real time data ?
In MainForm.cs, there is an event that is triggered everytime the sensor records a new value (every millisecond or so).
That is natturally way higher then what Winforms Drawing can take. See, drawing a GUI is expensive. If you only do it once per user-triggered event, you will never notice that. But do it from a loop - including sampling a sensor every MS - and you can quickly overlord the UI. My first Multithreading tests actually appeared to have failed on big numbers, becaus I ended up sending so many updates I plain overloaded the GUI thread. Since then I know not to go past progress bars.
You can add data to a background collection as quickly as you can sample them, but you can not draw that quickly. And honestly drawing more often then 30-60 times/second (every ~17 ms) is not really going to help anyone anyway. Usually you can not use a timer, as the Tick might happen more often then it can be processed - again, a GUI Thread with a overflowing Event Queue.
I do not have any rate limiting code for WindowsForms. But I would guess an Event that re-queues itself at the end of the EventQueue after finishing the work would work.

"The calling thread cannot access this object because a different thread owns it" error when updating UI control from different thread in WPF

I've got method which I'am calling from main thread. This method is creating a new thread. Code looks like this:
MouseCursorWallObject MouseCursorWall = null;
List<MovingRectangle> MovingRectangles = null;
DrawingImage RenderedImage;
public MainWindow()
{
InitializeComponent();
PrepareObjects();
GameLoopRun();
}
private void GameLoopRun()
{
Thread thread = new Thread(() =>
{
while (true)
{
DateTime dtStart = DateTime.Now;
Events();
Update();
Display();
DateTime dtEnd = DateTime.Now;
TimeSpan ts = dtEnd - dtStart;
if (SkipTicks - ts.TotalMilliseconds >= 0)
{
Thread.Sleep((int)(SkipTicks - ts.TotalMilliseconds));
}
}
});
thread.Start();
}
In Display() method i'am trying update Image control. "Display()" method looks like this:
private void Display()
{
DrawingGroup imageDrawings = new DrawingGroup();
// Drawing main canvas
imageDrawings.Children.Add(DrawingObject(500, 350, 0, 0, new Uri(#"Images\gameCanvas.jpg", UriKind.Relative)));
// Drawing mouse cursor wall
imageDrawings.Children.Add(DrawingObject(MouseCursorWall.Width, MouseCursorWall.Height, MouseCursorWall.GetLocX, MouseCursorWall.GetLocY, MouseCursorWall.DisplayTexture));
for (int i = 0; i < MovingRectangles.Count; i++)
{
MovingRectangle o = MovingRectangles[i];
// Drawing moving object
imageDrawings.Children.Add(DrawingObject(20, 20, o.GetLocX, o.GetLocY, o.TextureUri));
}
if (GamePause == true)
{
}
RenderedImage = new DrawingImage(imageDrawings);
// Image control on main UI thread
renderImage.Dispatcher.Invoke(() =>
{
renderImage.Source = RenderedImage;
});
}
The problem is when I'am trying update Image control using Dispatcher.Invoke I'am receiving error "The calling thread cannot access this object because a different thread owns it". I was trying a lot of different options, and only one works fine:
private void Display()
{
this.Dispatcher.Invoke(() => {
DrawingGroup imageDrawings = new DrawingGroup();
// Drawing main canvas
imageDrawings.Children.Add(DrawingObject(500, 350, 0, 0, new Uri(#"Images\gameCanvas.jpg", UriKind.Relative)));
// Drawing mouse cursor wall
imageDrawings.Children.Add(DrawingObject(MouseCursorWall.Width, MouseCursorWall.Height, MouseCursorWall.GetLocX, MouseCursorWall.GetLocY, MouseCursorWall.DisplayTexture));
for (int i = 0; i < MovingRectangles.Count; i++)
{
MovingRectangle o = MovingRectangles[i];
// Drawing moving object
imageDrawings.Children.Add(DrawingObject(20, 20, o.GetLocX, o.GetLocY, o.TextureUri));
}
if (GamePause == true)
{
}
RenderedImage = new DrawingImage(imageDrawings);
renderImage.Source = RenderedImage;
});
}
Could You explain me why second option of "Display()" method works fine, but the first one throwing exception? What I'am doing wrong?
Both the DrawingImage and the DrawingGroup inherit from DispatcherObject, which means that they need to be accessed from the thread on which they were created. That is why your version where all of the work is invoked back to the dispatcher works correctly.
As pointed out by Brian Reichle, these object also inherit from System.Windows.Freezable, which you can leverage to allow cross thread access to the objects.

WPF Animation: Complete callback not called if to == from

I'd like to find a cleaner way to get the animation complete callback to fire when to == from. Right now I hack this to work by adding a small amount to my from value. Is there a better way?
//todo is there a way to get animations to call their Complete() even when to == from?
if (to.Equals(from)) {
from += .01;
}
DoubleAnimation animation = new DoubleAnimation {
Name = axis == Axis.X ? TranslateTransform.XProperty.Name : TranslateTransform.YProperty.Name,
From = from,
To = to,
Duration = TimeSpan.FromSeconds(translate.Time),
FillBehavior = FillBehavior.Stop,
EasingFunction = translate.Curve.ToEase(),
IsAdditive = false,
};
AnimationClock = animation.CreateClock();
AnimationClock.Completed += (sender, args) => {
};
You can check values before the creation of the animation.
If the Duration is important you can start a timer and launch the code when it ticks.
Something like this should do the trick :
if (to.Equals(from)) {
if (_timer == null)
{
_timer = new Timer(x =>
{
//completeCode
}, null, translate.Time * 1000, Timeout.Infinite);
}
else
{
_timer.Change(translate.Time * 1000, Timeout.Infinite);
}
return;
}

C# wpf dispatcher thread

I am trying to create a new Thread and put it to sleep in some occasions, but when I do it the main thread sleep, not only the one that I had created. I am using a Dispatcher.BeginInvoke but this is only to "give a permission" from the main thread to access to the method.
It works because it does not give me an InvalidOperationException, but the "focus" of the created thread losses when the linked method start.
I think I should use a ManualResetEvent to wait for the created Thread, but I do not know how doing it. I have been looking for possible solutions but no one works.
I think this should be easy but i cannot do it. The following code is Below:
void EmpujeDispatcher(object objeto)
{
this.Dispatcher.BeginInvoke(new Action<object>(Empuje), objeto);
}
private void Empuje(object objeto)
{
Thread.Sleep(2000); MessageBox.Show("This should not freeze the window");
Canvas Bacteria = objeto;
double PosX = Canvas.GetLeft(Bacteria);//PosiciĆ³n del sender
double PosY = Canvas.GetTop(Bacteria);//Lo mismo
Bacterias BacteriaInstancia = InstanciaBacterias[Bacteria.Uid];//Se busca la bacteria para relacionarla con al instancia
BacteriaInstancia.posX = PosX;
BacteriaInstancia.posY = PosY;
// BacteriaInstancia.Moverse();
if (BacteriaInstancia.momemtum <= 0)
{
Canvas.SetTop(Bacteria, PosY); Canvas.SetLeft(Bacteria, PosX);//Para el empuje
dispatcherTimer.Stop();
}
else
{ //Rebote:
BacteriaInstancia.Posicion();
PosX = BacteriaInstancia.posX;
PosY = BacteriaInstancia.posY;
if (PosX + Bacteria.Width >= CanvasSimulador.Width) { BacteriaInstancia.direccionAnterior = BacteriaInstancia.direccion; BacteriaInstancia.direccion = 1; }
if (PosX <= 0) { BacteriaInstancia.direccionAnterior = BacteriaInstancia.direccion; BacteriaInstancia.direccion = 3; }
if (PosY + Bacteria.Height >= CanvasSimulador.Height) { PosY = CanvasSimulador.Height - Bacteria.Height; BacteriaInstancia.direccionAnterior = BacteriaInstancia.direccion; BacteriaInstancia.direccion = 2; }
if (PosY <= 0) { PosY = 1; BacteriaInstancia.direccionAnterior = BacteriaInstancia.direccion; BacteriaInstancia.direccion = 4; }
Canvas.SetTop(Bacteria, PosY); Canvas.SetLeft(Bacteria, PosX);
BacteriaInstancia.momemtum = Math.Sqrt(Math.Pow(BacteriaInstancia.Vfx, 2) + Math.Pow(BacteriaInstancia.Vfy, 2));
ControlFlujo = BacteriaInstancia.momemtum;
}
private void EmpujeEvent(object sender, MouseButtonEventArgs e)
{
Thread TimerClockThread = new Thread(new ParameterizedThreadStart(EmpujeDispatcher));
TimerClockThread.IsBackground = true;
TimerClockThread.Start(sender);
}
This is not exacly the code because in this one Dispatcher does not have any sense, if I create the Thread without dispatcher
TimerClockThread = new Thread( new ParameterizedThreadStart(Empuje));
It works well... because it's a MessageBox, but in the original I have a lot of code inside of the "Empuje".
Thanks for your attention and hopefully you can help me :)
Your Dispatcher.Invoke forces your Empuje method to be called on the UI thread. If you want to update the screen, you should move the call to the background thread:
TimerClockThread = new Thread( new ParameterizedThreadStart(Empuje));
private void Empuje(object objeto)
{
Thread.Sleep(2000);
Dispatcher.BeginInvoke(new Action(() => {
MessageBox.Show("This should not freeze the window");
}));
//........ Do stuff.....
}
In modern C# with async however, you can remove all code and instead write:
private async void EmpujeEvent(object sender, MouseButtonEventArgs e)
{
await Task.Delay(2000);
MessageBox.Show(...);
}
Your function EmpujeDispatcher is using the same dispatcher that your GUI thread is associated with. That means you are telling the dispatcher to execute Empuje asynchronously, unfortunately it is executed on the GUI thread. At least that's what I think.
A BackgroundWorker is quite fit for this task. The shortest piece of code to implement it looks like this:
var worker = new BackgroundWorker();
worker.DoWork += (s,e) =>
{
Thread.Sleep(2000);
// Do Stuff...
};
worker.RunWorkerAsync();
Searching SO will yield a plethora of Q&A about the BackgroundWorker (i.e. this or this)

Trying to fire WPF StoryBoard multiple times with different Completed-actions

I'm unsuccessful in making a storyboard in code behind and running it multiple times chained to each other. Somehow, it seems the storyboard keeps in context, and will not reset.
I'm animating several elements, and X number of times I'm recursively running the animation-method, but with different call-back actions in the Completed event. First animation runs fine, but the rest it doesn't animate at all (the completed-event fires).
If I create a StoryBoard in a method and run it, should it not be disposed after it is completed? I'm trying to do storyboard.Remove().
private void SlideLeft(int numberOfStepsToSlide)
{
if (numberOfStepsToSlide < 1) return;
Slide(() => SlideLeft(numberOfStepsToSlide - 1));
}
protected void Slide(Action callBackAfterAnimation = null)
{
var sb = new Storyboard();
sb.FillBehavior = FillBehavior.Stop; //i thought maybe this would fix it, but no
//..
//.. a number of double animations created and added to storyboard
//..
sb.Completed += (sender, e) =>
{
sb.Stop();
sb.Remove();
//..
//..sending message to ViewModel and manipulating values
//..
if (callBackAfterAnimation != null)
callBackAfterAnimation();
};
sb.Begin();
}
Thanks for your time!
Sorry, completely forgot about this question!
You don't want to call Remove - that basically kills the animation dead, by killing all of the animation clocks created to run it...Try something like this instead (quick-and-dirty example):
var win = new Window();
win.Width = 50;
win.Height = 50;
int runCount = 3;
int halfSteps = runCount * 2;
double toWidth = 500.0;
var sb = new Storyboard();
var biggerator = new DoubleAnimation(toWidth, new Duration(TimeSpan.FromSeconds(2)));
sb.Children.Add(biggerator);
Storyboard.SetTarget(biggerator, win);
Storyboard.SetTargetProperty(biggerator, new PropertyPath("Width"));
sb.Completed += (o,e) =>
{
sb.Stop();
halfSteps--;
if(halfSteps <= 0)
{
win.Height = 150;
}
else
{
biggerator.To = biggerator.To == 0 ? toWidth : 0;
sb.Begin();
}
};
sb.Begin();
win.Show();

Categories

Resources