fix(sidebar): wrap ElementHost in WinForms UserControl + cast Explorer.Close event

Two VSTO-only build errors:

1. CustomTaskPanes.Add() requires a System.Windows.Forms.UserControl.
   ElementHost derives from Control, not UserControl. Wrap it inside a
   thin UserControl so the COM adapter is happy.

2. Outlook.Explorer exposes BOTH a Close() method and a Close event with
   the same name; the C# compiler resolves "explorer.Close += handler"
   to the method group. Cast through ExplorerEvents_10_Event to bind
   the event explicitly.

Core/UI build clean. Host project still requires the VSTO build targets
to compile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
PointStar
2026-05-11 16:00:18 +03:00
parent fa3562114d
commit 3bedb2cb06
+15 -3
View File
@@ -89,13 +89,18 @@ namespace OutlookAddin.Services
}; };
view.DataContext = vm; view.DataContext = vm;
// CustomTaskPanes.Add requires a System.Windows.Forms.UserControl,
// so we host the WPF UserControl inside ElementHost which itself
// lives inside a thin WinForms UserControl wrapper.
var element = new ElementHost var element = new ElementHost
{ {
Child = view, Child = view,
Dock = System.Windows.Forms.DockStyle.Fill Dock = System.Windows.Forms.DockStyle.Fill
}; };
var hostControl = new System.Windows.Forms.UserControl();
hostControl.Controls.Add(element);
var pane = _taskPanes.Add(element, PaneTitle, explorer); var pane = _taskPanes.Add(hostControl, PaneTitle, explorer);
pane.DockPosition = Office.MsoCTPDockPosition.msoCTPDockPositionRight; pane.DockPosition = Office.MsoCTPDockPosition.msoCTPDockPositionRight;
pane.Width = DefaultWidth; pane.Width = DefaultWidth;
pane.Visible = true; pane.Visible = true;
@@ -109,9 +114,12 @@ namespace OutlookAddin.Services
}; };
vm.OpenInBrowserRequested += (s, url) => TryOpenInBrowser(url); vm.OpenInBrowserRequested += (s, url) => TryOpenInBrowser(url);
explorer.Close += () => DetachExplorer(key); // Outlook.Explorer exposes BOTH a Close() method and a Close event
// on the same name; cast through the events interface to bind the
// event handler.
((Outlook.ExplorerEvents_10_Event)explorer).Close += () => DetachExplorer(key);
_bindings[key] = new ExplorerBinding(pane, element, vm, handler, explorer); _bindings[key] = new ExplorerBinding(pane, hostControl, element, vm, handler, explorer);
// Kick a first lookup based on whatever is selected when the pane opens. // Kick a first lookup based on whatever is selected when the pane opens.
handler.TriggerNow(); handler.TriggerNow();
@@ -170,6 +178,7 @@ namespace OutlookAddin.Services
private sealed class ExplorerBinding : IDisposable private sealed class ExplorerBinding : IDisposable
{ {
private readonly CustomTaskPane _pane; private readonly CustomTaskPane _pane;
private readonly System.Windows.Forms.UserControl _hostControl;
private readonly ElementHost _element; private readonly ElementHost _element;
private readonly ExplorerSelectionHandler _handler; private readonly ExplorerSelectionHandler _handler;
private readonly Outlook.Explorer _explorer; private readonly Outlook.Explorer _explorer;
@@ -178,12 +187,14 @@ namespace OutlookAddin.Services
public ExplorerBinding( public ExplorerBinding(
CustomTaskPane pane, CustomTaskPane pane,
System.Windows.Forms.UserControl hostControl,
ElementHost element, ElementHost element,
ReadingPaneViewModel viewModel, ReadingPaneViewModel viewModel,
ExplorerSelectionHandler handler, ExplorerSelectionHandler handler,
Outlook.Explorer explorer) Outlook.Explorer explorer)
{ {
_pane = pane; _pane = pane;
_hostControl = hostControl;
_element = element; _element = element;
ViewModel = viewModel; ViewModel = viewModel;
_handler = handler; _handler = handler;
@@ -195,6 +206,7 @@ namespace OutlookAddin.Services
try { _handler.Dispose(); } catch { /* ignore */ } try { _handler.Dispose(); } catch { /* ignore */ }
try { _pane.Visible = false; } catch { /* ignore */ } try { _pane.Visible = false; } catch { /* ignore */ }
try { _element.Dispose(); } catch { /* ignore */ } try { _element.Dispose(); } catch { /* ignore */ }
try { _hostControl.Dispose(); } catch { /* ignore */ }
} }
} }
} }