Threading progress bar not added to form - c#

Trying to create a thread which will modify the window which is generated from the class. It will also start the ultra activity indicator. however when I run this code it will modify the window but will not add the ultra activity monitor and will just have a white rectangle where it would sit.
public void refreshNotification()
{
Thread backgroundThread = new Thread(
new ThreadStart(() =>
{
window.Size = new System.Drawing.Size(330, 100);
window.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
window.TransparencyKey = System.Drawing.Color.Turquoise;
window.BackColor = System.Drawing.Color.Gainsboro;
window.MouseDown += window_MouseDown;
window.MouseMove += window_MouseMove;
window.MouseUp += window_MouseUp;
UltraActivityIndicator Indicator = customiseActivityIndicator();
window.Controls.Add(Indicator);
window.Show();
while (done == false)
{
Thread.Sleep(10);
//do nothing
}
Indicator.Stop();
}
));
backgroundThread.Start();
}
private static UltraActivityIndicator customiseActivityIndicator()
{
UltraActivityIndicator Indicator = new UltraActivityIndicator();
Indicator.Start();
Indicator.Location = new System.Drawing.Point(50, 35);
Indicator.Size = new System.Drawing.Size(230, 25);
Indicator.MarqueeAnimationStyle = Infragistics.Win.UltraActivityIndicator.MarqueeAnimationStyle.BounceBack;
Indicator.AnimationSpeed = (25);
Infragistics.Win.Appearance appearance12 = new Infragistics.Win.Appearance();
appearance12.BackColor = System.Drawing.Color.CornflowerBlue;
Indicator.MarqueeFillAppearance = appearance12;
return Indicator;
}

You are creating your window in separate Thread and after you put exactly that Thread into unlimit Sleep with this code:
while (done == false)
{
Thread.Sleep(10);
//do nothing
}
This is exactly why your window is white - it's busy from the Windows point of view.
I suggest you to create your window on UI Thread and use the BeginInvoke for resizing and indicator logic.

Related

Render to winforms picturebox over and over again

