在 WinForms 應用程式中顯示 WPF 視窗是常見的混合開發場景,通常只需透過 WindowInteropHelper 設定擁有者視窗控制代碼,再呼叫 ShowDialog() 即可正常運作。然而,當 WPF 視窗的程式碼後置(非 XAML)中嘗試載入外部 XAML 資源(如主題檔案)時,可能會在建構視窗實例時拋出 NotSupportedException: "The URI prefix is not recognized" 異常。
典型程式碼如下:
MyWindow wpfWindow = new MyWindow();
WindowInteropHelper wih = new WindowInteropHelper(wpfWindow);
wih.Owner = ownerHwnd;
wpfWindow.ShowDialog();
若 MyWindow 建構函式中包含如下載入資源字典的程式碼:
_dictionary.Source = new Uri("/RibbonControlsLibrary;component/Themes/RibbonWindow.xaml", UriKind.Relative);
即會觸發該異常。原因在於 WPF 的 Pack URI 解析機制需要 pack://application:,,, 前綴才能正確定位組件資源,而在純 WinForms 環境中缺少 System.Windows.Application 實例,導致相關初始化未執行。
在設定資源來源時,明確加上 Pack URI 前綴:
_dictionary.Source = new Uri("pack://application:,,,/RibbonControlsLibrary;component/Themes/RibbonWindow.xaml", UriKind.Relative);
這樣可直接讓 WPF 的資源解析器正確識別組件路徑。
若無法修改 MyWindow 原始碼,可在建立視窗實例前,手動建立一個 System.Windows.Application 實例並設定關閉模式:
System.Windows.Application app = new System.Windows.Application();
app.ShutdownMode = System.Windows.ShutdownMode.OnExplicitShutdown;
原理在於 Application 的靜態建構函式會執行關鍵初始化:
PreloadedPackages.AddPackage(PackUriHelper.GetPackageUri(BaseUriHelper.PackAppBaseUri), new ResourceContainer(), true);
這行程式碼會向預載入封裝集合註冊應用程式基底 URI,使後續的相對 URI 能自動補全 pack://application:,,, 前綴。
ShutdownMode 為 OnLastWindowClose,會在最後一個 WPF 視窗關閉時自動關閉應用程式。若需反覆開關 WPF 視窗,務必設為 OnExplicitShutdown 並手動呼叫 app.Shutdown()。app.Run(),因為只需靜態建構函式的初始化邏輯,不需啟動訊息迴圈。透過上述任一方式,即可解決 WinForms 裝載 WPF 時的 URI 前綴識別問題,實現無縫混合開發。