WPF Application DispatcherTimer not working correctly (lag) - c#

I have been trying to run a very simple application that moves a 20 by 20 pixel square 20 pixels to the right on a canvas every second. I am using a dispatchertimer to fire the event every second.
The problem is that the square doesn't move to the right unless I shake the application window (with my mouse), and it occasionally moves on its own (albeit not every second).
I have already tried reinstalling Visual Studio 2017 and installing it on my SSD and HDD, neither seem to fix the issue.
Here is the full code of the application's MainWindow.xaml.cs
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
DispatcherTimer timer = new DispatcherTimer();
Rectangle s = new Rectangle();
Point currentPosition = new Point(20, 20);
public MainWindow()
{
InitializeComponent();
timer.Tick += Timer_Tick;
timer.Interval = TimeSpan.FromSeconds(1);
timer.Start();
s.Width = 20;
s.Height = 20;
s.Fill = new SolidColorBrush(Colors.Black);
map.Children.Add(s);
}
public void Timer_Tick(object sender, EventArgs e)
{
RedrawSquare();
}
public void RedrawSquare()
{
map.Children.Clear();
s.Width = 20;
s.Height = 20;
s.Fill = new SolidColorBrush(Colors.Black);
Canvas.SetLeft(s, currentPosition.X += 20);
map.Children.Add(s);
}
}
On the MainWindow.xaml file there is an empty Canvas with the name "map"
Thank you in advance

You don't need to remove and add the Rectangle on each timer tick, or reset its properties each time.
Just increment the value of the Canvas.Left property:
public partial class MainWindow : Window
{
private readonly DispatcherTimer timer = new DispatcherTimer();
private readonly Rectangle s = new Rectangle();
public MainWindow()
{
InitializeComponent();
timer.Tick += Timer_Tick;
timer.Interval = TimeSpan.FromSeconds(1);
timer.Start();
s.Width = 20;
s.Height = 20;
s.Fill = Brushes.Black;
Canvas.SetLeft(s, 0);
map.Children.Add(s);
}
public void Timer_Tick(object sender, EventArgs e)
{
Canvas.SetLeft(s, Canvas.GetLeft(s) + 20);
}
}
The movement would however be much smoother with an animation:
public MainWindow()
{
InitializeComponent();
s.Width = 20;
s.Height = 20;
s.Fill = Brushes.Black;
Canvas.SetLeft(s, 0);
map.Children.Add(s);
var animation = new DoubleAnimation
{
By = 20,
Duration = TimeSpan.FromSeconds(1),
IsCumulative = true,
RepeatBehavior = RepeatBehavior.Forever
};
s.BeginAnimation(Canvas.LeftProperty, animation);
}

You can try setting the DispatcherPriority to Normal.
Instantiate your timer like this:
DispatcherTimer timer = new DispatcherTimer(DispatcherPriority.Normal);
EDIT:
Although this somehow fixed the issue (square was moving without the need to move the window), it's apparently still the wrong answer. I don't know much about the DispatcherTimer, but I recall having changed the priority once but I don't remember why. In any case, it might be helpful to someone else.

Related

Richtextbox multiple back colors

