首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

WPF 介面不更新?棄用 List 改用 ObservableCollection 才是正解

     編輯:關於.NET

問題背景

專案需實作硬體偵測功能,後台執行緒持續將偵測結果加入集合,但 UI 介面 (ListBox) 始終不顯示新資料。嘗試使用 DispatcherTimer 搭配 UIHelper.DoEvents() 強制重繪,仍無效果。

錯誤嘗試:Dispatcher 強制處理佇列

網路上常見的 DoEvents 實作:

public static void DoEvents() {
    DispatcherFrame nestedFrame = new DispatcherFrame();
    DispatcherOperation exitOperation = Dispatcher.CurrentDispatcher.BeginInvoke(
        DispatcherPriority.Background, new DispatcherOperationCallback(ExitFrame), nestedFrame);
    Dispatcher.PushFrame(nestedFrame);
    if (exitOperation.Status != DispatcherOperationStatus.Completed) exitOperation.Abort();
}
private static object ExitFrame(object state) {
    ((DispatcherFrame)state).Continue = false; return null;
}

在資料新增後呼叫 UIHelper.DoEvents(),但 ListBox 依然不更新。根因在於 資料來源集合未實作變更通知,而非 UI 執行緒阻塞。

正確解法:ObservableCollection<T> 自動通知 UI

List<T> 不實作 INotifyCollectionChanged,UI 綁定無法感知項目增減。必須改用 ObservableCollection<T> (或 BindingList<T>),其內建 CollectionChanged 事件會自動通知綁定目標重繪。

完整範例:DispatcherTimer 定時新增硬體資料

public partial class Window2 : Window {
    DispatcherTimer _mainTimer;
    ObservableCollection<DeviceCheckInfo> list;

    public Window2() {
        InitializeComponent();
        _mainTimer = new DispatcherTimer();
        _mainTimer.Interval = TimeSpan.FromSeconds(1);
        _mainTimer.Tick += _mainTimer_Tick;
        _mainTimer.IsEnabled = true;
    }

    void _mainTimer_Tick(object sender, EventArgs e) {
        if (progressBar1.Value == progressBar1.Maximum) progressBar1.Value = 0;
        progressBar1.Value++;
        DeviceCheckInfo device = new DeviceCheckInfo {
            CheckResult = true,
            Name = "發卡器" + progressBar1.Value,
            CheckContent = "打卡短短"
        };
        Dispatcher.BeginInvoke(new Action(() => {
            if (list != null) list.Add(device);
            // lbtest.ItemsSource = list; // 已在 Loaded 設定,無需重複指派
        }));
    }

    private void Window_Loaded(object sender, RoutedEventArgs e) {
        list = new ObservableCollection<DeviceCheckInfo>() {
            new DeviceCheckInfo { Name="三合一讀卡器", CheckContent="duankou", CheckResult=true },
            new DeviceCheckInfo { Name="發卡器", CheckContent="tongdao", CheckResult=false },
            new DeviceCheckInfo { Name="打印機", CheckContent="youzhi", CheckResult=true }
        };
        lbtest.ItemsSource = list;
    }

    private void button1_Click(object sender, RoutedEventArgs e) {
        DeviceCheckInfo device = new DeviceCheckInfo {
            CheckResult = true,
            Name = "發卡器" + progressBar1.Value,
            CheckContent = "打卡短短"
        };
        list.Add(device); // 直接新增,UI 自動更新
    }
}

public class DeviceCheckInfo {
    public string Name { get; set; }
    public string CheckContent { get; set; }
    public bool CheckResult { get; set; }
}

XAML 綁定

<ListBox Name="lbtest" />
<ProgressBar Name="progressBar1" Maximum="100" Height="20" />

關鍵差異對照

集合類型實作介面UI 綁定自動更新適用場景
List<T>IList, ICollection否純後台資料處理,無 UI 綁定
ObservableCollection<T>INotifyCollectionChanged, IList是 (WPF/Silverlight/WinRT)WPF/UI 綁定首選
BindingList<T>IBindingList, INotifyPropertyChanged是 (WinForms/WPF)跨平台或需排序/篩選

總結

WPF 資料綁定的核心在於「通知機制」。永遠不要為了刷新 UI 而呼叫 DoEvents,這會導致重進入風險與效能問題。正確做法是:資料來源使用實作 INotifyCollectionChanged 的集合 (如 ObservableCollection),並在 UI 執行緒上修改集合,綁定引擎會自動處理視覺樹更新。本例中將 List<DeviceCheckInfo> 改為 ObservableCollection<DeviceCheckInfo> 即徹底解決介面不更新問題。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved