Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed last year.
Improve this question
This is a little guide to help resolve a problem with timers that stops when opening a file using process to open it with the default application.
If you are using a timer in C# WPF you will notice that the timer will stop working until the file has been closed which may not be the behavior that you want.
Let say for instance that you want a timer to start as soon as the file you want to show to the user is open. While the user sees or read the file you want the timer to continue. Without using a Thread the timer will not continue it's work up until the file is closed.
This is the problem encountered:
I have a timer that is started by pressing a button (btnTache1) but it stops when I'm using System.Diagnostics.Process to open a file with it's default application using another button (btnVideo1). It only resume after the file is closed.
This a brief description of the components of the program:
There is a button to show the media called btnVideo1
The onclick event of the button defines a timer _timer1 and the file to show to the user _media1 and a countdown used by the timer _startingTimer1 set to 30 seconds.
Here is the code:
private int _startingTimer1;
private string _media1;
DispatcherTimer _timer1 = new DispatcherTimer();
private void btnVideo1_Click(object sender, RoutedEventArgs e)
{
_startingTimer1 = 30; // 30 seconds
// stop timer if already started
if (_timer1.IsEnabled == true)
{
_timer1.Stop();
}
// configure timer
_timer1 = new DispatcherTimer();
_timer1.Interval = TimeSpan.FromSeconds(1);
_timer1.Tick += timer_Tick1;
_timer1.Start();
// defining the file to show to the user
string procedure = "procedure1.mp4"
_media1 = "//10.10.0.1/Procedures/ + procedure;
ShowVideo(_media1);
}
// Action done when a tick for timer occur
private void timer_Tick1(object sender, EventArgs e)
{
// decreasing countdown
_startingTimer1--;
// calculate and show the timer (countdown)
TimeSpan time = TimeSpan.FromSeconds(_startingTimer1);
tbxTemps1.Text = time.ToString();
// if timer under 0
if (_startingTimer1 < 0)
{
// change background color depending on number
if (_startingTimer1 % 2 == 0)
btnTemps1.Background = new SolidColorBrush(Colors.OrangeRed);
else
btnTemps1.Background = new SolidColorBrush(Colors.LightGray);
}
}
// show the file to the user
private void ShowVideo(string media)
{
try
{
// define a process to show the file
System.Diagnostics.Process process = new System.Diagnostics.Process();
// declare the path to the file
process.StartInfo.FileName = new Uri(media, UriKind.Absolute).ToString();
process.StartInfo.UseShellExecute = true;
// start the process to show the file to the user
process.Start();
process.WaitForExit();
}
catch
{
MessageBox.Show("Could not open the file.", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
To resolve the issue the use of a Thread really comes handy. Calling the function using a thread makes the timer continue in the primary thread while other things are done in parallel in another thread. To achieve this here's what I have changed.
I changed the ShowVideo method as follow:
private void ShowVideo(string media)
{
try
{
System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = new Uri(media, UriKind.Absolute).ToString();
process.StartInfo.UseShellExecute = true;
Thread showFile = new Thread(() => process.Start());
showFile.Start(); // start the Thread
showFile.Join();
showFile.WaitForExit();
}
catch
{
MessageBox.Show("Could not open the file.", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
Related
We are working on a windows application which caters to an engineering calculation which are essentially very long running. So we are basically trying to keep the calculation module separate and working in a separate worker thread and pass it an Action delegate in method signature which will be invoked to report the calculation progress in the UI. The delegate handler declared in the UI will be updating the UI. We found that while a huge loop is running in the calculation, the UI is not showing the periodic progress and only displaying the final result. If a Thread Sleep for 1 millisecond is introduced in the calculation loop, the UI is getting updated correctly. This is not expected behavior as we are executing the calculation using a separate Task and updating the UI using BeginInvoke calls.
I have created a simple application to demonstrate our approach and code so that it is easier to understand. It is obvious that we are missing something very simple but cannot quite pin it down. Will appreciate any insights.
Thanks for reading.
private void cmdStart_Click(object sender, EventArgs e)
{
txtResultDIsplay.Text = "";
var maxIterations = long.Parse(txtIterationNo.Text.Trim());
var ui = TaskScheduler.FromCurrentSynchronizationContext();
Task<double> calculationTask = Task.Factory.StartNew<double>(
() => SumRootN(maxIterations, UpdateProgress));
var handleResultTask = calculationTask.ContinueWith((t) => DisplayResult(t),
CancellationToken.None, TaskContinuationOptions.OnlyOnRanToCompletion, ui);
}
private void DisplayResult(Task<double> calculationTask)
{
txtResultDIsplay.Text = "Final Calculation Result : " + calculationTask.Result.ToString();
}
private void UpdateProgress(string msg)
{
this.BeginInvoke((MethodInvoker)delegate
{
txtResultDIsplay.Text = msg;
});
}
public double SumRootN(long maxIterations, Action<string> progressUpdateDelegate)
{
int root = 20;
double result = 0;
for (long i = 1; i < maxIterations; i++)
{
Thread.Sleep(1);
result += Math.Exp(Math.Log(i) / root);
progressUpdateDelegate(result.ToString("0.00000"));
}
return result;
}
It is possible you are flooding the UI thread with your progress updates. You need to find a way to prevent lots of updates occurring.
We can solve the problem using tasks!
Task progressTask = null;
private void UpdateProgress(string msg)
{
//only schedule work if the task if not running
if(progressTask == null || progressTask.IsCompleted) //updates will end if there is an exception!
{
//Create a task representing the update
progressTask = Task.Factory.FromAsync<object>(BeginInvoke(new Action(() => txtResultDIsplay.Text = msg)), this.EndInvoke)
.ContinueWith(() => System.Threading.Thread.Sleep(100)); //add a sleep on the end
}
}
Note that locking will not do here as you want to skip the update if there is already an update occurring.
this is my first question in this forum, hope it will not be duplicated somewhere because i have searched for the respons for almost 4 weeks without making any progress.
here is my situation,
im developing an application that need to do a lot of background operation, for that reason i creat 2 BKW, the first one used to load data from a DB and put it inside an observable collection , 'no need to report progress or support cancelation for this one' :
private Boolean loadTestSteps()
{
// Create a background worker thread that don't report progress and does not
// support cancelation
BackgroundWorker wk_LoadTestSteps = new BackgroundWorker();
wk_LoadTestSteps.DoWork += new DoWorkEventHandler(wk_LoadTestSteps_DoWork);
wk_LoadTestSteps.RunWorkerAsync();
return true;
}
observable collection class :
public class clsTestStep : DependencyObject
{
public static DependencyProperty TestStepProperty = DependencyProperty.Register(
"TestStep", typeof(String), typeof(clsTestStep));
public string TestStep
{
get { return (string)GetValue(TestStepProperty); }
set { SetValue(TestStepProperty, value); }
} and so on for the rest of items....
now the main backGround that should do the longer operation and in the same time report the progress to the main UI ,declared like so
private void InitializeBackGroundWork()
{
_wk_StartTest = new BackgroundWorker();
// Create a background worker thread that ReportsProgress &
// SupportsCancellation
// Hook up the appropriate events.
_wk_StartTest.DoWork += new DoWorkEventHandler(_wk_StartTest_DoWork);
_wk_StartTest.ProgressChanged += new ProgressChangedEventHandler
(_wk_StartTest_ProgressChanged);
_wk_StartTest.RunWorkerCompleted += new RunWorkerCompletedEventHandler
(_wk_StartTest_RunWorkerCompleted);
_wk_StartTest.WorkerReportsProgress = true;
_wk_StartTest.WorkerSupportsCancellation = true;
_wk_StartTest.RunWorkerAsync();
}
in the do work events, exactly in the foreach loop i encontered an error saying : you cannot access this object because another thread own it :
void _wk_StartTest_DoWork(object sender, DoWorkEventArgs e)
{
//Loop through each test step and perform Test
foreach (clsTestStep item in _testStep)
{
Thread.Sleep(200);
temp[0] = item.TestStep;
temp[1] = item.Delay.ToString();
temp[2] = item.NumberRepetition.ToString();
temp[3] = item.Mode.ToString();
//Report % of Progress, Test step Name,and the paragraph from Class PerformTest
_wk_StartTest.ReportProgress(counter,
temp[0]);
counter += 1;
_performTest.Fdispatcher(temp, out _paragraph);
//_si.PgBarMax = Convert.ToDouble(_testStep.Count);
}
//Report completion on operation completed
_wk_StartTest.ReportProgress(counter);
}
what im missing here please, because my head is gonna explod from searching !!!
It sounds like your ObservableCollection is created and so owned by an other thread so your _wk_StartTest_DoWork method can't access it.
Where your _testStep variable comes from ?
By the way, in a multithread environment when many thread access the same data you should prefer the use of ConcurrentBag class instead of an ObservableCollection. ConcurrentBag is thread safe.
for the ones that may enconter this kind of problem ^^
finnaly i have found a way to acess class even if its not owned by the current thread here a nice article explaining step by step how to do this here
I need to perform 2 tasks in parallel. One will load data in the GUI, till then I want to run a progress bar continuously in front of user. I tried BackgroundWorker but it is giving me some Thread synchronization error. Can somebody suggest me any other best way of doing same.
Code:
backgroundWorker1 initialization:
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
error coming on following line:
XmlDocumentHierarchy _remoteObj = new XmlDocumentHierarchy(comboBox2.Text, "username", "password");
is:
"Cross-thread operation not valid: Control 'comboBox2' accessed from a thread other than the thread it was created on."
You are trying access comboBox2.Text in thread other than GUI thread (background worker thread). If you using only one property in background worker thread, than you can pass `comboBox2.Text' to background worker method:
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync(comboBox2.Text);
}
In backgroundWorker1_DoWork procedure you can read property in following way:
void backgroundWorker1_DoWork(Object sender, DoWorkEventArgs e)
{
String comboBoxText = (String)e.Argument;
XmlDocumentHierarchy _remoteObj = new XmlDocumentHierarchy(comboBoxText, "username", "password");
}
If you accessing more than one property from GUI controls you can create simple class to pass all necessary data to your background worker method.
If you need to access GUI thread from BackgroundWorker thread, you can easily invoke your methods in the GUI thread like this:
public Form1()
{
InitializeComponent();
Thread thr = new Thread(new ThreadStart(BackGroundThread));
thr.Start();
}
void BackGroundThread()
{
for (int i = 0; i < 100; i++)
{
// The line below will be run in the GUI thread with no synchronization issues
BeginInvoke((Action)delegate { this.Text = "Processed " + i.ToString() + "%"; });
Thread.Sleep(200);
}
}
I have created sample application and implemented threading. basically aim to craete this application is i would like to
If any process(s) are runnig then User Interface should Notify
[DONE]
Handle dynamically created thread with ProgressBar [DONE]
Provide addition functionality to Start, Pause and Stop thread from
available progress list. [NEED YOUR HELP]
Note:- I don't have much knowledge about Threading and Delegates, so please let me know best solution for existing code.
Files and Controls are used:-
Basically three files are used in this demo application
ProgressForm.cs (Window Form)
which conatains Button for creating new progress and Container whic will hold all the created progressbars
ProgressClass.cs
Which contains Dynamic Threading and Delegates to Notify UI without locking or hanging user interface
ProgressControl.cs (User Control)
Which contains
Progressbar (to display process done)
Precent Label (display percentage of completed progress)
Start/Pause button (for play/pause a thread)
Stop button (stop running thread and remove progress from list)
StartTime Label (display process started time)
EndTime label (display time of process completed)
MaxValue Lable (generate random number between 25 to 100)
CODE SNIPPET:-
1. ProgressForm .cs
public partial class ProgressForm : Form
{
Random randomMaxValue = new Random();
public ProgressForm()
{
InitializeComponent();
}
private void btnStart_Click(object sender, EventArgs e)
{
ProgressClass m_clsProcess;
ProgressControl progress = new ProgressControl();
progress.StartedAt = DateTime.Now;
progress.MinValue = 0;
progress.CurrentValue = 0;
progress.MaxValue = randomMaxValue.Next(25, 100);
AddControl(progress);
m_clsProcess = new ProgressClass(progress, this, new ProgressClass.NotifyProgress(DelegateProgress));
m_clsProcess.Start();
}
private void DelegateProgress(int CurrentValue, ProgressControl Progress)
{
ProgressBar p = (ProgressBar)Progress.Controls.Find("pgbPercent", false)[0];
p.Minimum = Progress.MinValue;
p.Value = CurrentValue;
p.Maximum = Progress.MaxValue;
Label percent = (Label)Progress.Controls.Find("lblPercent", false)[0];
percent.Text = string.Format("{0:#00} %", Convert.ToInt16((CurrentValue * 100) / Progress.MaxValue));
Label start = (Label)Progress.Controls.Find("lblStart", false)[0];
start.Text = string.Format("{0:HH:mm:ss}", Progress.StartedAt);
if (CurrentValue == Progress.MaxValue)
{
Label complete = (Label)Progress.Controls.Find("lblComplete", false)[0];
complete.Text = string.Format("{0:HH:mm:ss}", DateTime.Now);
Progress.Status = ProgressControl.ProgressStatus.Completed;
}
Label max = (Label)Progress.Controls.Find("lblMaxValue", false)[0];
max.Text = string.Format("{0:#00}", Progress.MaxValue);
Button btnstartstop = (Button)Progress.Controls.Find("btnStartStop", false)[0];
btnstartstop.Click += new EventHandler(ProgressStartStop);
}
private void AddControl(Control ctl)
{
tableLayoutPnl.RowCount += 1;
tableLayoutPnl.RowStyles.Add(new RowStyle());
ctl.Dock = DockStyle.Fill;
tableLayoutPnl.Controls.Add(ctl, 0, tableLayoutPnl.RowCount - 1);
}
void ProgressStartStop(object sender, EventArgs e)
{
Button btn = sender as Button;
//
//Here i would like to write a code for START / PAUSE thread and update Image acording too.
//
}
}
2. ProgressControl.cs
public partial class ProgressControl : UserControl
{
public enum ProgressStatus
{
Initialize,
Running,
Paused,
Completed
}
public DateTime StartedAt { get; set; }
public DateTime CompletedAt { get; set; }
public int MinValue { get; set; }
public int CurrentValue { get; set; }
public int MaxValue { get; set; }
public ProgressStatus Status { get; set; }
public ProgressControl()
{
InitializeComponent();
this.Status = ProgressStatus.Initialize;
}
}
3. ProgressClass.cs
public class ProgressClass
{
private int ThreadWaitTime = 100;
private ProgressControl m_progress;
private NotifyProgress m_clsNotifyDelegate;
private System.Threading.Thread m_clsThread;
private System.ComponentModel.ISynchronizeInvoke m_clsSynchronizingObject;
public delegate void NotifyProgress(int PercentComplete, ProgressControl Progress);
public ProgressClass(ProgressControl Progress, System.ComponentModel.ISynchronizeInvoke SynchronizingObject, NotifyProgress NotifyDelegate)
{
m_progress = Progress;
m_clsSynchronizingObject = SynchronizingObject;
m_clsNotifyDelegate = NotifyDelegate;
}
public void Start()
{
m_clsThread = new System.Threading.Thread(DoProcess);
m_clsThread.Name = "Background Thread";
m_clsThread.IsBackground = true;
m_progress.Status = ProgressControl.ProgressStatus.Running;
m_clsThread.Start();
}
private void DoProcess()
{
for (int i = m_progress.MinValue; i <= m_progress.MaxValue; i++)
{
NotifyUI(i);
Thread.Sleep(ThreadWaitTime);
}
}
private void NotifyUI(int Value)
{
object[] args = new object[2];
args[0] = Value;
args[1] = m_progress;
m_clsSynchronizingObject.Invoke(m_clsNotifyDelegate, args);
}
}
I am not asking for write whole code instead of provide hint.
I would like to start/pause relevent thread from list, os what should i do for that?
I would like hind in following function:
void ProgressStartStop(object sender, EventArgs e)
{
Button btn = sender as Button;
//Here i would like to write a code for START / PAUSE thread and update Image acording too.
}
UPDATED:
You will want to use a ManualResetEvent or ManualResetEventSlim to create the pause and resume behavior in the thread. The idea is to check the state of the event in the worker thread at safe points. This is done via the WaitOne or Wait methods. If the event is signaled then the calls will return immediately allowing the thread to proceed. If the event is unsignaled then the calls block until the event is signaled via the Set method. So to pause the thread you would call Reset to unsignal the event and to resume the thread you would call Set.
Just remember to place calls to WaitOne or Wait at safe points in the instruction sequence of the worker thread. In other words, do not call these methods inside a lock or something like that. At the beginning or end of a loop is often a good start.
Also, it looks like you use the Invoke method for updating the UI. That is all fine and good, but for simply updating the UI with progress information there is a better option. It is better to publish the progress information to a shared data structure and then have the UI thread pick it up via a timer. For those that monitor my answers I harp about this a lot, I know. But, this strategy has a lot of advantages.
It breaks the tight coupling between the UI and worker threads that Invoke imposes.
It puts the responsibility of updating the UI thread on the UI thread where it should belong anyway.
The UI thread gets to dictate when and how often the update should take place.
There is no risk of the UI message pump being overrun as would be the case with the marshaling techniques initiated by the worker thread.
The worker thread does not have to wait for an acknowledgement that the update was performed before proceeding with its next steps (ie. you get more throughput on both the UI and worker threads).
It avoids the subtle race conditions that can occur when trying to gracefully end the worker thread.
It is more efficient since Invoke is an expensive operation.
Update:
Here is the general idea regarding the changes that could be made to ProgressStartStop.
private Dictionary<int, ThreadInfo> threads = new Dictionary<int, ThreadInfo>();
void ProgressStartStop(object sender, EventArgs e)
{
Button button = sender as Button;
int index = GetThreadIndexFromButton(button);
if (!threads.ContainsKey(index))
{
// The thread has not been started yet so do it now.
var thread = new Thread(RunThread);
thread.Start();
var mres = new ManualResetEventSlim(true);
var info = new ThreadInfo { Thread = thread, ProceedSignal = mres };
threads.Add(index, info);
// Change the button image here.
}
else
{
ThreadInfo info = threads[index];
if (info.ProceedSignal.Wait(0))
{
// The event is signaled which means the thread is running. Pause it.
info.ProceedSignal.Reset();
// Change the button image here.
}
else
{
// The event is unsignaled which means the thread is paused. Resume it.
info.ProceedSignal.Set();
// Change the button image here.
}
}
}
private class ThreadInfo
{
Thread Thread { get; set; }
ManualResetEventSlim ProceedSignal { get; set; }
}
It is generally considered bad practice to Suspend threads (though it is possible). The right way to pause and terminate threads is through the cooperation with the job that the thread is doing. The job should check a variable in a loop, and pause or exit accordingly. The controlling program can set that variable, and if you need feedback the background thread can call a notification method before exiting or sleeping.
I need in wpf app check messages on server. I have own method which load messages on server-LoadRp().
I would like to create some kind of listener which would check, every 3 seconds whether on the server are not new messages.
I call method for loading messages on dispatcher timer tick event, it is suitable? Any another solution. It’s possible call timer in another thread in wpf?
Code is here:
public MessangerWindow(PokecCommands pokecCmd)
{
InitializeComponent();
PokecCmd = pokecCmd;
_friendsData = PokecCmd.LoadFriends();
friendsListBox.DataContext = _friendsData;
_dispatcherTimer = new DispatcherTimer();
_dispatcherTimer.Tick+=new EventHandler(DispatcherTimer_Tick);
_dispatcherTimer.Interval = new TimeSpan(0,0,3);
_dispatcherTimer.Start();
}
private void DispatcherTimer_Tick(object sender, EventArgs eventArgs)
{
try
{
//try load new message from sever
RP message = PokecCmd.LoadRp();
//arived message
if (message != null)
{
//exist window
if (_chatWindows.ContainsKey(message.Nick))
{
_chatWindows[message.Nick].Show();
}
{
//create new Window
var chatWindow = new ChatWindow(PokecCmd, message);
_chatWindows.Add(message.Nick, chatWindow);
chatWindow.Show();
}
}
}
catch (Exception ex)
{
//MessageBox.Show(ex.Message);
}
}
What is suitable to use:
Dispatcher with no background threads
Dispatcher with background threads
Multiple Threads
If you are ok with locking up your UI for the time it takes to check on the server, using a DispatcherTimer the way you are doing it will work fine.
If checking for new messages could take more than a few milliseconds and you want your UI to be responsive while it checks, you should use multiple threads. In that case, once the new data had arrived you would use Dispatcher.Invoke to display it.
Your code in the thread that checks for messages might look like this:
//try load new message from sever
RP message = PokecCmd.LoadRp();
//arived message
if( message != null )
Dispatcher.Invoke(DispatcherPriority.Send, new Action(() =>
{
//exist window
if (_chatWindows.ContainsKey(message.Nick))
{
_chatWindows[message.Nick].Show();
}
{
//create new Window
var chatWindow = new ChatWindow(PokecCmd, message);
_chatWindows.Add(message.Nick, chatWindow);
chatWindow.Show();
}
}
);