I saw this cool thing about having a RichTextBox like a "progress bar" if we can call it like that from another window application and I was trying to do the same.
I was looking at the methods and I couldn't apply any of them on what I'm looking for, I tried to see if there was a similar question but I didn't get so lucky.
How can I accomplish the same result?
Looks like a Richtextbox to me
Based on what Jimi told me I will to explain what I need. The string inside label rappresents a timer which has to change the color in red when it reaches the end, before of that, like at 10 minutes I want it to be Yellow, using that like an alert.
The way the method is wroten doesn't let me to choose which color I want. After that, when the timer is stopped by a button, I want the label to redraws itself and makes it blank without having any kind of colors, looking like a "textbox".
This is a label used as a ProgressBar.
Just an example (quite raw, what I could do with the time I had), but it shows how you can paint the surface of a Control that provides a Paint() event.
It uses a Timer class to increase a value and generates a progress bar effect by calling the Label.Invalidate() method, which raises the Label's Paint event, executing whatever code you have in the label1_Paint() handler.
If you want to test it, paste this code inside a Form which contains a Button (button1) to start the Timer and a Label (label1) that generates the graphic effect.
Then assign the two events - Click() to the Button and Paint() to the Label.
This is how it looks like:
Timer timer;
private bool TimerStarted = false;
private float ProgressMaxValue = 100;
private float Progress = 0;
private int seconds = 0;
private int cents = 0;
private void button1_Click(object sender, EventArgs e)
{
if (TimerStarted) { TimerStop(); return; }
timer = new Timer();
timer.Interval = 20;
Progress = 0;
seconds = 0;
cents = 0;
timer.Tick += (s, ev) => {
++Progress;
if (Progress > ProgressMaxValue) { TimerStop(); return; }
cents += (timer.Interval / 5);
if (cents > 99) { cents = 0; ++seconds; }
this.label1.Invalidate();
};
TimerStarted = true;
timer.Start();
}
private void TimerStop()
{
timer.Stop();
timer.Dispose();
TimerStarted = false;
}
private void label1_Paint(object sender, PaintEventArgs e)
{
StringFormat format = new StringFormat() {
Alignment = StringAlignment.Center,
LineAlignment = StringAlignment.Center
};
e.Graphics.Clear(this.label1.BackColor);
Rectangle rect = label1.ClientRectangle;
rect.Inflate(-1, -1);
e.Graphics.DrawRectangle(Pens.LimeGreen, rect);
RectangleF ProgressBar = new RectangleF(
new PointF(3, 3),
new SizeF((((float)rect.Width - 3) / ProgressMaxValue) * Progress, rect.Height - 4));
e.Graphics.FillRectangle(Brushes.YellowGreen, ProgressBar);
e.Graphics.DrawString($"0.{seconds.ToString("D2")}.{cents.ToString("D2")}", label1.Font, Brushes.White, rect, format);
}

C# - Automatically close a form after x minutes

Situation: I wrote a WinForms application which users access via an RDP. The nature of the application is such that two users cannot run the application at the same time. The problem we're having is that folks will forget to close out of the app, essentially locking other users out.
What I'd like to do is add functionality to automatically close the app after x minutes. I realize that I need to use threading for this since marking time in the main thread would freeze up the app. I don't have a critical need to detect activity/inactivity, but if that's trivial to do, I'd definitely like to know.
Thanks in advance for any assistance!
Assuming you're talking about closing the app from inside the WinForms app itself...use a Timer as suggested by SLaks:
public partial class Form1 : Form
{
private System.Windows.Forms.Timer tmr;
public Form1()
{
InitializeComponent();
tmr = new System.Windows.Forms.Timer();
tmr.Tick += delegate {
this.Close();
};
tmr.Interval = (int)TimeSpan.FromMinutes(10).TotalMilliseconds;
tmr.Start();
}
}
If you wan't to get fancier and close after X minutes of inactivity, then write your own IMessageFilter() that resets a Timer whenever mouse/keyboard activity occurs (WM_LBUTTONDOWN, WM_KEYDOWN, WM_SYSKEYDOWN, etc.). You register your filter with Application.AddMessageFilter() in the Load() event of your Form.
This may help, just put this together, it will close the form 1.5 seconds after the button1 is pressed. you could change this to any time after form load.
public void testc()
{
Timer t = new Timer();
t.Interval = 1500;
t.Tick += new EventHandler(timer_Tick);
t.Start();
}
private void timer_Tick(object sender, EventArgs e)
{
MessageBox.Show("Tick");
this.Close();
}
private void button1_Click(object sender, EventArgs e)
{
testc();
}
I wrote a class starting from the Idle_Mind's reply, that create a message with another feature : Opacity
Add a Form to your project and call it VanishingMessage.
Add a text label to the Form, i called it : testo
Add the event PAINT to the form.
Copy and paste this code
When you call the class set the the message and seconds as parameters, like required.
I personally suggest to set the FormBorderStyle to NONE.
public partial class VanishingMessage : Form
{
private System.Windows.Forms.Timer tmr;
private System.Windows.Forms.Timer tmr2;
/// <summary>
/// Shows a message windows. 3 sec is the suggested time
/// </summary>
/// <param name="messageText">Message</param>
/// <param name="vanishingSeconds">Showing time</param>
public VanishingMessage(string messageText, int vanishingSeconds=3)
{
InitializeComponent();
double elapsedTime = 0;
testo.Text = messageText;
double vanishingMilliSeconds = vanishingSeconds * 1000;
// message dimensions
this.Width = testo.Width + 50;
this.Height = testo.Height + 50;
//text position
testo.Location = new Point((this.Width / 2) - (testo.Width / 2), (this.Height/2)-(testo.Height/2));
//first timer
tmr = new System.Windows.Forms.Timer();
tmr.Tick += delegate {
tmr.Stop();
tmr2.Stop();
tmr.Dispose();
tmr2.Dispose();
this.Close();
};
tmr.Interval = (int)TimeSpan.FromSeconds(vanishingSeconds).TotalMilliseconds;
tmr.Start();
//second timer
tmr2 = new System.Windows.Forms.Timer();
tmr2.Tick += delegate {
elapsedTime += 50;
if (elapsedTime >= (vanishingMilliSeconds * 65)/100)
this.Opacity -= 0.05f;
};
tmr2.Interval = (int)TimeSpan.FromMilliseconds(50).TotalMilliseconds;
tmr2.Start();
}
private void VanishingMessage_Paint(object sender, PaintEventArgs e)
{
Graphics g = this.CreateGraphics();
Pen p = new Pen(Color.DarkRed);
SolidBrush sb = new SolidBrush(Color.DarkRed);
Rectangle r = this.DisplayRectangle;
r.Width -= 1;
r.Height -= 1;
g.DrawRectangle(p,r);
}
}
Here how to call the class :
VanishingMessage vm = new VanishingMessage("your message",3);
vm.Show();

