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

如何避免重复打开同一员工详情窗口并实现窗口聚焦?

员工详情窗口重复打开管控方案

要解决重复打开同一员工窗口的问题,完全可以在ViewModel里用字典管控已打开窗口,同时实现激活置顶的逻辑,具体改造如下:

核心思路

  • 用Dictionary<int, IDialogWindow>存储已打开的员工窗口:键为员工ID,值为对应的对话框窗口实例
  • 打开窗口前先检查字典:如果该员工窗口已打开,直接激活置顶;否则新建窗口并加入字典
  • 监听窗口关闭事件,关闭时从字典移除对应条目,避免内存泄漏

改造后的完整ViewModel代码

public class EmployeeListViewModel : BindableBase
{
    private readonly IUnitOfWork _unitOfWork;
    private readonly IDialogService _dialogService;
    // 存储已打开的员工窗口:员工ID -> 对话框窗口实例
    private readonly Dictionary<int, IDialogWindow> _openEmployeeWindows = new Dictionary<int, IDialogWindow>();

    private ObservableCollection<Employee> employees;
    public ObservableCollection<Employee> Employees
    {
        get => employees;
        set => SetProperty(ref employees, value);
    }

    private Employee selectedEmployee;
    public Employee SelectedEmployee
    {
        get => selectedEmployee;
        set => SetProperty(ref selectedEmployee, value);
    }
    
    public DelegateCommand OpenSelectedEmployeeCardCommand { get; }

    public EmployeeListViewModel(IUnitOfWork unitOfWork, IDialogService dialogService)
    {
        _unitOfWork = unitOfWork;
        _dialogService = dialogService;

        OpenSelectedEmployeeCardCommand = new DelegateCommand(OpenSelectedEmployeeCardCommandExecute);
    }
    
    private void OpenSelectedEmployeeCardCommandExecute()
    {
        if (SelectedEmployee == null) return;

        var employeeId = SelectedEmployee.Id;
        // 检查是否已打开该员工的窗口
        if (_openEmployeeWindows.TryGetValue(employeeId, out var existingWindow))
        {
            // 恢复窗口正常状态(如果是最小化)
            if (existingWindow.WindowState == WindowState.Minimized)
            {
                existingWindow.WindowState = WindowState.Normal;
            }
            // 激活并置顶窗口
            existingWindow.Activate();
            existingWindow.Focus();
            // 临时设置置顶,确保用户能立刻看到
            existingWindow.Topmost = true;
            existingWindow.Topmost = false;
            return;
        }

        // 准备打开新窗口的参数
        var parameters = new DialogParameters { { "EmployeeId", employeeId } };
        // 打开对话框
        _dialogService.Show(nameof(EmployeeCard), parameters, result =>
        {
            // 对话框关闭时,从字典中移除(双重保障)
            if (_openEmployeeWindows.ContainsKey(employeeId))
            {
                _openEmployeeWindows.Remove(employeeId);
            }
        });

        // 获取刚打开的对话框窗口实例并加入字典
        Application.Current.Dispatcher.BeginInvoke(() =>
        {
            // 找到对应员工的对话框窗口(这里假设窗口的DataContext是EmployeeCardViewModel,且包含EmployeeId属性)
            var newWindow = Application.Current.Windows.OfType<IDialogWindow>()
                .FirstOrDefault(w => 
                    w.DataContext is EmployeeCardViewModel vm && 
                    vm.EmployeeId == employeeId);
            
            if (newWindow != null)
            {
                _openEmployeeWindows[employeeId] = newWindow;
                // 监听窗口关闭事件,确保字典同步更新
                newWindow.Closed += (s, e) =>
                {
                    if (_openEmployeeWindows.ContainsKey(employeeId))
                    {
                        _openEmployeeWindows.Remove(employeeId);
                    }
                };
            }
        });
    }
}

关键逻辑说明

  1. 字典管控:用_openEmployeeWindows字典实时跟踪已打开的窗口,确保每个员工最多只有一个窗口实例
  2. 重复窗口处理:通过TryGetValue快速判断窗口是否已打开,避免遍历所有窗口的低效操作
  3. 窗口激活置顶:先恢复窗口正常状态(防止最小化时看不到),再调用Activate()和Focus()激活窗口,临时设置Topmost确保窗口能立刻出现在用户视野
  4. 内存泄漏防护:同时通过对话框关闭回调和窗口Closed事件双重保障,确保窗口关闭时从字典中移除对应条目,避免内存泄漏

补充优化方案

如果你的EmployeeCardViewModel实现了IDialogAware接口,可以在OnDialogOpened方法中直接将窗口实例传递给EmployeeListViewModel,这样就不需要通过遍历窗口的方式获取实例,代码会更可靠:

// 在EmployeeCardViewModel中
public class EmployeeCardViewModel : BindableBase, IDialogAware
{
    public int EmployeeId { get; set; }
    public event Action<int, IDialogWindow> DialogOpened;

    public void OnDialogOpened(IDialogParameters parameters)
    {
        if (parameters.TryGetValue<int>("EmployeeId", out var employeeId))
        {
            EmployeeId = employeeId;
            // 通知父ViewModel窗口已打开
            DialogOpened?.Invoke(employeeId, DialogWindow);
        }
    }

    // 其他IDialogAware接口实现代码...
}

然后在EmployeeListViewModel的打开方法中,先获取EmployeeCardViewModel实例并订阅事件:

// 修改OpenSelectedEmployeeCardCommandExecute中的打开逻辑
var cardViewModel = _container.Resolve<EmployeeCardViewModel>();
cardViewModel.DialogOpened += (id, window) =>
{
    _openEmployeeWindows[id] = window;
    window.Closed += (s, e) => _openEmployeeWindows.Remove(id);
};

parameters.Add("ViewModel", cardViewModel);
_dialogService.Show(nameof(EmployeeCard), parameters, result =>
{
    if (_openEmployeeWindows.ContainsKey(employeeId))
    {
        _openEmployeeWindows.Remove(employeeId);
    }
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 23:35:03