views:

689

answers:

3

I have been trying to learn and take advantage of WPF and databinding. I have a listview that has a column that will display one of three images as shown in this snippet:

<GridViewColumn Header="Status" Width="50">
  <GridViewColumn.CellTemplate>
    <DataTemplate>
      <Image x:Name="TheImage" Height="18"/>
        <DataTemplate.Triggers>
          <DataTrigger Binding="{Binding Path=Status}" Value="queued">
            <Setter TargetName="TheImage" Property="Source" Value="Images\que_48.png" />
          </DataTrigger>
          <DataTrigger Binding="{Binding Path=Status}" Value="completed">
            <Setter TargetName="TheImage" Property="Source" Value="Images\complete_48.png" />
          </DataTrigger>
          <DataTrigger Binding="{Binding Path=Status}" Value="failed">
            <Setter TargetName="TheImage" Property="Source" Value="Images\fail_48.png" />
          </DataTrigger>
        </DataTemplate.Triggers>
      </DataTemplate>
    </GridViewColumn.CellTemplate>
  </GridViewColumn>

I have a class (BatchQueueItem) that has among it this code for handling the PropertyChange Event:

public string status;
  public string Status
  {
    get { return status; }
    set 
    { 
      status = value;
      OnPropertyChanged("Status");
    }            
  }
  // Create the OnPropertyChanged method to raise the event

  public event PropertyChangedEventHandler PropertyChanged;    
  private void OnPropertyChanged(string status)
  {
    if (PropertyChanged != null)
    {
      PropertyChanged(this, new PropertyChangedEventArgs(status));
    }
  }

And I have a button on the listview window page:

private void btnStart_Click(object sender, RoutedEventArgs e)
{
  foreach (var item in listView1.Items)
  {
    BatchQueueItem bqi = (BatchQueueItem)item;
    string currFile = bqi.CurrFile;
    if (mainWindow.isIsbnInFileName(ref currFile))
    {
       bqi.Status = "completed";
    }
    else
    {
      bqi.Status = "failed";
    }
  }
}

The problem I am having is that the images do not update until after the foreach loop has completed and the btnStart_Click() method has completed. Once that happens, all the images update as expected, just all at once not iteratively.

What I am wanting, and what I thought would happen, was that each iteration of the foreach loop would update the corresponding row's image. Undoubtedly I am missing something important about how this all works. Any tips?

+4  A: 

The problem I am having is that the images do not update until after the foreach loop has completed and the btnStart_Click() method has completed. Once that happens, all the images update as expected, just all at once not iteratively.

What I am wanting, and what I thought would happen, was that each iteration of the foreach loop would update the corresponding row's image. Undoubtedly I am missing something important about how this all works. Any tips?

This is a matter of how you're updating.

The problem isn't that the UI isn't updating - internally, it does. The problem is that you're doing this update, and your foreach loop, on the main UI thread. When you do this, the UI will be completely blocked (and not refresh/redraw) until your operation is 100% completed, and the message pump can run again.

There are options to work around this - if your operation is taking a while, and you wanted it to update as it ran, you could move the foreach loop into a delegate that's run on a background thread. You would then use the Dispatcher to Invoke the update of the status back onto the UI thread.

When you have a long running operation, this has a couple of benefits:

  1. You can get instance feedback/status in your UI
  2. Your UI stays responsive while your operation is running.
Reed Copsey
Actually, you don't need to use Dispatcher.Invoke here : WPF automatically marshals the PropertyChanged notification across threads for bindings, so you can update the property directly from the worker thread
Thomas Levesque
Ok, you guys lost me here. Any chance of a link or snippet of code to clear things up a bit pleaes?
Dave
A: 

Why is this a problem?

It looks like this loop should take microseconds to finish. If so, there's no perceptible difference whether the controls redraw at the end of the loop or not.

If the list is that large, you should consider paging it anyway.

If it takes that long to get the data, you should be doing it in a different thread, as suggested above. However, if it's that complex, I'd argue that you should really look at not having complex logic in your button click event handler, and move to more of an MVVM model.

kyoryu
Ok, I will look into using a different thread. It is not very complex logic, but some iterations may take 10-15 seconds as it parses a text file and the list will typically be 20-30 items.
Dave
A: 

Thanks to all the help! I was able to get this working exactly like I wanted once you got me pointed in the right direction. With your hints and this link: Worker Threads & Dispatcher I was able to get it going with a few extra lines of code. Plus I once again learned something new about WPF.

Dave