2012-11-16 15 views
9

Ho creato un componente aggiuntivo per l'ufficio che contiene un'istanza di un'applicazione WPF. Quando l'utente fa clic pulsanti del componente aggiuntivo, mi lancio diverse finestre nel modo seguente:Avvio di finestre WPF da un add in Office

MyViewModel viewModel = new MyViewModel(string infoFromOffice); 
MyWindow view = new MyWindow(); 
view.DataContext = viewModel; 

wpfApp.Run(view); 

Nel costruire vista modelli prima della mia chiamata a wpfApp.Run() mi ha colpito probelms con lo SynchronizationContext corrente in seguito. La risposta here spiega perché. C'è un modo migliore per avviare Windows WPF da un componente aggiuntivo di Office?

+1

Per curiosità, ci si fa alcuna differenza per chiamare 'wpfApp.Esegui (new MyWindow {DataContext = new MyViewModel (infoFromOffice)}); ' – Jay

+0

Grazie per il suggerimento, sfortunatamente questo non funziona. – Coder1095

risposta

1

Mentre la risposta di Arthur punto perché il problema stava accadendo ha aiutato, ma in realtà non rispondere come passare dati a un modello di vista da un'applicazione host, pur avendo la chiamata al costruttore vista del modello sia dopo la chiamata a App.Run(). Da allora ho trovato una (molto semplice) soluzione! Per chiunque sia interessato.

In App.xaml.cs:

private string data; 

public App(string infoFromOffice) { 
    this.data = data; 
} 

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

    MyViewModel viewwModel = new MyViewModel(this.data); 
    MyWindow view = new MyWindow(); 
    view.Show(); 
} 

Quando si lancia l'applicazione:

App application = new App(infoFromOffice); 
application.Run(); 

Si noti che l'avvio URI deve essere rimosso in App.xaml. Questa soluzione semplicissima mi consente di passare informazioni alla mia app, ma allo stesso tempo non richiede che il modello di visualizzazione sia costruito in un "ambiente non WPF" e quindi può fare uso del Dispatcher ecc.

2

Non ho mai creato un componente aggiuntivo di Office, ma ho utilizzato Windows WPF in altri tipi di applicazioni non WPF (Windows Form, librerie per generare file .XPS da visual WPF, ecc.). Puoi provare l'approccio suggerito in this question.. Mostra come configurare un thread in modo che sia in grado di eseguire l'app WPF. Se si dà un'occhiata sul codice dell'app generato ("App.gics") di un'applicazione WPF, a quanto pare è iniziato in questo modo:

/// <summary> 
    /// Application Entry Point. 
    /// </summary> 
    [System.STAThreadAttribute()] 
    [System.Diagnostics.DebuggerNonUserCodeAttribute()] 
    public static void Main() { 
     WpfApplication1.App app = new WpfApplication1.App(); 
     app.InitializeComponent(); 
     app.Run(); 
    } 

Ho provato a lanciare un app da un test di unità con il codice seguente e ha funzionato bene:

[TestMethod] 
    public void TestMethod() 
    { 
     // The dispatcher thread 
     var t = new Thread(() => 
     { 
      var app = new App(); 
      // Corrects the error "System.IO.IOException: Assembly.GetEntryAssembly() returns null..." 
      App.ResourceAssembly = app.GetType().Assembly; 
      app.InitializeComponent(); 

      app.Run(); 
     }); 

     // Configure the thread 
     t.SetApartmentState(ApartmentState.STA); 
     t.Start(); 
     t.Join(); 
    } 

EDIT

Guardando il codice credo che la dichiarazione sensibile al SynchronizationContext è la creazione dell'istanza finestra, non la creazione del tuo ViewModel (a meno che ViewModel non abbia a che fare con la logica View e i controlli di istanza, qualcosa che non dovrebbe fare). Quindi puoi provare a spostare l'istanza della finestra sul thread dell'app. Qualcosa di simile a questo:

[TestMethod] 
    public void TestMethod3() 
    { 
     // Creates the viewmodel with the necessary infomation wherever 
        // you need to. 
     MyViewModel viewModel = new MyViewModel(string infoFromOffice); 

     // The dispatcher thread 
     var t = new Thread(() => 
     { 
      var app = new App(); 
      // Corrects the error "System.IO.IOException: Assembly.GetEntryAssembly() returns null..." 
      App.ResourceAssembly = app.GetType().Assembly; 
      app.InitializeComponent(); 

      // Creates the Window in the App's Thread and pass the information to it 
      MyWindow view = new MyWindow(); 
      view.DataContext = viewModel; 

      app.Run(view); 
     }); 

     // Configure the thread 
     t.SetApartmentState(ApartmentState.STA); 
     t.Start(); 
     t.Join(); 
    } 
+0

Grazie, questo è un esempio utile ma non risolve il problema. Qui i modelli di visualizzazione vengono creati dopo la chiamata ad app.Run(). Perché ho bisogno di passare il mio modello di vista alcune informazioni che deve essere istanziato prima della chiamata ad app.Run(). Ciò significa che durante la costruzione del modello di visualizzazione, non esiste un dispatcher, che sta causando problemi in particolare con l'ottenimento di SynchronizationContext.Current. – Coder1095

+0

Credo che il problema sia con l'intantiation della finestra. Vedi la risposta modificata. –

+0

"a meno che ViewModel non abbia a che fare con la logica di View e controlli di istanza, qualcosa che non dovrebbe fare". Penso che questa sia la chiave qui. Il mio programma utilizza thread in background (attività) per interrogare un database e quindi aggiornare l'interfaccia utente tramite il thread dell'interfaccia utente. Se utilizzo una di queste attività nel costruttore del modello di vista (prima della chiamata ad app.Run()) fallisce perché SynchronizationContext.Current è null. Suppongo che la correzione è solo per non eseguire una query di database nel costruttore del modello di vista. Grazie per avermi aiutato con questo. – Coder1095

1

Ecco qui una versione che utilizza il dominio dell'applicazione per aprire l'applicazione wpf più volte in domini dell'applicazione e thread dell'interfaccia utente separati. Utilizzato questo in un addin ufficio. Ogni volta che si chiama l'avvio si ottiene una nuova applicazione. Non ho verificato la chiusura dei thread quando l'app wpf è chiusa.

http://eprystupa.wordpress.com/2008/07/31/running-multiple-wpf-applications-in-the-same-process-using-appdomains/

public class WpfHelper {

public static void Startup() 
    { 
     var appDomainSetup = new AppDomainSetup() 
     { 
      ApplicationBase = Path.GetDirectoryName(typeof(WpfHelper).GetType().Assembly.Location) 
     }; 

     AppDomain domain = AppDomain.CreateDomain(DateTime.Now.ToString(), null, appDomainSetup); 

     CrossAppDomainDelegate action =() => 
     { 
      Thread thread = new Thread(() => 
      { 
       var app = new WpfApplication.App(); 

       WpfApplication.App.ResourceAssembly = app.GetType().Assembly; 

       app.MainWindow = new WpfApplication.MainWindow(); 
       app.MainWindow.Show(); 
       app.Run(); 
      }); 

      thread.SetApartmentState(ApartmentState.STA); 
      thread.Start(); 

     }; 

     domain.DoCallBack(action); 
    } 

}

Problemi correlati