I have some bugs that need fixing, one of them involves an out of memory error.
Does anyone know how to do this properly? Thanks, I don't want it to be too messy, or too complicated. I just want to treat a new image as a buffer to render another image to (because of positional changes), and do it via a background thread. Not the UI thread (Too slow likely).
I get out of memory errors, and such. Also not able to access members of Form1 from within the thread function (images and the like throw access errors such as "Object already in use")
Here is my code:
System.Threading.Thread t;
public Image b;
public Bitmap c;
public Bitmap d;
public Bitmap e;
public Bitmap bg;
public Bitmap spr;
int spritex = 0;
int spritey = 0;
int spritedir = 1;
public Form1()
{
InitializeComponent();
Text = "Escape The Hypno Mansion!!".ToString();
t = new System.Threading.Thread(DoThisAllTheTime);
t.Start();
textBox1.Text = "Press Begin button to start!";
pictureBox1.Image = Image.FromFile(#"Images\introgirl.jpg");
b = new Bitmap(#"Images\introgirl.jpg");
c = new Bitmap(#"Images\sprite.png");
var graphics = Graphics.FromImage(b);
Pen blackpen = new Pen(Color.Black, 3);
graphics.DrawLine(blackpen, 0, 0, 100, 100);
graphics.DrawImage(c, new Point(500, 500));
pictureBox1.Image = b;
//pictureBox1.SizeMode = PictureBoxSizeMode.StretchImage;
}
public void DoThisAllTheTime()
{
while (true)
{
Point p = new Point(spritex, spritey);
bg = new Bitmap(#"Images\test.bmp");
spr = new Bitmap(#"Images\sprite.png");
using (var graphics = Graphics.FromImage(bg))
{
graphics.DrawImage(spr, p);
}
if (pictureBox1.Image != null)
{
pictureBox1.Image.Dispose();
}
pictureBox1.Image = bg;
pictureBox1.Invalidate();
if (spritedir == 1) { spritex += 5; }
if (spritedir == 2) { spritex -= 5; }
if (spritex < 0) { spritex = 0; spritedir = 1; }
if (spritex > 700) { spritex = 700; spritedir = 2; }
}
}
The reason you can't change the image in your picturebox is because the thread that created the image is not the thread that created the picturebox.
In a debugger you can check this by asking the picturebox for InvokeRequired (function Control.IsInvokeRequired) just before changing the function.
So let's rewrite your function and show that modern classes Like Task are much easier to use the your thread.
I'll start your task when the form is loading, and try to stop it when the form is closing.
private Task myTask = null;
private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private void OnFormLoading(object sender, EventArgs e)
{
// Start the task
this.myTask = Task.Run( () => DoMyWork(this.cancellationTokenSource.Token));
}
private void OnFormClosing(object sender, FormClosingEventArgs e)
{
// if the Task is still running, ask it to stop itself:
if (myTask != null && !myTask.IsCompleted)
{ // ask the task to stop and wait until it is completed:
this.cancellationTokenSource.Cancel();
// all Tokens extractes from this source will get state CancellationRequested
// wait maximum 5 seconds until the task is completed:
this.UseWaitCursor = true;
this.myTask.Wait(TimeSpan.FromSeconds(5));
this.UseWaitCursor = false;
// cancel closing if the task is still not completed
e.Cancel = !this.myTask.Completed;
}
}
Now the function DoMyWork:
private void DoMyWork(CancellationToken cancellationToken)
{
// Do the same as in your DoThisAllTheTime
// except that you regularly check cancellationToken.IsCancelRequested:
while(!cancellationToken.IsCancelRequested)
{
// calculate the image to display
var imageToDisplay = ...
this.DisplayImage(imageToDisplay);
}
}
void DisplayImage(Image imageToDisplay)
{
if (this.pictureBox1.InvokeRequired)
{
this.Invoke(new MethodInvoker( () => this.DisplayImage(imageToDisplay)));
}
else
{
this.PictureBox1.Image = imageToDisplay;
}
}
See:
How to cancel a Task and its children
Use InvokeRequired with lambda expression
Dispose every disposable instances before the loop ends. Your memory leak is related with disposable items not being cleaned from memory, so you'll eventually run out of memory in your infinite loop.
At the very least, you'll want to dispose both bitmaps at the end of the loop:
bg = new Bitmap(#"Images\test.bmp");
spr = new Bitmap(#"Images\sprite.png");

wpf Multiple dispatch timer for timer application : Run multiple timer simulteniously

Read multiple stackoverflow, codeproject solution, could not integrate to my problem.
Have a datagrid in a usercontrol which is loaded in a window. Each DataRow in the DataGrid represents a timer setting.
Like:
timer name : Test 1 , Timer : 1h 3m
timer name : Test 2 , Timer : 2h 2m
timer name : Test 3 , Timer : 3h 1m
Selecting a row, clicking on the button Start, Starts the timer of that row. And with dispatcher tick event, it updates the grid I have done till this. Now I have to start another(or two or ...) timer which will do the same at the same time. I am stuck on this. Let me share what I have tried!
btnStartClickEvent in mainwindow.xaml.cs
if (btnStart.Content.ToString() == "Start")
{
if (_AUC == ActiveUserControl.Grid)
{
runningRow = (TaskGridData)_TG.dgEmployee.SelectedItem;
if (runningRow != null)
{
currentlyRunningID.Add(runningRow.ID);
btnStart.Content = "Stop";
//worker.RunWorkerAsync(runningRow);
StartTimer(runningRow);
}
}
}
else if (btnStart.Content.ToString() == "Stop")
{
btnStart.Content = "Start";
StopTimer();
}
private DateTime TimerStart { get; set; }
private void StartTimer(TaskGridData tgd)
{
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 1, 0);
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
TimerStart = DateTime.Now;
dispatcherTimer.Start();
//worker.RunWorkerAsync();
//string etime = DateTime.Now.Second.ToString();
}
private void StopTimer()
{
dispatcherTimer.Stop();
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
var currentValue = DateTime.Now - TimerStart;
runningRow.Duration = DurationValueToString(currentValue);
temp = (List<TaskGridData>)_TG.dgEmployee.ItemsSource;
foreach (TaskGridData item in temp)
{
if (item.ID == runningRow.ID)
{
item.Duration = DurationValueToString(DurationStringToVlaue(item.Duration) - DurationStringToVlaue(runningRow.Duration));
break;
}
}
//_TG.dgEmployee.ItemsSource = null;
//_TG.dgEmployee.ItemsSource = temp;
Thread NewThreadforStartProcessAfterTraining = new Thread(() => UpdateGrid());
NewThreadforStartProcessAfterTraining.IsBackground = true;
NewThreadforStartProcessAfterTraining.SetApartmentState(ApartmentState.STA);
NewThreadforStartProcessAfterTraining.Start();
}
private void UpdateGrid()
{
this.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
_TG.dgEmployee.ItemsSource = null;
_TG.dgEmployee.ItemsSource = temp;
}));
}
I know this code is for single timer. If I click a 2nd row and try to start timer, then it gets error in tick event, running row is found null.
I am wondering how can I keep this code and make it work for multiple timer. May be multithreading. A guide to do that, will be very helpful.
Thread NewThreadforStartProcessAfterTraining = new Thread(() => UpdateGrid());
NewThreadforStartProcessAfterTraining.IsBackground = true;
NewThreadforStartProcessAfterTraining.SetApartmentState(ApartmentState.STA);
NewThreadforStartProcessAfterTraining.Start();
All the above part where you start a new STA thread is unneeded and wrong in this context, since you can't update the visual tree in this way.
You can find a correct example of using a STA thread in one of my previous answers: https://stackoverflow.com/a/42473167/6996876
Try to understand the concept of thread affinity in WPF.
You simply need an UpdateGrid() where you have to delegate UI work to the dispatcher.
Furthermore, passing an argument to the Tick event is already explained here: https://stackoverflow.com/a/16380663/6996876
In your case you may want to change the current unique runningRow so that it's passed to the event instead.

Why disabling a button would cause InvalidOperationException - C#

I recently encountered a situation for which I have no explanation so far
Trying to work on cross-threading operations with winform I wrote a short piece of code to test solutions faster.
I have a form including a progress bar, a a DataGridView and a Button
public MainForm()
{
this._progressBar = new ProgressBar { Dock = DockStyle.Top, };
this._button = new Button { Dock = DockStyle.Bottom, Text = #"&GO!" };
this._dataGridView = new DataGridView {Dock = DockStyle.Fill,};
this.Controls.Add(this.ProgressBar);
this.Controls.Add(this.Button);
this.Controls.Add(this.DataGridView);
this.WindowsFormsSynchronizationContext = WindowsFormsSynchronizationContext.Current as WindowsFormsSynchronizationContext;
this._records = new SpecialBindingList<Record>();
//this._records = new SpecialBindingList<Record>();
this.DataGridView.DataSource = this.Records;
this.Button.Click += this.button_Click;
}
The button has an event OnClick
private void button_Click(object sender, EventArgs e)
{
var dispatcherUI = Dispatcher.CurrentDispatcher;
Action action = () =>
{
while (true)
{
Task.Factory.StartNew(() =>
{
var value = (this.ProgressBar.Value == this.ProgressBar.Maximum)
? 0
: this.ProgressBar.Value + 1;
var record = new Record
{
A = DateTime.Now.Second.ToString(),
B = DateTime.Now.Millisecond.ToString()
};
if (Thread.CurrentThread != dispatcherUI.Thread)
{
dispatcherUI.BeginInvoke(new Action(() => { ProgressBar.Value = value; }), null);
}
this.WindowsFormsSynchronizationContext.Send((state) => this.Records.Add(record), null);
Thread.Sleep(100);
});
}
};
var task = new Task(action);
task.Start();
this.Button.Enabled = false;
}
It creates a task, which is filling the progress bar and adding new lines to the grid, then start it and disable the button.
The problem is, is I start the code like this the action (state) => this.Records.Add(record) would always throw an InvalidOperationException when adding a record to my Bindinglist.
However I realized that if I skip the this.Button.Enabled = false; line my code would execute without any problem !
This seems a little odd to me, so I was wondering why modifying a button property would creates an exception on an apparently unrelated operation

Error:The calling thread cannot access this object because a different thread owns it

I have a Threading.timer that show a ballon in a special time.
I use this code for show Balloon
var thread = new Thread(new ThreadStart(DisplayFormThread));
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
private void DisplayFormThread()
{
try
{
Show();
}
catch (Exception ex)
{
// Log.Write(ex);
}
}
it is my class for show ballon .
if (!Application.Current.Dispatcher.CheckAccess())
{
var action = new Action(() => ShowCustomBalloon(balloon, animation, timeout));
Application.Current.Dispatcher.Invoke(DispatcherPriority.Normal, action);
return;
}
if (balloon == null) throw new ArgumentNullException("balloon");
if (timeout.HasValue && timeout < 500)
{
string msg = "Invalid timeout of {0} milliseconds. Timeout must be at least 500 ms";
msg = String.Format(msg, timeout);
throw new ArgumentOutOfRangeException("timeout", msg);
}
Popup popup = new Popup();
popup.AllowsTransparency = true;
popup.PopupAnimation = animation;
popup.Child = balloon;
popup.Placement = PlacementMode.AbsolutePoint;
popup.StaysOpen = true;
Point position = new Point(SystemParameters.WorkArea.Width - ((UserControl)balloon).Width,
SystemParameters.WorkArea.Height - ((UserControl)balloon).Height);
popup.HorizontalOffset = position.X - 1;
popup.VerticalOffset = position.Y - 1;
//display item
popup.IsOpen = true;
when i show the balloon i get error :The calling thread cannot access this object because a different thread owns it
in this code i get error :
popup.Child = balloon;
You cannot update UI directly from another thread. When you are done in the thread and need to update the UI then you can use following:
this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (System.Threading.ThreadStart)delegate()
{
// Update UI properties
});
"this" is a UI element for example the window. You can also use:
System.Windows.Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, (System.Threading.ThreadStart)delegate()
{
// Update UI properties
});
instead of reference to the UI component i.e. "this" in the example above.

Task blocks UI from refreshing in WPF

I am building a WPF application that converts a powerpoint to WPF elements when you select one from a list.
I am using MVVM light to bind a ViewModel to my view and to add communication between ViewModels.
I have two views: OpenLocalView and PresentationView. When I select a powerpoint in the OpenLocalView, a message will be sent by MVVM light to the ViewModel of PresentationView and the MainViewModel with the path to that powerpoint. The MainViewModel switches the view to the PresentationView, and the PresentationViewModel executes this code to convert the powerpoint, and when that is finished, set the current slide so it is shown in the PresentationView:
public void StartPresentation(string location)
{
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Loading = true;
Task.Factory.StartNew(() =>
{
var converterFactory = new ConverterFactory();
var converter = converterFactory.CreatePowerPointConverter();
_slides = converter.Convert(location).Slides;
},
CancellationToken.None,
TaskCreationOptions.LongRunning,
scheduler).ContinueWith(x =>
{
Loading = false;
CurrentSlide = _slides.First();
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
scheduler);
}
When the Loading property is set, the view gets updated with a "loading" message, to make the UI more responsive:
public Boolean Loading
{
get { return _loading; }
set
{
_loading = value;
RaisePropertyChanged("Loading");
}
}
The problem is, this executes properly the first time when I load a powerpoint: The view switches to the PresentationView, the "loading" message is displayed, and after the converting is finished, the message disappears and the slide is shown. But when I go back to the OpenLocalView, and choose another powerpoint, the OpenLocalView hangs and it switches to the PresentationView after the converter is finished, not showing the "loading" message at all.
For reference, I will add some more relevant code.
This is executed when a powerpoint is selected in the OpenLocalViewModel:
private void PerformOpenPresentation(string location)
{
Messenger.Default.Send<OpenPowerPointMessage>(new OpenPowerPointMessage {Location = location});
}
The MainViewModel is subscribed to the messenger and switches the view:
Messenger.Default.Register<OpenPowerPointMessage>(this,
delegate
{
if (_presentation == null) _presentation = new PresentationView();
CurrentView = _presentation;
});
The PresentationViewModel is subscribed to the messenger as well and executes the method shown above:
Messenger.Default.Register<OpenPowerPointMessage>(this, message => StartPresentation(message.Location));
So, what am I doing wrong? Again, it executes fine one time, then after that not anymore, although the same code is executed.
Maybe the UI isn't updated yet when you already start converting. Try waiting a few milliseconds between setting the Loading to true and the start of the converter thread :)
Look here:
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Loading = true;
Task.Factory.StartNew(() =>
{
var converterFactory = new ConverterFactory();
var converter = converterFactory.CreatePowerPointConverter();
_slides = converter.Convert(location).Slides;
},
CancellationToken.None,
TaskCreationOptions.LongRunning,
here ----> scheduler).ContinueWith(x =>
{
Loading = false;
CurrentSlide = _slides.First();
},
CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
scheduler);
You're starting the 'long running' task on the synchronization context, that is - on the UI thread.
Get rid of the scheduler in the long running task, leave it on continuation. :)
I suggest moving the "Loading = true" in to task start block.. but make sure to use dispatcher while setting "Loading" value. i may not give the actual reason for the problem but its worth a try...
something like this may help..
Task.Factory.StartNew(() =>
{
System.Windows.Application.Current.Dispatcher.BeginInvoke((Action)delegate()
{
Loading = true;
});
var converterFactory = new ConverterFactory();
var converter = converterFactory.CreatePowerPointConverter();
_slides = converter.Convert(location).Slides;
}
Ok, I did it with windows forms, but I think is the same
I create a Label in the Task thread and call the Invoke of the form
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void AddControl(Control control)
{
if (InvokeRequired)
{
this.Invoke(new Action<Control>(AddControl), new object[] { control });
return;
}
this.Controls.Add(control);
}
private void Form1_Load(object sender, EventArgs e)
{
Task.Factory.StartNew(() =>
{
var label = new Label
{
Location = new Point(0, 0),
Text = "hola",
ForeColor = Color.Black
};
this.Invoke(new Action<Control>(AddControl), new object[] { label });
});
}
}
EDIT
Ok how about using Dispather.Invoke I'm not sure if this is going to block the UI....
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public void AddControl()
{
var l = new Label
{
Content = "Label",
Height = 28,
HorizontalAlignment = System.Windows.HorizontalAlignment.Left,
Margin = new Thickness(209, 118, 0, 0),
Name = "label1",
VerticalAlignment = System.Windows.VerticalAlignment.Top
};
Grid.Children.Add(l);
}
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
Task.Factory.StartNew(() =>
{
Dispatcher.Invoke(new Action(AddControl), null);
});
}
}

Categories