English 中文(简体)
WPF listbox dynamically populated - how to get it to refresh?
原标题:

I am new to WPF, so I thought this was simple. I have a form with a listbox and a button. In the click handler for the button I do something iteratively that generates strings, which I want to put in the listbox as I get them. The xaml for the list box is like

    <ListBox Height="87" Margin="12,0,12,10" Name="lbxProgress" VerticalAlignment="Bottom">
        <ListBox.BindingGroup>
            <BindingGroup Name="{x:Null}" NotifyOnValidationError="False" />
        </ListBox.BindingGroup>
    </ListBox>

and the click handler is like

private void btn_Click(object sender, RoutedEventArgs e)  
{  
  List<String> lstrFiles= new List<String>(System.IO.Directory.GetFiles   ("C:\temp", "*.tmp");  

  foreach(string strFile in lstrFiles)  
    lbxProgress.Items.Add(strFile);  
}  

Pretty straightforward. Since my real operation is lengthy, I want the listbox to update as I do each one - how do I get the box to dynamically update on each addition?

最佳回答

Create an ObservableCollection<string> and set your ListBox.ItemsSource to that collection. Because the collection is observable, the ListBox will update as its contents change.

However, if your real operation is blocking the UI thread, this may prevent WPF from updating the UI until the operation completes (because the WPF data binding infrastructure doesn t get a chance to run). So you may need to run your lengthy operation on a background thread. In this case, you will not be able to update the ObservableCollection from the background thread due to WPF cross-threading restrictions (you can update properties, but not collections). To get around this, use Dispatcher.BeginInvoke() to update the collection on the UI thread while continuing your operation on the background thread.

问题回答

don t use a List<>, use an ObservableCollection<>. Unlike a normal List, Observable collection fires events whenever an item is added or removed, this will cause any objects that are listening to act appropriately--such as your listbox refreshing to reflect the new/removed items.

If you require sorting, grouping, filtering, then consider using a CollectionView.

For a complete answer, here is the resulting code snippet, minus some error handling code:

namespace Whatever  
{  
  public partial class MyWindow : Window  
  {  
    public delegate void CopyDelegate();  
    private string m_strSourceDir;  // Source directory - set elsewhere.  
    private List<string> m_lstrFiles;  // To hold the find result.  
    private string m_strTargetDir;  // Destination directory - set elsewhere.  
    private int m_iFileIndex;  // To keep track of where we are in the list.  
    private ObservableCollection<string> m_osstrProgress;  // To attach to the listbox.  

    private void CopyFiles()  
    {  
      if(m_iFileIndex == m_lstrFiles.Count)  
      {  
         System.Windows.MessageBox.Show("Copy Complete");  
         return;  
      }  

      string strSource= m_lstrFiles[m_iFileIndex];  // Full path.  
      string strTarget= m_strTargetDir + strSource.Substring(strSource.LastIndexOf( \ ));  
      string strProgress= "Copied "" + strSource + "" to "" + strTarget +  " ;  
      try  
      {  
        System.IO.File.Copy(strFile, strTarget, true);  
      }  
      catch(System.Exception exSys)  
      {  
        strProgress = "Error copying "" + strSource + "" to "" + strTarget + "" - " + exSys.Message;  
      }  

      m_osstrProgress.Add(strProgress);  
      ++m_iFileIndex;  
      lbxProgress.Dispatcher.BeginInvoke(DispatcherPriority.SystemIdle, new CopyDelegate(CopyFiles));  
    }  

    private void btnCopy_Click(object sender, RoutedEventArgs e)  
    {  
      m_lstrFiles= new List<String>(System.IO.Directory.GetFiles(m_strSourceDir, "*.exe"));  
      if (0 == m_lstrFiles.Count)  
      {  
        System.Windows.MessageBox.Show("No .exe files found in " + m_strSourceDir);  
        return;  
      }  

      if(!System.IO.Directory.Exists(m_strTargetDir))  
      {  
        try  
        {  
          System.IO.Directory.CreateDirectory(m_strTargetDir);  
        }  
        catch(System.Exception exSys)  
        {  
          System.Windows.MessageBox.Show("Unable to create " + m_strTargetDir + ": " + exSys.Message);  
          return;  
        }  
      }  

      m_iFileIndex= 0;  

      m_osstrProgress= new ObservableCollection<string>();  
      lbxProgress.ItemsSource= m_osstrProgress;  
      lbxProgress.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new CopyDelegate(CopyFiles));  
    }  
  }  
}  




相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...

热门标签