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

Elmish.WPF模态窗口关闭时无法触发事件传递数据,怎么解决?

问题描述

我有一个MainWindow,参照Elmish.WPF教程里的NewWindow示例打开了一个模态窗口。该模态窗口的视觉元素(如手写识别)几乎完全通过XAML和C#代码处理。除了在XAML中单独设置“提交”按钮外,我希望窗口关闭时自动将累积的数据发送给F#后端代码。尝试以下实现后,Closing/Closed事件从未触发以发送数据,该如何解决?

现有代码

XAML部分

<i:Interaction.Triggers>
    <i:EventTrigger EventName="Closing">
        <i:InvokeCommandAction PassEventArgsToCommand="True" Command="{Binding ClosingCommand}" CommandParameter="{Binding ElementName=ProgressNote, Path=WordDictionary}"/>
    </i:EventTrigger>
</i:Interaction.Triggers>

Elmish.WPF F#部分

"ClosingCommand" |> Binding.cmdParam paramWordDictionaryMsg

"ProgressNoteWindow"  |> Binding.subModelWin(                          
    (fun m -> m.ProgressNoteWin |> WindowState.ofOption),
    snd,                                           
    ProgressNoteWinMsg,                            
    ProgressNoteWinBindings,                       
    createProgressNote,                              
    onCloseRequested = ProgressNoteWinCloseRequested,
    isModal = true)  

type Msg = | ProgressNoteWinCloseRequested

let update msg m =
    | ProgressNoteWinCloseRequested -> 
        match m.ProgressNoteWin with
        | Some { ConfirmState = Some ConfirmStateEnum.CloseRequested } -> { m with ProgressNoteWin = None }, Cmd.none
        | Some progressNoteWin -> { m with ProgressNoteWin = Some { progressNoteWin with ConfirmState = Some ConfirmStateEnum.CloseRequested } }, Cmd.none
        | None -> m, Cmd.none
解决方案

问题核心在于Elmish.WPF的subModelWin会接管模态窗口的关闭逻辑,导致XAML中的Closing事件触发器无法正常执行。可以通过以下几种方式调整:

1. 在Elmish关闭逻辑中主动提交数据

修改F#的消息定义与更新逻辑,在处理窗口关闭请求时,先提取数据并发送到主ViewModel,完成数据处理后再关闭窗口:

// 新增接收手写数据的消息类型
type Msg = 
    | ProgressNoteWinCloseRequested
    | HandwritingDataReceived of WordDictionary

let update msg m =
    match msg with
    | ProgressNoteWinCloseRequested ->
        match m.ProgressNoteWin with
        | Some progressNoteWin ->
            // 从子模型中获取累积的手写数据
            let handwritingData = progressNoteWin.WordDictionary
            // 发送数据到主模型处理
            let cmd = Cmd.ofMsg (HandwritingDataReceived handwritingData)
            // 标记窗口为待关闭状态
            let updatedSubModel = { progressNoteWin with ConfirmState = Some ConfirmStateEnum.CloseRequested }
            { m with ProgressNoteWin = Some updatedSubModel }, cmd
        | None -> m, Cmd.none
    | HandwritingDataReceived data ->
        // 处理接收到的手写数据(例如保存到主模型状态)
        let updatedModel = { m with SavedHandwritingData = Some data }
        // 数据处理完成后关闭窗口
        match updatedModel.ProgressNoteWin with
        | Some _ -> { updatedModel with ProgressNoteWin = None }, Cmd.none
        | None -> updatedModel, Cmd.none

2. 确保C#侧数据同步到F# ViewModel

如果手写识别数据仅存在于C#的UI元素中,需在C#代码后台将数据同步到绑定的F#子模型属性:

// C#窗口后台代码示例
private void OnHandwritingContentUpdated()
{
    if (DataContext is ProgressNoteWinViewModel subModel)
    {
        // 将UI中的手写数据同步到F#子模型
        subModel.WordDictionary = this.ProgressNote.WordDictionary;
    }
}

调用时机可以绑定到手写识别控件的内容更新事件。

3. 直接在C#后台订阅Closing事件

跳过XAML的Interaction.Triggers,直接在C#窗口后台重写OnClosing方法,手动触发F#侧的命令:

// C#窗口后台代码
protected override void OnClosing(CancelEventArgs e)
{
    base.OnClosing(e);
    if (DataContext is ProgressNoteWinViewModel subModel)
    {
        // 传递手写数据执行F#侧的命令
        subModel.ClosingCommand.Execute(this.ProgressNote.WordDictionary);
        // 若需要等待数据处理完成再关闭,可设置e.Cancel = true,处理完成后手动关闭
    }
}

同时调整F#侧的绑定,确保命令能正确接收数据并处理:

let ProgressNoteWinBindings = [
    "ClosingCommand" |> Binding.cmdParam (fun dict -> HandwritingDataReceived dict)
    // 其他绑定项...
]

4. 校验Elmish窗口关闭逻辑

确保onCloseRequested的处理流程正确,只有在数据提交完成后,才将ProgressNoteWin设为None以关闭窗口,避免窗口被直接关闭而跳过数据提交步骤。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 14:01:16