Avalonia/WPF 打开子窗口,并且跨页面传值

项目描述:

MVVM模式下一个主页面,一个子窗口。

项目为小型项目。

场景描述:

数据库有两个表格:主页面表A,子窗口表B。

在主页面点击Dgv1选中一行数据(表A的某一行)

(页面传值过程)

点击一个按钮,打开子窗口。根据主页面中选中的Dgv1那一行数据的列的值①,查询子窗口表B的数据并显示。同时子窗口修改完并关闭子窗口后,主页面的页面数据会自动变化。

目前汇总:

按兔基大佬说法:打开新页面是View行为,不能完全局限在ViewModel。

群友大佬的说法:这场景看起来用viewfirst,虽然说有点落后。看起来不能用ViewModelFirst。

自己的想法:在主页面new一个子窗口view对象,通过构造函数传值(同事写winform是这样)

老大:用全局变量。

不知道还有啥方式

方法1示例

用了本质是全局变量的一种方式,在VM里new窗口了,破坏MVVM就破坏吧,反正都是我写。

ParamTransferService

cs 复制代码
public class ParamTransferService
{
    // 单例实例(线程安全)
    private static readonly Lazy<ParamTransferService> _instance = new Lazy<ParamTransferService>(() => new ParamTransferService());
    public static ParamTransferService Instance => _instance.Value;

    // 私有构造函数
    private ParamTransferService() { }

    // 核心:用字典存储所有参数(键:参数唯一标识,值:参数值)
    private readonly Dictionary<string, object> _params = new Dictionary<string, object>();
    // 线程安全锁
    private readonly object _lock = new object();

    // --------------------------
    // 兼容原有逻辑:保留图形引导界面的-ParamName的专用方法
    // --------------------------
    public void SetParamName(string paramName)
    {
        SetParam(ParamKeys.ConfGuideParamName, paramName); // 调用通用方法
    }

    public string GetParamName()
    {
        return GetParam<string>(ParamKeys.ConfGuideParamName); // 调用通用方法
    }

    // --------------------------
    // 新增:通用参数设置/获取方法(支持任意参数)
    // --------------------------
    /// <summary>
    /// 设置任意参数
    /// </summary>
    /// <param name="key">参数键(从ParamKeys获取,避免硬编码)</param>
    /// <param name="value">参数值(任意类型)</param>
    public void SetParam(string key, object value)
    {
        lock (_lock)
        {
            if (_params.ContainsKey(key))
                _params[key] = value; // 覆盖已有值
            else
                _params.Add(key, value); // 新增值
        }
    }

    /// <summary>
    /// 获取任意参数(自动转换类型)
    /// </summary>
    /// <typeparam name="T">参数类型</typeparam>
    /// <param name="key">参数键(从ParamKeys获取)</param>
    /// <returns>参数值(默认值为default(T))</returns>
    public T GetParam<T>(string key)
    {
        lock (_lock)
        {
            if (_params.TryGetValue(key, out object value) && value is T t)
                return t;
            return default; // 键不存在或类型不匹配时返回默认值
        }
    }

    // 可选:删除指定参数(避免残留)
    public void RemoveParam(string key)
    {
        lock (_lock)
        {
            _params.Remove(key);
        }
    }


}

ParamKyes

cs 复制代码
public class ParamKeys
{
    // --------------------------
    // 图形引导页面ConfGuide-ParamName
    // --------------------------
    public const string ConfGuideParamName = "ParamName"; // 对应原来的ParamName

    // 未来新增参数时,直接在这里添加常量即可
}

主页面VM按钮绑定方法

全局变量赋值为当前选中行

打开子窗口

cs 复制代码
    private void DoSelectedAndMark(object obj)
    {
        if (Dgv1SelectedRow == null)
        {
//自定义弹窗,可自行替换
            CustomMessageBoxView.ShowAsync("错误", "请先选择一个参数名称");
            return;
        }

        //给全局变量赋值
        ParamTransferService.Instance.SetParam(ParamKeys.ConfGuideParamName, Dgv1SelectedRow.ParamName);

        //弹出子窗口
        var editWindow = new EditImagePositionWindow();
        var desktop = App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime;
        editWindow.ShowDialog(desktop.MainWindow);
    }

