簡體   English   中英

從 Excel VSTO 和 Win Form 運行 WPF 應用程序

[英]Running WPF Application from Excel VSTO and Win Form

我有一個 WPF 項目,它使用了很多連接到本地數據庫的資源字典和實體框架。 當我在單獨的解決方案中測試項目時,一切正常。

現在,我正在嘗試將此 WPF 項目連接到現有的 Excel VSTO 項目,並通過單擊 Excel 功能區上的按鈕運行 WPF 應用程序窗口。 我修改了 App.xaml.cs 如下:

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        MainWindow view = new MainWindow();
        view.Show();
    }
}

此外,我已經從 App.xaml 中刪除了 StartupUri="MainWindow.xaml"。 在 Excel 功能區上,我有一個應該運行應用程序的按鈕:

private void button1_Click(object sender, RibbonControlEventArgs e)
{
    App application = new App();
    application.Run();
}

現在我有兩個不同的問題:

首先,當我單擊按鈕時,我在 MainWindow.xaml 的不同部分收到異常,如下所示:“在 'System.Windows.Baml2006.TypeConverterMarkupExtension' 上提供值拋出異常”。 很可能缺少與資源的連接。

<ResourceDictionary>
   <vm:ViewModelLocator x:Key="Locator"/>
   <ResourceDictionary.MergedDictionaries>
     <ResourceDictionary Source="Dictionaries\DataGridDictionary.xaml" />
     <ResourceDictionary Source="Dictionaries\DarkTheme.xaml" />
     <ResourceDictionary Source="Dictionaries\WindowStyle.xaml" />
   </ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

我試圖用一個空的 WPF 窗口重復相同的過程並且它有效,但現在我遇到了第二個問題。 當我再次單擊按鈕時,出現異常; “不能在同一個 AppDomain 中創建多個 System.Windows.Application 實例。”

我認為在一個大型項目中,通常有多個模塊,每個模塊作為一個 WPF 窗口,這些 WPF 窗口將從主應用程序 - Windows 窗體或 Office 功能區中調用。

您能否建議如何解決上述兩個問題? 謝謝!

編輯:我不想使用任務窗格和托管 WPF 控件。 我更喜歡將 WPF 應用程序作為獨立於 Excel 的單獨窗口運行。

我已經為這兩個問題找到了解決方案,但是當我修復第一個問題時,第二個問題不起作用,反之亦然。

首先,VSTO 項目應該引用 WPFproject.EXE 擁有的所有庫——實體框架和 WPF 應用程序使用的所有 NuGet 包。 此外,連接字符串也應復制到 Excel App.config 中。 Excel 項目獨立於 WPFproject.EXE,但看起來因為 Excel(或 Win 窗體應用程序)正在運行該節目,所以它必須具有所有引用。

當我在應用程序級別運行 WPF 項目時,它可以工作,但是當我再次單擊該按鈕時,我得到“無法在同一個 AppDomain 中創建多個 System.Windows.Application 實例。”。 我的目標是每隔一次單擊將 WPF 窗口置於最前面,或者在此期間關閉 WPF 應用程序時再次打開它。

private void button1_Click(object sender, RibbonControlEventArgs e)
{
    var t = new Thread(() =>
    {
        var app = new App();
        App.ResourceAssembly = app.GetType().Assembly;
        app.InitializeComponent();
        System.Windows.Threading.Dispatcher.Run();
    });

    t.SetApartmentState(ApartmentState.STA);
    t.Start();
}

我通過調用 MainWindow 而不是 App 找到了解決上述問題的方法。 當我測試一個簡單的 WPF 窗口時它可以工作,但它不會打開我的原始項目:

private void button1_Click(object sender, RibbonControlEventArgs e)
{
   var t = new Thread(() =>
   {
       MainWindow window = new MainWindow();
       window.Closed += (s2, e2) => window.Dispatcher.InvokeShutdown();
       window.Show();
       System.Windows.Threading.Dispatcher.Run();
   });

   t.SetApartmentState(ApartmentState.STA);
   t.Start();
}   