System.InvalidOperationException in C# WPF

I want to move a rectangle in WPF application using the following code. However, I am getting the following error:
System.InvalidOperationException: Cannot use a DependencyObject that belongs to a different thread
I looked at other problems in stackoverflow but nothing worked.
public partial class MainWindow : Window
{
private Rectangle rect;
int count = 1;
Timer timer;
public MainWindow()
{
InitializeComponent();
Rectangle movedRectangle = new Rectangle();
movedRectangle.Width = 200;
movedRectangle.Height = 50;
movedRectangle.Fill = Brushes.Blue;
movedRectangle.Opacity = 0.5;
TranslateTransform translateTransform1 = new TranslateTransform(50, 20);
movedRectangle.RenderTransform = translateTransform1;
this.can.Children.Add(movedRectangle);
this.rect = movedRectangle;
timer = new Timer(500);
timer.Elapsed += OnTimedEvent;
timer.Enabled = true;
}
private void OnTimedEvent(Object source, ElapsedEventArgs e)
{
count++;
TranslateTransform translateTransform1 = new TranslateTransform(50 + count * 2, 20);
this.rect.Dispatcher.Invoke(new Action(()=>
rect.RenderTransform = translateTransform1));
//this.can.UpdateLayout();
this.can.Dispatcher.Invoke(new Action(()=>
this.can.UpdateLayout()
));
}
I would suggest you to use DispatcherTimer than a normal timer.
Please see the below solution. enjoy.
Note: for DispatcherTimer you will need to add the assembly reference for System.Windows.Threading
public partial class MainWindow : Window
{
private Rectangle rect;
int count = 1;
private DispatcherTimer timer = null;
public MainWindow()
{
InitializeComponent();
Rectangle movedRectangle = new Rectangle();
movedRectangle.Width = 200;
movedRectangle.Height = 50;
movedRectangle.Fill = Brushes.Blue;
movedRectangle.Opacity = 0.5;
TranslateTransform translateTransform1 = new TranslateTransform(50, 20);
movedRectangle.RenderTransform = translateTransform1;
this.can.Children.Add(movedRectangle);
this.rect = movedRectangle;
timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 0, 500);
timer.Tick += timer_Tick;
timer.Start();
timer.IsEnabled = true;
}
void timer_Tick(object sender, EventArgs e)
{
count++;
TranslateTransform translateTransform1 = new TranslateTransform(50 + count * 2, 20);
Dispatcher.BeginInvoke(new Action<TranslateTransform>(delegate(TranslateTransform t1)
{
rect.RenderTransform = t1;
this.can.UpdateLayout();
}), System.Windows.Threading.DispatcherPriority.Render, translateTransform1);
}
}
You are constructing a TranslateTransform (which is a DependencyObject) outside the UI thread. Easy fix:
this.rect.Dispatcher.Invoke(new Action(
() =>
{
TranslateTransform translateTransform1 = new TranslateTransform(50 + count * 2, 20);
rect.RenderTransform = translateTransform1;
}));
Arguably a better fix: use a DispatcherTimer instead and get rid of all your Dispatcher.Invoke calls.

Maintaining 60 elements in a observablecollection

