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,但主窗口的当前操作流程会暂停,直到子窗口关闭后才继续(如果后续有代码的话)。

相关推荐
玖笙&7 小时前
✨WPF编程进阶【6.1】:图形原则(附源码)
c++·c#·wpf·visual studio
huoshan123457 小时前
给旧版 .NET 也开一扇“私有之门”——ILAccess.Fody 实现原理与设计
c#·.net·fody·il·mono.cecil
lixy5798 小时前
WPF检测网络状态切换
wpf
纸照片8 小时前
WPF中为Button设置IsMouseOver和IsPressed事件中改变背景颜色不起作用
c#·.net·wpf
Aevget8 小时前
DevExpress WPF中文教程:Data Grid - 如何使用虚拟源?(四)
ui·.net·wpf·devexpress·wpf控件
关关长语9 小时前
Dotnet使用System.Xml.Serialization处理Xml序列化
xml·c#·.net
Z_W_H_9 小时前
【ArcGISProSDK】刷新按钮样式
wpf·arcgisprosdk
歪歪10012 小时前
在C#中除了按属性排序,集合可视化器还有哪些辅助筛选的方法?
开发语言·前端·ide·c#·visual studio
weixin_3077791312 小时前
C#程序实现将Teradata的存储过程转换为Snowflake的sql的存储过程
数据库·数据仓库·c#·云计算·迁移学习