I have button on my window. After user clickes the button i want my application to animate loading label (with rotationg it), during the other thread gets some data from database. After loading data from DB animation must end. The task seems simple , but it doesn't work.
The problem is that animation whatever i do animation starts only after loading from the database when it is not needed.
Help please. Here some code:
private void LoginButtonClick(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(new ThreadStart(
delegate()
{
DispatcherOperation dispatcherOp =
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(
delegate()
{
var da = new DoubleAnimation(360, 0, new Duration(TimeSpan.FromSeconds(1)));
var rt = new RotateTransform();
loadingLabel.RenderTransform = rt;
loadingLabel.RenderTransformOrigin = new Point(0.5, 0.5);
da.RepeatBehavior = RepeatBehavior.Forever;
rt.BeginAnimation(RotateTransform.AngleProperty, da);
}));
dispatcherOp.Completed += new EventHandler(DispatcherOpCompleted);
}));
thread.Start();
}
void DispatcherOpCompleted(object sender, EventArgs e)
{
//Loading From Database
}
The Dispatcher.Completed event is executed on the main UI thread. Your worker thread is just queueing the dispatcher operation and exiting. Instead of creating a thread that starts the animation and then doing your database loading in the Completed handler, just start your animation in the main thread, and then create a worker thread to do the database loading.
private void LoginButtonClick(object sender, RoutedEventArgs e)
{
var da = new DoubleAnimation(360, 0, new Duration(TimeSpan.FromSeconds(1)));
var rt = new RotateTransform();
loadingLabel.RenderTransform = rt;
loadingLabel.RenderTransformOrigin = new Point(0.5, 0.5);
da.RepeatBehavior = RepeatBehavior.Forever;
rt.BeginAnimation(RotateTransform.AngleProperty, da);
Thread thread = new Thread(new ThreadStart(LoadData));
thread.Start();
}
void LoadData()
{
//Loading From Database
// Use a Dispatch.BeginInvoke here to stop the animation
// and do any other UI updates that use the results of the database load
}
Related
I have created a thread, but the thread pauses the main process after I start it. The thread loads some images from google, but when the internet connection is lost the user interface is unusable.
This is the thread:
string searchWord = "car photo";
PhotoSearchThread = new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
if (!string.IsNullOrWhiteSpace(searchWord))
{
string html = GetHtmlCode(searchWord);
SearchedImagesUrls = GetUrls(html);
this.Dispatcher.Invoke(() =>
{
if (SearchedImagesUrls.Count > 0)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(SearchedImagesUrls[0]);
image.EndInit();
SelectPhotoImage.Source = image;
}
});
}
});
PhotoSearchThread.Start();
Well threads should run simultaneously, then why this thread is interrupting other threads?
Invoke is used to run code on the main or UI thread. Specifically for updating UI elements as those can only be updated by that thread. Currently you have code that loads the image in the Invoke. Instead you should only put the part of the code that updates the UI inside of the Invoke.
PhotoSearchThread = new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
if (!string.IsNullOrWhiteSpace(searchWord))
{
string html = GetHtmlCode(searchWord);
SearchedImagesUrls = GetUrls(html);
if (SearchedImagesUrls.Count > 0)
{
BitmapImage image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(SearchedImagesUrls[0]);
image.EndInit();
this.Dispatcher.Invoke(() =>
{
SelectPhotoImage.Source = image;
});
}
}
});
I found the solution:
1.Add following using: using System.ComponentModel;
2.Declare background worker:
private readonly BackgroundWorker worker = new BackgroundWorker();
3.Subscribe to events:
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
4.Implement two methods:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
}
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//update ui once worker complete his work
}
5.Run worker async whenever your need.
worker.RunWorkerAsync();
Also if you want to report process progress you should subscribe to ProgressChanged event and use ReportProgress(Int32) in DoWork method to raise an event. Also set following: worker.WorkerReportsProgress = true; (thanks to #zagy)
source: How to use WPF Background Worker
Hope this help.
I have looked everywhere for the answer and I thought it would be simple to find but apparently not. I've heard about invoke but I have no idea how to use it or what it is.
Here is my code:
public void Thread1(object sender, EventArgs e)
{
this.button1.Enabled = false;
this.textBox2.Clear();
this.textBox3.Clear();
this.textBox4.Clear();
this.textBox6.Text = "£" + "0";
//Generate 3 random numbers
Stopwatch timer = new Stopwatch();
timer.Start();
this.Refresh();
//This is only part of this function
}
private void button1_Click(object sender, EventArgs e)
{
ThreadStart threadStart = new ThreadStart(() => Thread1(sender, e));
Thread newThread = new Thread(threadStart);
newThread.Start();
}
In background threads, use Invoke() on WinForms components to execute code on the UI thread:
this.Invoke( () => {
this.button1.Enabled = true;
this.textBox2.Text = "whatever";
} );
Documentation: https://msdn.microsoft.com/en-us/library/a1hetckb.aspx
my goal here is to play some animation and make the UI wait until it is finished to execute some other code. I have a class like this:
class Game
{
private string currentClickedCommand;
private string currentClickedSlot;
private GamePage gamePage;// gamePage na kojem se vrsi radnja
private string[] chosenCommands;// ovo je string array u kojem su sadrzana imena odabranih komandi
private int chapterNumber;
// Kontekst stranice koristen za update objekata UI
// Za kontrolu animacija
public static bool isAnimationComplete;
private BitmapImage currentImage;
public Game(GamePage gp, int chapterNum)
{
currentClickedCommand = null;
currentClickedSlot = null;
gamePage = gp;
chapterNumber = chapterNum;
isAnimationComplete = false;
chosenCommands = new string[5];
gamePage.characterRectangle.Visibility = System.Windows.Visibility.Collapsed;
Thread AnimationThread = new Thread(playIntroAnimation);
AnimationThread.Start();
while (!isAnimationComplete) { }
loadCommands();
loadSlots();
}
public void playIntroAnimation() {
// Pretpostavljam da na osnovu baze znam koja je animacija za ovaj chapter navedena kao uvodna
Debug.WriteLine("on other thread playing animation");
// Sakrivam gridove komandi i ostalog
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
gamePage.CommandGrid.Visibility = System.Windows.Visibility.Collapsed;
gamePage.SlotGrid.Visibility = System.Windows.Visibility.Collapsed;
MessageBox.Show("Prvi dispatch");
Debug.WriteLine("prvi dispatch");
}));
// Ucitavam pozadinsku sliku preko canvasa cijelog
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
ImageBrush bgImage = new ImageBrush();
Uri imgSource = new Uri(#"Assets/Chapter backgrounds/chapter_1_introduction.png", UriKind.Relative);
BitmapImage img = new BitmapImage(imgSource);
bgImage.ImageSource = img;
gamePage.mainCanvas.Background = bgImage;
MessageBox.Show("Drugi dispatch");
}));
// Kreiram parametre animacije
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
Uri sheetSource = new Uri(#"Assets/Sheets/test_wave_sheet.png", UriKind.Relative);
currentImage = new BitmapImage(sheetSource);
currentImage.ImageOpened += new EventHandler<RoutedEventArgs>(setAnimationParams);
Image i = new Image();
i.Source = currentImage;
MessageBox.Show("Treci dispatch");
}));
// Odredjujem lika i animiram ga -- visinu i sirinu znam iz baze(kad dobavim sheet i ostalo imam gotove parametere
// sada moram hardkodirat radi testa
Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
{
gamePage.characterRectangle.Visibility = System.Windows.Visibility.Visible;
Canvas.SetLeft(gamePage.characterRectangle, gamePage.mainCanvas.ActualWidth / 4.0);
Canvas.SetTop(gamePage.characterRectangle, gamePage.mainCanvas.ActualHeight / 5.0);
MessageBox.Show("Cetvrti dispatch");
}));
}
private void setAnimationParams(object sender, RoutedEventArgs e)
{
Deployment.Current.Dispatcher.BeginInvoke(() => {
AnimationClasses.AnimationParams parameters = new AnimationClasses.AnimationParams(currentImage, 462, 438, 7, true, currentImage.PixelWidth, false, 2);
new AnimationClasses.Animation().Animate(gamePage.characterRectangle, parameters);
});
}
As you can see, I've been trying to do this using Deployment.Current... etc. but no luck. I have been debugging but it simply never gets into executing this code and nothing is happening.
I create an instance of Game on Page like this:
public GamePage()
{
InitializeComponent();
game = new Game(this, Convert.ToInt32(PhoneApplicationService.Current.State["chapter"]));
}
I have a static attribute(isAnimationComplete) which is set elsewhere to true, when the animation is complete. The 2 commands after(loadCommands, loadSlots) need to wait for this to finish and then execute.
Thank you!
You simply have a deadlock.
The UI thread is calling the constructor of the Game class. Inside, you're starting a background thread, then waiting in a loop:
while (!isAnimationComplete) { }
Which means that the UI thread won't be available for other operations until you set the isAnimationComplete flag to true. Which will never happen since you're doing that when the animations are done executing... Animations that are waiting for the UI thread to be free.
We have developed an application in WPF. This application should be refreshed on particular time interval, so we have used the the "DispatchTimer" control.
The following code is used to do this refresh process.
private void PageReferesh()
{
try
{
DispatcherTimer dispatcherTimer = App.dirctedWorkTaskTimer;
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 10);
dispatcherTimer.Start();
}
catch (Exception ex)
{
// throw new Exception(ex.Message, ex);
}
}
protected void dispatcherTimer_Tick(object sender, EventArgs e)
{
GetOperatorDetails();
GetCustomers();
}
It is working fine in the development environment. When we change to "Release Mode" and deploy to another folder on the same system, it doesn't work.
Actually, the same is working in "Release" mode, running from he VS2012 IDE. it does not work while copy the "Release" folder (bin/release) to some other file path.
I believe the DispatcherTimer you have does not run on the main UI dispatcher.
if not what is the DispatcherPriority (It should not be ApplicationIdle either) that you have set when creating the DispatcherTimer instance ?
Based on that if the Timer runs on the main UI dispatcher with a less priority it might no run to completion as expected depending on other higher priority operation that might be lined up in the UI dispatcher's pumping stack.
I recommend something like below
Dispatcher _uiRefreshDispatcher;
//Make sure calling thread is UI thread
Dispatcher _uiDispatcher = Dispatcher.CurrentDispatcher;
private void PageRefresh()
{
var waitForRefreshWatchThread = new ManualResetEventSlim(false);
var messageThread = new Thread(() =>
{
_uiRefreshDispatcher = Dispatcher.CurrentDispatcher;
waitForRefreshWatchThread.Set();
Dispatcher.Run();
waitForRefreshWatchThread.Dispose();
}) { Name = "RefresherThread", IsBackground = true };
messageThread.SetApartmentState(ApartmentState.STA);
messageThread.Start();
waitForRefreshWatchThread.Wait();
var refreshTimer = new DispatcherTimer(DispatcherPriority.Send, _uiRefreshDispatcher);
refreshTimer.Tick += OnRefreshTimeElapsed;
refreshTimer.Interval = TimeSpan.FromSeconds(10);
}
private void OnRefreshTimeElapsed(object sender, EventArgs e)
{
if (!_uiDispatcher.CheckAccess())
{
_uiDispatcher.BeginInvoke((Action<object, EventArgs>)OnRefreshTimeElapsed, sender, e);
}
else
{
//Update UI here
}
}
Im trying to populate a datagridview from a query of sql but it takes a long time , what im trying to do is show a .gif "loading" meanwhile is populating the grid, im using threads but the .gif freezes , and if I use the CheckForIllegalCrossThreadCalls = false; the datagridview doesnt load the scroll bar acts weird. here is my code
delegate void CambiarProgresoDelegado();
BUTTON CLICK
private void btn_busca_Click(object sender, EventArgs e)
{
pictureBox1.Visible = true;
thread= new Thread(new ThreadStart(ejecuta_sql));
thread.Start();
}
method
private void ejecuta_sql()
{
if (this.InvokeRequired)
{
CambiarProgresoDelegado delegado = new CambiarProgresoDelegado(ejecuta_sql);
object[] parametros = new object[] { };
this.Invoke(delegado, parametros);
}
else
{
myConnection.Open();
SqlCommand sql_command2;
DataSet dt2 = new DataSet();
sql_command2 = new SqlCommand("zzhoy", myConnection);
sql_command2.CommandType = CommandType.StoredProcedure;
sql_command2.Parameters.AddWithValue("#FechaIni", dateTimePicker1.Value.ToShortDateString());
sql_command2.Parameters.AddWithValue("#FechaFin", dateTimePicker2.Value.ToShortDateString());
SqlDataAdapter da2 = new SqlDataAdapter(sql_command2);
da2.Fill(dt2, "tbl1");
grid_detalle.DataSource = dt2.Tables[0];
myConnection.Close();
pictureBox1.Visible = false;
}
and the .gif freezes until the thread finish his job.
You created a thread but then immediately made the code switch back to the main UI thread with Invoke(), negating any benefits of making the thread in the first place.
Run the query on the other thread, then Invoke() just the part that updates the UI:
private string FechaIni;
private string FechaFin;
private void btn_busca_Click(object sender, EventArgs e)
{
btn_busca.Enabled = false;
pictureBox1.Visible = true;
FechaIni = dateTimePicker1.Value.ToShortDateString();
FechaFin = dateTimePicker2.Value.ToShortDateString();
thread = new Thread(new ThreadStart(ejecuta_sql));
thread.Start();
}
private void ejecuta_sql()
{
myConnection.Open();
SqlCommand sql_command2;
DataSet dt2 = new DataSet();
sql_command2 = new SqlCommand("zzhoy", myConnection);
sql_command2.CommandType = CommandType.StoredProcedure;
sql_command2.Parameters.AddWithValue("#FechaIni", FechaIni);
sql_command2.Parameters.AddWithValue("#FechaFin", FechaFin);
SqlDataAdapter da2 = new SqlDataAdapter(sql_command2);
da2.Fill(dt2, "tbl1");
myConnection.Close();
this.Invoke((MethodInvoker)delegate {
grid_detalle.DataSource = dt2.Tables[0];
pictureBox1.Visible = false;
btn_busca.Enabled = true;
});
}
You may consider changing your approach, especially if you are doing a lot of GUI updates from your background threads. Reasons:
UI update takes time and it slows down background processing as you have to lock
too many updates coming from the background threads will overwhelm the UI and might cause freezes.
you probably don't want to update GUI every millisecond or so
What I prefer is to do polling the background thread data instead. Set up GUI timer for say 300ms, then check if there is any data ready to be updated, then do quick update with proper locking.
Here is code example:
private string text = "";
private object lockObject = new object();
private void MyThread()
{
while (true)
{
lock (lockObject)
{
// That can be any code that calculates text variable,
// I'm using DateTime for demonstration:
text = DateTime.Now.ToString();
}
}
}
private void timer_Tick(object sender, EventArgs e)
{
lock(lockObject)
{
label.Text = text;
}
}
Note, that while the text variable is updated very frequently the GUI still stays responsive. If, instead, you update GUI on each "text" change, your system will freeze.