tags:

views:

45

answers:

2

Hi,

I've a window that shows a 'working' animation when another thread is running. The window shows and I can see the progress bar but the animation is frozen. The code runs on a ViewModel, and the dispatcher is created in the constructor:

_dispatcher = Dispatcher.CurrentDispatcher;

The code to create the animation and run the process is as follows:

Working wrk;    
protected void Search()
{
  ImplementSearch();

  wrk = new Working();
  wrk.Owner = (MainWindow)App.Current.MainWindow;
  wrk.WindowStartupLocation = WindowStartupLocation.CenterOwner;
  wrk.HeadingMessage = "Searching...";
  wrk.UpdateMessage = "Running your search";
  wrk.ShowDialog();      
}

void ImplementSearch()
{
  System.Threading.Thread thread = new System.Threading.Thread(
    new System.Threading.ThreadStart(
      delegate()
      {
        System.Windows.Threading.DispatcherOperation
          dispatcherOp = _dispatcher.BeginInvoke(
          System.Windows.Threading.DispatcherPriority.Normal,
          new Action(
            delegate()
            {                  
              ResetSearch();

              string ret = _searchlogic.PerformSearch(SearchTerm, ref _matchingobjects, TypeOfFilter());
              if (ret != null)
                SearchMessage = ret;

              if (_matchingobjects.Count > 0)
              {
                DataRow row;
                foreach (SearchLogicMatchingObjects item in _matchingobjects)
                {
                  row = _dt.NewRow();
                  row["table"] = item.Table;
                  row["pk"] = item.PK;
                  _dt.Rows.Add(row);
                }

                SelectCurrent();
              }          
            }
        ));

        dispatcherOp.Completed += new EventHandler(dispatcherOp_Completed);
      }
  ));

  thread.Start();
}

void dispatcherOp_Completed(object sender, EventArgs e)
{
  wrk.Close();
}

I can't figure out why the animation stops? Can anyone help? Thanks

A: 

Your thread does nothing useful - by using _dispatcher.BeginInvoke to run your search you are effectively executing the search on the UI thread, which blocks your animation. Use dispatcher from your background thread only for operations that manipulate UI controls or that cause PropertyChanged events to be fired.

Bojan Resnik
A: 

I think you want to do the actual work on the background thread, not marshal everything to the UI thread, which is what BeginInvoke does! By doing everything on the UI thread with BeginInvoke, your animation won't run.

Working wrk;     
protected void Search() 
{ 
  ImplementSearch(); 

  wrk = new Working(); 
  wrk.Owner = (MainWindow)App.Current.MainWindow; 
  wrk.WindowStartupLocation = WindowStartupLocation.CenterOwner; 
  wrk.HeadingMessage = "Searching..."; 
  wrk.UpdateMessage = "Running your search"; 
  wrk.ShowDialog();       
} 

void ImplementSearch() 
{ 
    Thread thread = new Thread(new ThreadStart( 
      delegate() 
      { 
          // Call to function which changes UI - marshal to UI thread.
          _dispatcher.BeginInvoke((Action)(() => ResetSearch()));

          string ret = _searchlogic.PerformSearch(SearchTerm, ref _matchingobjects, TypeOfFilter()); 

          if (ret != null) 
          {
              // Call to function which changes UI - marshal to UI thread.
              _dispatcher.BeginInvoke((Action<string>)((r) => SearchMessage = r), ret);
          }

          if (_matchingobjects.Count > 0) 
          { 
            DataRow row; 
            foreach (SearchLogicMatchingObjects item in _matchingobjects) 
            { 
              row = _dt.NewRow(); 
              row["table"] = item.Table; 
              row["pk"] = item.PK; 
              _dt.Rows.Add(row); 
            }  

            // Call to function which changes UI - marshal to UI thread.
            _dispatcher.BeginInvoke((Action)(() => SelectCurrent()));
          }           
        } 

        wrk.Close();
  })); 
  thread.Start();
} 
codekaizen
Cheers! I've had some success implementing this although its thrown up other related issues. Specifically, when the search returns more than one matching object the _matchingobjects List is not populated. This list is declared in the UI thread so I guess that's the issue. I also had to use this ObservableCollectionEx http://geekswithblogs.net/NewThingsILearned/archive/2008/01/16/have-worker-thread-update-observablecollection-that-is-bound-to-a.aspx to prevent exceptions. Will soldier on with my rudimentary understanding of Threading!
pilsdumps
Scratch that comment about the list not being populated
pilsdumps
Generally, you can access _non-UI_ objects on any thread, however you will need to take care that multiple threads don't try to read/write them at the same time. Also, operations across threads are not guaranteed to be in any order, which is often a source of errors for someone new to threading.
codekaizen
Ok. That perhaps explains my strange new problem. When I run a search that returns only one hit and the datatable (_dt) is not required all works fine. If I run a search that returns multiple records it works first time, but when I run the same search again I get an error {"There is no row at position 0."}. If I step through the code it appears to be working, but when run as normal I get the error. It's as if the datatable has not been populated in time. Any suggestions?
pilsdumps
Try locking access to any shared state. You can use the `lock` keyword (which uses `System.Threading.Monitor` under the covers) or use something like `System.Threading.ReaderWriterLockSlim` if you need to have multiple readers. A great look at synchronization is found here: http://www.albahari.com/threading/part2.aspx Scroll down to "Locking and Thread Safety" on that page and enjoy! Do note, however, that threading can be very nasty to debug. If you could some how _not_ share the state, and instead work on copies or return values, it would be better.
codekaizen
"threading can be very nasty to debug" - definitely agree!! Thanks for the suggestions - will check these out. I've made a little headway though by destroying and recreating the _matchingobjects List and _dt data table. I no longer get the exception. However (there is always a however with this!) the animation only animates when a single record is returned. When _matchingobjects.Count > 0 the animation sticks - which was my original problem. Presumably because _matchingobjects is declared in the UI thread....
pilsdumps
I doubt that is the problem, since fields are not "declared" on a thread, and access can be on any thread, unless, of course, it is a UI object, which, for WPF, means anything descending from DispatcherObject. My guess is that since you are calling BeginInvoke in that loop, it is still starving the animation. You could try calling BeginInvoke with a lower priority (maybe DispatchPriority.Background), and see if that helps.
codekaizen
Ok, thanks v much for all your help - much appreciated. I've got a working if not perfect solution at the moment. Clearly I need to read up on threading!
pilsdumps
If you are using .Net v4, you will probably be better off looking at the `System.Threading.Task` namespace, as this library has a nice interface compared to writing code to run in a thread (it uses the ThreadPool, but hides this). http://msdn.microsoft.com/en-us/library/system.threading.tasks.aspx
codekaizen