Hi I have a ObservableCollection getting data in every minute. When it reaches an hour, I would like to clear the first item and move all the items up then adding the new item, thus maintaining it at 60 elements. Does anyone have any idea how to do so?
Here is my code:
public class MainWindow : Window
{
double i = 0;
double SolarCellPower = 0;
DispatcherTimer timer = new DispatcherTimer();
ObservableCollection<KeyValuePair<double, double>> Power = new ObservableCollection<KeyValuePair<double, double>>();
public MainWindow()
{
InitializeComponent();
timer.Interval = new TimeSpan(0, 0, 1); // per 5 seconds, you could change it
timer.Tick += new EventHandler(timer_Tick);
timer.IsEnabled = true;
}
void timer_Tick(object sender, EventArgs e)
{
SolarCellPower = double.Parse(textBox18.Text);
Power.Add(new KeyValuePair<double, double>(i, SolarCellPower ));
i += 5;
Solar.ItemsSource = Power;
}
}
Just count the items in your list and remove the top item if the count equals 60. Then insert the new item like normal.
if (Power.Count == 60)
Power.RemoveAt(0);
Power.Add(new KeyValuePair<double, double>(i, SolarCellPower ));
Also if you Bind your ItemsSource instead of setting it, it will update automatically when the collection changes.

Why does the WPF storyboard stops after a few cycles?

I have a canvas where I draw rectangles and move them randomly with the help of a storyboard. After a few cycles the storyboard.completed event does not fire any more. Does anybody know? here is my xaml:
<Grid>
<Canvas Name="movingCanvas" Background="Green" Margin="0,29,0,0"></Canvas>
<TextBlock Height="23" Name="textBlock1" Text="TextBlock" Margin="528,0,0,538" />
</Grid>
And the code:
private Random random = new Random();
private Storyboard gameLoop = new Storyboard();
private int i = 0;
public Window3()
{
InitializeComponent();
this.gameLoop.Duration = TimeSpan.FromMilliseconds(100);
this.gameLoop.Completed += new EventHandler(this.gameLoop_Completed);
this.gameLoop.Begin();
}
private void gameLoop_Completed(object sender, EventArgs e)
{
this.addRectangle();
this.moveRectangle();
i++;
this.textBlock1.Text = i.ToString();
this.gameLoop.Begin();
}
private void addRectangle()
{
Rectangle rect = new Rectangle();
rect.Height = 100;
rect.Width = 100;
rect.Stroke = new SolidColorBrush(Colors.Black);
Canvas.SetLeft(rect, random.Next((int)this.Width));
Canvas.SetTop(rect, random.Next((int)this.Height));
this.movingCanvas.Children.Add(rect);
}
private void moveRectangle()
{
foreach (UIElement elm in this.movingCanvas.Children)
{
int moveLeft = random.Next(10);
int distance = random.Next(-10, 20);
if (moveLeft > 5)
{
Canvas.SetTop(elm, Canvas.GetTop(elm) + distance);
}
else
{
Canvas.SetLeft(elm, Canvas.GetLeft(elm) + distance);
}
}
}
The Completed event does also not occur whitout creating and moving rectangles:
private Storyboard gameLoop = new Storyboard();
private int i = 0;
public Window3()
{
InitializeComponent();
this.gameLoop.Duration = TimeSpan.FromMilliseconds(100);
this.gameLoop.Completed += new EventHandler(this.gameLoop_Completed);
this.gameLoop.Begin();
}
private void gameLoop_Completed(object sender, EventArgs e)
{
i++;
this.textBlock1.Text = i.ToString();
this.gameLoop.Begin();
}
If you add a animation to the storyboard, the storyboard not stops firing the event.
public Window3()
{
InitializeComponent();
this.gameLoop.Duration = TimeSpan.FromMilliseconds(100);
this.gameLoop.Completed += new EventHandler(this.gameLoop_Completed);
DoubleAnimation animation= new DoubleAnimation { From = 100, To = 101 };
ani.SetValue(Storyboard.TargetProperty, this);
ani.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath("Height"));
this.gameLoop.Children.Add(ani);
this.gameLoop.Begin();
}
Like Kshitij Mehta said above, i think use a timer instead the storyboard, but maybe you have a reason to use a storyboard....
Do you really mean to add a new rectangle on every iteration of the loop?
You'll very quickly get tens of thousands if not millions of rectangles which will take longer and longer to draw.

Categories

Resources