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
相关产品推荐
相关产品推荐

