You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF主窗口更新第二窗口列表与DataGrid的实现问题

Hey there, let's break down your problem and fix it step by step—this is a super common WPF pitfall, so you're not alone!

First, Why Your Current Setup Isn't Working

  1. List<Laser> doesn't notify the UI of changes: When you add/remove/update lasers in your laserList, the second window's DataGrid has no way of knowing something changed because regular List<T> doesn't implement INotifyCollectionChanged.
  2. Thread exceptions from cross-thread UI updates: Serial port events run on a background thread, and WPF strictly requires all UI-related operations (including updating bound data) to happen on the main UI thread. Directly modifying the list from the serial thread triggers that exception.

Let's Fix This with Simple, Practical Changes

We'll use WPF's built-in data binding features without overcomplicating MVVM—just enough to get your windows synced and avoid thread issues.

Step 1: Update Your Laser Class to Notify Property Changes

First, make sure individual laser properties (like power, temperature) trigger UI updates when they change. Add INotifyPropertyChanged to your Laser class:

using System.ComponentModel;

public class Laser : INotifyPropertyChanged
{
    private string _name;
    public string Name
    {
        get => _name;
        set
        {
            _name = value;
            OnPropertyChanged(nameof(Name));
        }
    }

    private int _wavelength;
    public int Wavelength
    {
        get => _wavelength;
        set
        {
            _wavelength = value;
            OnPropertyChanged(nameof(Wavelength));
        }
    }

    private int _power;
    public int Power
    {
        get => _power;
        set
        {
            _power = value;
            OnPropertyChanged(nameof(Power));
        }
    }

    private int _temperature;
    public int Temperature
    {
        get => _temperature;
        set
        {
            _temperature = value;
            OnPropertyChanged(nameof(Temperature));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Step 2: Use ObservableCollection Instead of List<Laser>

ObservableCollection<T> is WPF's go-to collection type for data binding—it automatically tells the UI when items are added, removed, or moved. We'll wrap this in a simple view model to share between both windows:

public class LaserViewModel : INotifyPropertyChanged
{
    private ObservableCollection<Laser> _laserList;
    public ObservableCollection<Laser> LaserList
    {
        get => _laserList;
        set
        {
            _laserList = value;
            OnPropertyChanged(nameof(LaserList));
        }
    }

    public LaserViewModel()
    {
        LaserList = new ObservableCollection<Laser>();
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

Step 3: Update MainWindow to Use the ViewModel

Replace your List<Laser> with the view model, share it with the second window, and handle serial data safely on the UI thread:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.IO.Ports;
using System.ComponentModel;

namespace WpfApp1
{
    using Screen = System.Windows.Forms.Screen;
    public partial class MainWindow : Window
    {
        SerialPort sp = new SerialPort();
        LaserViewModel _viewModel;
        Window1 doorPanel;
        string[] ports = SerialPort.GetPortNames();

        public MainWindow()
        {
            InitializeComponent();
            // Initialize the view model and set it as the DataContext
            _viewModel = new LaserViewModel();
            DataContext = _viewModel;

            foreach (string s in SerialPort.GetPortNames())
            {
                dropdownList.Items.Add(s);
            }

            // Hook up serial data received event (you probably have this already)
            sp.DataReceived += Sp_DataReceived;
        }

        private void Sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            // Read and parse serial data (replace with your actual parsing logic)
            string serialData = sp.ReadLine();
            Laser updatedLaser = ParseSerialDataIntoLaser(serialData);

            // MUST update the collection on the UI thread to avoid exceptions
            Application.Current.Dispatcher.Invoke(() =>
            {
                // Update existing laser or add a new one
                var existingLaser = _viewModel.LaserList.FirstOrDefault(l => l.Name == updatedLaser.Name);
                if (existingLaser != null)
                {
                    existingLaser.Power = updatedLaser.Power;
                    existingLaser.Temperature = updatedLaser.Temperature;
                    // Update other properties as needed
                }
                else
                {
                    _viewModel.LaserList.Add(updatedLaser);
                }
            });
        }

        // Example parsing method—replace with your actual logic
        private Laser ParseSerialDataIntoLaser(string data)
        {
            // Split your serial string and map to Laser properties
            return new Laser { Name = "Laser1", Wavelength = 532, Power = 800, Temperature = 28 };
        }

        private void showDoorDisplay(object sender, RoutedEventArgs e)
        {
            doorPanel = new Window1();
            // Share the SAME view model with the second window—this is key for syncing
            doorPanel.DataContext = _viewModel;

            int secondScreen = 1;
            ShowOnMonitor(secondScreen, doorPanel);
        }

        private void ShowOnMonitor(int monitor, Window window)
        {
            var screen = ScreenHandler.GetScreen(monitor);
            window.WindowState = WindowState.Normal;
            window.Left = screen.Bounds.Left;
            window.Top = screen.Bounds.Top;
            window.Width = screen.Bounds.Width;
            window.Height = screen.Bounds.Height;
            window.Show();
        }

        // Keep your existing connect/disconnect methods here—just use _viewModel.LaserList when updating lasers
    }
}

Step 4: Configure Window1's DataGrid to Bind to the ViewModel

Update your Window1.xaml to bind its DataGrid directly to the shared LaserList:

<Window x:Class="WpfApp1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Door Display" Height="450" Width="800">
    <Grid>
        <DataGrid ItemsSource="{Binding LaserList}" 
                  AutoGenerateColumns="False"
                  IsReadOnly="True">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Laser Name" Binding="{Binding Name}"/>
                <DataGridTextColumn Header="Wavelength" Binding="{Binding Wavelength}"/>
                <DataGridTextColumn Header="Power" Binding="{Binding Power}"/>
                <DataGridTextColumn Header="Temperature" Binding="{Binding Temperature}"/>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

And simplify Window1's code-behind (no need to pass the list anymore):

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
    }
}

Why This Works

  • ObservableCollection automatically tells the DataGrid when the collection changes (add/remove items), so both windows refresh instantly.
  • Shared ViewModel means both windows are looking at the exact same list of lasers—no need to manually sync them.
  • Dispatcher.Invoke ensures all data updates happen on the main UI thread, eliminating those annoying cross-thread exceptions.

内容的提问来源于stack exchange,提问作者Tolga Aktas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:05:16