子窗口VM

获取主页面绑定的全局变量的值。

cs 复制代码
    #region 从主页面获取ParamName

    private string _currentParamName;

    #endregion
    
    public EditImagePositionViewModel()
    {
        //通过全局单例服务获取主页面ParamName
        _currentParamName = ParamTransferService.Instance.GetParamName();
        

        CustomMessageBoxView.ShowAsync($"当前选中ParamName为:{_currentParamName}");

    }

打开子页面后提示:

打开窗口的方式

cs 复制代码
//弹出子窗口
var editWindow = new EditImagePositionWindow();
var desktop = App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime;
editWindow.ShowDialog(desktop.MainWindow);  

解析:

1. 创建子窗口实例

var editWindow = new EditImagePositionWindow();

  • 实例化EditImagePositionWindow子窗口对象,执行其构造函数(初始化 UI、绑定 ViewModel 等,即你在EditImagePositionWindow构造函数中写的InitializeComponent()DataContext = vm逻辑)。

2. 获取主窗口引用

var desktop = App.Current.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime;

  • App.Current.ApplicationLifetime获取当前应用的生命周期对象,由于是桌面应用,转换为IClassicDesktopStyleApplicationLifetime(Avalonia 桌面应用的标准生命周期接口)。

  • 通过desktop.MainWindow拿到应用的主窗口实例(通常是启动时设置的MainWindow),确保子窗口知道 "父窗口是谁"。

3. 以模态方式显示子窗口(核心效果)

editWindow.ShowDialog(desktop.MainWindow);

  • 这是 Avalonia 中显示模态窗口的标准方式,核心效果是:

    • 阻塞父窗口交互:子窗口打开后,主窗口会被 "冻结"(用户无法点击、输入或操作主窗口),必须先关闭子窗口(如通过你写的InputElement_OnPointerPressed事件关闭),才能回到主窗口继续操作。

    • 明确窗口层级:子窗口会以主窗口为 "所有者"(Owner),显示在主窗口上方,不会被主窗口遮挡,且关闭主窗口时会自动关闭子窗口(避免子窗口游离)。

    • 等待子窗口关闭:ShowDialog是异步方法(返回Task<object?>),虽然你的代码中没有await,但主窗口的当前操作流程会暂停,直到子窗口关闭后才继续(如果后续有代码的话)。

相关推荐
影寂ldy5 小时前
C# 类和对象
开发语言·c#
z落落7 小时前
C# 构造函数(无参/有参/重载/this)+析构函数(终结器)|GC 垃圾回收
java·开发语言·c#
z落落7 小时前
C# 字段与属性(get/set访问器、三种属性写法、只读属性)+属性拦截例子(get动态计算 + set数据校验)
开发语言·c#
影寂ldy7 小时前
C#栈和队列
开发语言·c#
魔法阵维护师8 小时前
从零开发游戏需要学习的c#模块,第三十四章(设置界面)
学习·游戏·c#
gc_22998 小时前
学习C#调用OpenXml操作word文档的基本用法(39:学习表格类-1)
c#·word·表格·table·openxml
gc_22998 小时前
C#测试调用Net.Codecrete.QrCodeGenerator库生成二维码的基本用法
c#·二维码·qrcodegenerator
yivifu10 小时前
CSS 自动级联编号有序列表完全指南
前端·css·c#·html·有序列表·级联编号
Ws_10 小时前
C# 桌面端开发工程师面试题 + 参考答案
开发语言·面试·c#
周杰伦fans12 小时前
掌握 MVVM Light:.NET 桌面应用开发的 MVVM 利器,掌握 ObservableObject、RelayCommand 和 Messenger
c#·wpf