I have a folder full of image files whose names I've loaded into a String array. I have a button to display them. This works: (XAML fragment):
<StackPanel >
<Image Name="ImageViewer" Height="400" Width="400" />
</StackPanel>
C# fragment:
void DisplayNextRandomImage()
{
Random random = new Random();
int num = random.Next(_FileCount); // pick a random file
string selectedFileName = _sRoot + "\\" + _sFiles[num];
BitmapImage bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.UriSource = new Uri(selectedFileName);
bitmap.EndInit();
ImageViewer.Source = bitmap;
}
// "Next" button handler
private void buttonNext_Click(object sender, RoutedEventArgs e)
{
DisplayNextRandomImage();
}
When I click on the Next button it displays a new image. If I click on it again it displays another image. But what I really want is to display a sequence of random images with a 10 second wait between each one. But if I change the Next button handler to this :
private void buttonNext_Click(object sender, RoutedEventArgs e)
{
DisplayNextRandomImage();
System.Threading.Thread.Sleep(10000);
DisplayNextRandomImage();
System.Threading.Thread.Sleep(10000);
DisplayNextRandomImage();
}
It doesn't display anything.
Is there something I need to call or run in between the sleeps to cause some thread to wake up and display the image? What am I doing wrong?
private void Button_Click(object sender, RoutedEventArgs e)
{
DisplayNextRandomImage();
Dispatcher disp = ImageViewer.Dispatcher;
DispatcherTimer t = new DispatcherTimer(TimeSpan.FromSeconds(10), DispatcherPriority.Normal, timer_Tick , disp);
t.Start();
}
void timer_Tick(object sender, EventArgs e)
{
DisplayNextRandomImage();
}
You can also use as shorthand notation :
DispatcherTimer t = new DispatcherTimer(TimeSpan.FromSeconds(10), DispatcherPriority.Normal, (s1,e1)=>{DisplayNextRandomImage();} , disp);
The traditional way of doing things is to use a DispatcherTimer, the callback happens on the main thread so you won't have any problems manipulating GUI elements.
A more recent option is to use asynchronous programming, which in this case can be done by simply changing your click handler to this:
private async void buttonNext_Click(object sender, RoutedEventArgs e)
{
while (true)
{
DisplayNextRandomImage();
await Task.Delay(10000);
}
}
Neither method handles the case of the user clicking the button twice, but that should be enough to get you started.
Make a Queue<Storyboard>. And add all the image list ,load the first image and then after 10s , you can dequeue the next storyboard and load the image, when that finishes it dequeues the next one and starts that, and so on.
Related
UPDATE:
As mentioned in the comments section the problem was solved, but I do not understand why my way of implementation was wrong.
I have this situation:
I have a device which can be triggered with an event in my WPF project.
This event pulls Data from my device at a polling rate of 1ms. I want to process
the data in different threads.
My approach was, to start a backgroundworker which registers the device event (I read events run on the thread they are called from). In the device event itself the data is saved to an object, which is declared in my form. After that the labels in the WPF form a are refreshed with a Invoke Method.
This happens until someone presses cancel on a button in my form, which unregisters the device event and stops the thread.
Here is some code I use:
Declaration in my Main Window:
public partial class MainWindow : Window
{
private BackgroundWorker worker = new BackgroundWorker();
private MeasureObject mObject = new MeasureObject();
... }
This is my initialization:
public MainWindow()
{
InitializeComponent();
this.worker.WorkerSupportsCancellation = true;
this.worker.DoWork += worker_DoWork;
this.worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
If this button is press i run my background worker:
private void btnStartMeasure_Click(object sender, RoutedEventArgs e)
{
this.worker.RunWorkerAsync();
}
Here I register my event for the device. It should now run on my worker thread. I tried to declare the event itself here too, but it did not work, so I placed it in my main windows.
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
this.myController.ControlCenter.Diagnostics.NewDiagPacketArrived += new EventHandler<NewDiagPacketArrivedEventArgs>(Diagnostics_NewDiagPacketArrived);
// run all background tasks here
}
This is not needed and empty. The worker is only cancelled if the user sets it on cancel.
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
}
This event is triggered in my Window and calls two functions, it should run on the backgroundworker if I am correct.
private void Diagnostics_NewDiagPacketArrived(object sender, NewDiagPacketArrivedEventArgs e)
{
try
{
Measure(e);
this.Dispatcher.BeginInvoke( new Action(() => { SetStates(e); }),System.Windows.Threading.DispatcherPriority.Input);
}
catch
{
}
}
Measure gets the e Object from the device and saves it to a Dataobject i created
private void Measure(NewDiagPacketArrivedEventArgs e)
{
lock(this.mObject)
{
this.mObject.ID = this.list.Count;
....
this.list.Add(this.mObject);
}
}
SetStates refreshed the GUI
private void SetStates(NewDiagPacketArrivedEventArgs e)
{
lock(this.mObject)
{
this.lblID.Content = this.mObject.ID;
}
}
The problem with my code is if I cancel the event and the thread with this code:
private void btnStopMeasure_Click(object sender, RoutedEventArgs e)
{
this.myController.ControlCenter.Diagnostics.NewDiagPacketArrived -= Diagnostics_NewDiagPacketArrived;
this.worker.CancelAsync();
}
And try to get the list where I added my objects, all objects have the same ID's and values. It seems like as soon as I unregister the event or press the stop measure button, all mObjects in my list get overwritten with the mObject at the time when I unregister the event.
so my list looks like this:
list[0].ID = 1
list[1].ID = 1
list[2].ID = 1
rather than this:
list[0].ID = 1
list[1].ID = 2
list[2].ID = 3
Maybe you can help?
Your problem is that you are not creating a new instance of the mObject - you only create one of them here:
private MeasureObject mObject = new MeasureObject();
Your code then adds the SAME object to the list, and updates that. You need to make a new object each time and put it in the list.
this.mObject.ID = this.list.Count;
....
this.list.Add(this.mObject);
Currently your list is a list of the same object.
I want to show "Loading.., please wait" gif by getting content from web.
I have tried the following code, but Picturebox opening too late.
private void buttonStart_Click(object sender, EventArgs e)
{
pictureBox1.Visible = true;
webSatList = new WebSatelliteList(this, XMLSatList, name);
webSatList.LoadTPList();
TPListToBeAdded = webSatList.GetTPListToBeAdded();
TPListToBeRemoved = webSatList.GetTPListToBeRemoved();
drawTPListGridView(TPListToBeAdded, TPListToBeRemoved);
}
public void drawTPListGridView(List<TPInfo> TPListToBeAdded, List<TPInfo> TPListToBeRemoved)
{
pictureBox1.Visible = false;
//draw TP List ..
}
Picturebox is openning after this line:
"TPListToBeRemoved = webSatList.GetTPListToBeRemoved();"
I have tried to fix this problem by using backgroundworker (the following code) and the same problem has been seen. Also, I have used the popup form instead of PictureBox nothing has changed.
private void buttonStart_Click(object sender, EventArgs e)
{
pictureBox1.Visible = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
webSatList = new WebSatelliteList(this, XMLSatList, name);
webSatList.LoadTPList();
TPListToBeAdded = webSatList.GetTPListToBeAdded();
TPListToBeRemoved = webSatList.GetTPListToBeRemoved();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
drawTPListGridView(TPListToBeAdded, TPListToBeRemoved);
}
public void drawTPListGridView(List<TPInfo> TPListToBeAdded, List<TPInfo> TPListToBeRemoved)
{
pictureBox1.Visible = false;
//draw TP List ..
}
How can i fix this problem? Any help would be appreciated.
Not entirely sure what you're trying to do here, but chances are you want to implement the async / await pattern.
Mark your button press as async
private async void buttonStart_Click(object sender, EventArgs e)
For anything that you need to wait for should then be awaited and it will keep your form redrawing so it doesn't freeze up. For example, something like:
await Task.Run(() => loadPictureBox());
Or you could make your loadpicturebox method asynchronous by giving it a signature of async Task.
The problem you're likely having is that the form will not update or refresh itself until the click method has exited. This means if you first make it display a loading image, and then load the next image in the same method that the form will freeze up until both operations have finished and the method has exited.
The async await pattern means that while it's doing some processing or whatever, let windows carry on drawing the form and handling actions on it.
I am new to Windows Phone development and I am trying to do something which I believe is quite simple: I have a page, with a button and a textBlock. I would like that, whenever the button is pressed, the textBlock's text change to "Bazinga!" for a few seconds and then revert to its previous value.
I have tried the code below but it does not work (I suppose because the textBlock's display is not refreshed while still in the Button_Click call).
After looking up a few keywords, I saw this: WPF not updating textbox while in progress
This tells me I must explicitly call the Dispatcher's Invoke method... but I only see a BeginInvoke() method (I guess this is a specificity of Windows Phone) and my few attempts at getting it right have been unlucky.
Thanks for any help you can
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Function();
}
private void Function()
{
string text = this.TextBlock1.Text;
DateTime until = DateTime.Now.AddSeconds(5.0);
this.TextBlock1.Text = "Bazinga!";
while (DateTime.Now < until)
{
// Do nothing
}
this.TextBlock1.Text = text;
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
string text = TextBlock1.Text;
TextBlock1.Text = "Bazinga!";
await Task.Delay(5000);
TextBlock1.Text = text;
}
Lets say I have Task 1:
private void Task1()
{
//Here is some Code, could be any "longer" Task -
//For Example: Grab all words from a .txt File and fill in a List<String>
}
Then I have an other Task 2:
private void Task2(string word)
{
//So lets say theres a Label on my WinForm..
//Now While Task1 is grabbing the words, Task2 should fill a Label
//with the added 'word' (parameter) - (Task2 will be called from Task1
}
Actually I don't know how to make this possible, or whats the best way. On the UI I should be able to see the Label.Text changing (every word).. So I need to make a second Thread? How could I do this? Maybe someone could help me, cheers
UPDATE:
I tried it now with the Backgroundworker, but something seems to be false.. its actually not working, nothing happens on the form
Code:
public void CreateAndSaveAMatch(DateTime date) //That method is being called several times
{
//HERE IS CODE, WHICH CREATES AND SAVES A MATCH
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync(date);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
backgroundWorker1.ReportProgress(0, Convert.ToDateTime(e.Argument).ToShortDateString());
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = (string)e.UserState; //here on the Label I would like to show the Date
}
Ok, try this. This is a simple example that will show you how to solve your problem using BackgroundWorker. Also note that there are many other solutions. To use this example create a Form in a new project that only has a button and a label. Also note that this is a supplement of the other answers which were correct.
public partial class Form1 : Form
{
BackgroundWorker createAndSaveAMatchBGW;
public Form1()
{
InitializeComponent();
createAndSaveAMatchBGW = new BackgroundWorker();
createAndSaveAMatchBGW.DoWork += new DoWorkEventHandler(createAndSaveAMatchBGW_DoWork);
createAndSaveAMatchBGW.ProgressChanged += new ProgressChangedEventHandler(createAndSaveAMatchBGW_ProgressChanged);
createAndSaveAMatchBGW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(createAndSaveAMatchBGW_RunWorkerCompleted);
createAndSaveAMatchBGW.WorkerReportsProgress = true;
}
private void button1_Click(object sender, EventArgs e)
{
createAndSaveAMatchBGW.RunWorkerAsync(DateTime.Now);
}
void createAndSaveAMatchBGW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("BackgroundWorker finished");
}
void createAndSaveAMatchBGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = ((DateTime)e.UserState).ToString("ss");
}
void createAndSaveAMatchBGW_DoWork(object sender, DoWorkEventArgs e)
{
//BackgroundWorker does something for a 10 seconds, each second it Reports
BackgroundWorker bgw = (BackgroundWorker)sender;
DateTime dt = (DateTime) e.Argument;
for (int i = 0; i < 10; i++)
{
Thread.Sleep(1000);
dt = dt.AddSeconds(1);
bgw.ReportProgress(0, dt);
}
}
}
And if you report from CreateAndSave... method only once per its execution, then you can use this code:
BackgroundWorker createAndSaveAMatchBGW;
public Form1()
{
InitializeComponent();
createAndSaveAMatchBGW = new BackgroundWorker();
createAndSaveAMatchBGW.DoWork += new DoWorkEventHandler(createAndSaveAMatchBGW_DoWork);
createAndSaveAMatchBGW.RunWorkerCompleted += new RunWorkerCompletedEventHandler(createAndSaveAMatchBGW_RunWorkerCompleted);
}
private void button1_Click(object sender, EventArgs e)
{
createAndSaveAMatchBGW.RunWorkerAsync(DateTime.Now);
}
void createAndSaveAMatchBGW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
label1.Text = ((DateTime)e.Result).ToString();
}
void createAndSaveAMatchBGW_DoWork(object sender, DoWorkEventArgs e)
{
DateTime dt = (DateTime) e.Argument;
//you do something with your DateTime
dt = dt.AddDays(10);
e.Result = dt;
}
Use BackgroundWorker for reporting progress from first task. Drag this component from toolbox to your form, and subscribe to DoWork and ProgressChanged events. Also set property WorkerReportsProgress to true. Then start you first task asynchronously:
// this will execute code in `DoWork` event handler
backgroundWorker1.RunWorkerAsync();
Next - use userState object to pass processed words:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// grab words in a loop and report progress
backgroundWorker1.ReportProgress(0, word);
}
And last step - update label in ProgressChanged event handler
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text += (string)e.UserState; // this is your grabbed word
}
The simplest way to achieve this kind of thing is using BackgroundWorker.
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
BackgroundWorker automatically handles thread marshalling and provides events that allow you to update the UI. The event handlers run on the UI thread.
The things you do in Task1 could be moved into a BackgroundWorker, and the updates to the UI that you propose to do in Task2 can actually be in response to progress events from BackgroundWorker.
ProgressChangedEventArgs provides for user-defined data that could hold the current word.
However, Winforms (and indeed pretty much any UI) will not be able to keep up with a separate CPU thread just loading words from a file if you intend to show every word you load.
Task1 could be started on a separate thread.
You wouldn't actually need a Task2 unless there was some complex logic being performed to update the TextBox. You you really need to do is use TextBox.Invoke() to invoke the update on the UI Thread from Task1.
I am running a long process as a background process as the process makes my UI unresponsive.
Now the issue is that, while the process is running as a background process, I want to display a wait cursor. I tried using dispatcher to update the cursor, but it does not work.
My process runs on button click:
private void btnStartAsyncOperation_Click(object sender, EventArgs e)
{
backgroundworkerprocess.RunWorkerAsync();
}
In the backgroundworkerprocess event:
void backgroundworkerprocess_DoWork(object sender, DoWorkEventArgs e)
{
this.dispatcher.invoke(DispatcherPriority.Background,
new action((delegate) {
this.cursor = cursors.wait
})
);
}
I was hoping this would cause a wait cursor to be displayed anywhere on the form, but it's only showing up when the mouse is over the button.
private void btnStartAsyncOperation_Click(object sender, EventArgs e)
{
this.Cursor = Cursors.WaitCursor;
backgroundworkerprocess.RunWorkerAsync();
}
void backgroundworkerprocess_DoWork(object sender, DoWorkEventArgs e)
{
// do work
}
Then just simply change the Cursor back in the RunWorkerCompleted event.
I dont think people understand you.
try this, sending it to Dispatcher:
Window.GetWindow(this).Cursor = Cursors.WaitCursor;