這兩種解決方案都來自上面提供的鏈接 Peter Schneider。

兩種方法有什么區別? 了解該機制可能會幫助我解決問題。 你能建議我在哪里可以找到關於這個主題的更多信息嗎?

謝謝!

我也遇到過和你一樣的問題,但還是沒有找到滿意的方法來解決。 但是,這是我在代碼中使用的。

在 vsto 項目中,如果還沒有應用程序,我會初始化一個應用程序。 通過初始化應用程序的組件,可以加載 app.xaml 中的 resousece 字典。 我還將應用程序的shutdownmode 更改為explicitshutdown,以便在主窗口關閉后,我不需要再次初始化此應用程序,因為主窗口可能會反復使用。 每次單擊按鈕時,我都會檢查主窗口之前是否已初始化。 如果它確實有一個主窗口,只需顯示它並通過 Activate() 把它放在前面。

private void button1_Click(object sender, RibbonControlEventArgs e)
{
            // if it's the first time run, initialize an application so the resourcedictionary is loaded by initializeComponent.
            if (System.Windows.Application.Current == null)
            {
                // initialize an wpf application and load its resources dictionaries and take control of app's shutdown
                _app = new App {ShutdownMode = ShutdownMode.OnExplicitShutdown};

                _app.InitializeComponent();
            }

            // initialize mainwindow if it's the first time to call this
            if (_app.MainWindow == null)
            {
                _app.MainWindow = new MainWindow();
                _app.MainWindow.Closing += (s1, e) => { Dispatcher.ExitAllFrames();};
            }

            // bring main window to front 
            _app.MainWindow.Show();
            _app.MainWindow.Activate();

            Dispatcher.Run();
}   

在wpf項目中,我去掉了app.xaml中的啟動uri並覆蓋了OnStartup()來手動設置啟動uri。 如果wpf項目是啟動項目(如調試wpf),將starturi設置為mainwindow所在的位置。 這是通過檢查 GetEntryAssembly() 來完成的,就像將 vsto 設置為啟動項目一樣,沒有入口程序集。

        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);

            // if current assembly is the entry point, set the startup uri to main window.
            if (Assembly.GetEntryAssembly() == GetType().Assembly)
                StartupUri = new Uri("Windows/MainWindow.xaml", UriKind.Relative);
        }

使用上述方法仍然存在的問題是,當第一次顯示窗口時,雙擊excel單元格時鼠標點會顯示為忙。 但是,單擊第二個、第三個或任何其他單元格后,這不會出現。 我認為這與調度程序有關,因為當前的焦點在第一次單擊時在 wpf 窗口上,而在第二次單擊時切換到 excel。 我認為這是 XXX 問題,因為我找不到更好的方法。

使用 Wpf 項目作為類庫項目而不是Windows 應用程序 為此,請轉到 Wpf 項目的屬性->應用程序->輸出類型->類庫,同時刪除 App.xaml 文件。

現在您可以將此項目用作類庫項目。

現在,您可以顯示 wpf 窗口,而不是使用 AppDomain,如下所示

MainWindow window = new MainWindow();
window.Show();

但是上面的代碼不允許您在 wpf 窗口 TextBox 中進行編輯。 為此,您需要像下面這樣調用 MainWindow

MainWindow window = new MainWindow();
window.ShowDialog();

當您添加任何資源字典時,請執行以下操作

<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary x:Key="dataDict" Source="Dictionaries\DataGridDictionary.xaml"/>
    <ResourceDictionary x:Key="darkThemeDict" Source="Dictionaries\DarkTheme.xaml" />
    <ResourceDictionary x:Key="winStyleDict" Source="Dictionaries\WindowStyle.xaml" />
</ResourceDictionary.MergedDictionaries>    

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM