2012-04-06 8 views
22

Ho una libreria di controllo WPF che viene aggiunta a un'applicazione Windows Form. Vogliamo consentire ai controlli di essere localizzabili, tuttavia non sono sicuro di come FULLY compire questo senza duplicare il codice. This is what I am doing now.Esiste un modo per utilizzare StaticResource in una libreria di controlli WPF e poter visualizzare in fase di progettazione?

Fondamentalmente, nell'app di Windows Form, prima che l'applicazione principale venga avviata, sto creando un'istanza di un'app.xaml che vive all'interno dell'app di moduli (contenente i miei collegamenti alle risorse che vivono anche all'interno dell'app di moduli). Questo funziona perfettamente per il runtime.

Tuttavia, il mio utente controlla tutti hanno Content="{StaticResource SomeVariableName}", che finisce per essere vuoto. Posso risolvere il problema avendo un'app.xaml e dizionari di risorse appropriate nella mia libreria di controlli che corrispondono a quelli nella mia app per moduli Windows. Tuttavia, questo è un codice duplicato.

Le cose che mi hanno già provato inutilmente:

  • un'istanza App.xaml che vive all'interno della libreria di controllo utente da dentro il mio forme app. Questo non funziona perché l'URI delle mie risorse sta cercando una risorsa incorporata, non il mio dizionario delle risorse locali (potrei quindi semplicemente copiare i file di risorse dal controllo in una posizione appropriata all'interno della mia app di moduli su build). Potrei sfruttare lo DeferrableContent qui? Non c'è molto online, per quanto ho potuto trovare su questo attributo e come dovrebbe essere usato, però.
  • Vorrei utilizzare post build sia per App che per dizionari, tuttavia, l'istanza App è un riferimento statico a un app.xaml compilato per quanto posso dire. Quindi, App.xaml deve vivere all'interno del modulo almeno
    • Ho provato ad avere un'app.xaml duplicata con una generazione di post spostando il resourcedictionary.xaml. Ho capito che un'app app.xaml duplicata è ok dato che è la forza trainante e non si può preferire affidarsi a uno dal controllo in ogni caso (che torna indietro e ti chiedi se dovresti avere l'app.xaml nel controllo su tutto? A meno che non si desideri consentire un valore predefinito che utilizza risorse incorporate ....) Anche questo non è riuscito a dire che non è stato possibile trovare la risorsa anche se è stata collocata dove l'URI avrebbe dovuto puntare. I punti di codice decompilati a Uri resourceLocater = new Uri("/WindowsFormsApplication3;component/app.xaml", UriKind.Relative);

Quindi, C'è un modo per far sì che tale al lavoro e hanno la progettazione di visualizzazione tempo dei valori di default dei componenti ed evitare la duplicazione? Oppure, la duplicazione è OK in questo caso? Se la sottovoce del mio secondo proiettile sembra ok (App.xaml duplicato con resourcedictionaries copiati), come faccio a non cercare un elemento a livello di componente, ma invece a livello di file uno?

Ultima domanda (e posso postarla separatamente se necessario) a cui ho appena prestato attenzione. La mia app.xaml è incorporata nel codice, quindi non mi permette di creare nuovi ResourceDictionaries al volo. C'è un modo per fare questo?

Opzione finale ... forse la migliore? - I plan on using Andre van Heerwaarde's code anyway, quindi dovrei controllare l'esistenza di un file e aggiungerlo come una risorsa unita al volo? Fondamentalmente, ho un App.xaml nel mio controllo utente che si collega a un ResourceDictionary incorporato predefinito. E, quindi, fare in modo che il codice cerchi le risorse localizzate appropriate al volo, che possono essere percorsi di file relativi? L'unico lato negativo che vedo qui è che il default non può essere cambiato al volo ... che probabilmente potrei avere anche quello sguardo in un posto specificato (usando una sorta di convenzione) e preferire quello predefinito?

Oh, e il motivo per cui non desidero le risorse incorporate è che gli utenti finali possano aggiungere/modificare nuove risorse localizzate dopo la distribuzione della build.

Posso aggiungere codice se ti aiuterà a visualizzarlo meglio, fammelo sapere.

UPDATE

Ora sto funzionando in un ulteriore problema con uno stile e non solo la localizzazione.

Ecco un esempio di uno dei pulsanti interni su uno dei controlli:

<Button Style="{StaticResource GrayButton}" 

Alcune altre cose che ho provato/pensiero:

  • Non riesco a creare un App.xaml (che non verrebbe mai utilizzato) con il ResourceDictionary impostato come ApplicationDefinitions non è consentito nei progetti di libreria. Potrei incorporare questo nelle risorse del controllo, ma in questo caso avrei sempre la precedenza su qualsiasi risorsa a livello di applicazione e perderò la personalizzazione.

Here is a connect case che in realtà suona come quello che sto cercando, ma non fornisce alcuna soluzione reale a questo

La soluzione (al di là del top..which non funziona) che mi viene in mente che potrebbe funzionare (e deve ancora provare) sembra anche un sacco di lavoro per qualcosa che penserei dovrebbe essere semplice. Ma potrei essere in grado di creare alcune proprietà di dipendenza nel controllo a cui è possibile collegarsi e quindi consentire che vengano sovrascritte dal progetto che utilizzerà il controllo. Come ho detto, sembra molto lavoro per una richiesta piuttosto semplice :). Funzionerebbe anche questo? E ancora più importante, c'è una soluzione migliore e più semplice che mi manca?

risposta

12

Ho eseguito in questo problema una volta, e ho risolto facendo cadere il tutto "Le risorse sono oggetti indicizzati da chiave nei dizionari canoniche" cosa.

Voglio dire, il semplice fatto di definire una risorsa in un progetto e di farne riferimento in un'altra con la sua "chiave" dovrebbe dare la pelle d'oca a qualsiasi persona sana di mente. Volevo i riferimenti forti.

La mia soluzione a questo problema è stato quello di creare un custom tool che converte i file XAML di risorse per classi statiche con una proprietà per ogni risorsa:

Così MyResources.xaml:

<ResourceDictionary> 
    <SolidColorBrush x:Key="LightBrush" ... /> 
    <SolidColorBrush x:Key="DarkBrush" ... /> 
</ResourceDictionary> 

Becomes MyResources.xaml cs

public static class MyResources { 

    static MyResources() { 
    // load the xaml file and assign values to static properties 
    } 

    public static SolidColorBrush LightBrush { get; set; } 
    public static SolidColorBrush DarkBrush { get; set; } 

} 

Per riferimento a una risorsa, è possibile utilizzare il x:Static invece di StaticResource:

<Border 
    Fill="{x:Static MyResources.LightBrush}" 
    BorderBrush="{x:Static MyResources.DarkBrush}" 
    ... /> 

Ora hai un forte controllo, il completamento automatico e la verifica del tempo di compilazione delle risorse.

+1

+1 Questa è la migliore risposta che ho visto finora. In questo modo, posso inserire il mio codice risorsa ovunque e farlo agire, tuttavia, ne ho bisogno per agire. Ma come funzionerà nel designer? Sarà possibile creare un'istanza di MyResources e utilizzare i valori predefiniti? –

+0

Per quanto mi ricordo, sì. –

+0

+1 geniale!Non mi è mai venuto in mente di avvicinarsi così! – Dom

0

Anche io ho avuto un problema con i temi dello stile e le risorse statiche disponibili. Così, ho creato una libreria autonoma che fondamentalmente non aveva nient'altro che i temi da usare tutti annidati come le tue risorse MERGED della tua precedente domanda collegata.

Poi, sotto forma di Windows (XAML), ho appena messo riferimento a quella libreria, qualcosa come

<Window x:Class="MyAppNamespace.MyView" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ... /> 

    <Window.Resources> 
    <ResourceDictionary> 
     <ResourceDictionary.MergedDictionaries> 
     <!-- Common base theme --> 
     <ResourceDictionary Source="pack://application:,,,/MyLibrary;component/Themes/MyMainThemeWrapper.xaml" /> 
     </ResourceDictionary.MergedDictionaries> 
     </ResourceDictionary> 
    </Window.Resources> 

    <Rest of XAML for the WPF window> 

</Window> 

Il "componente" sembra riferirsi alla radice di un determinato progetto "MyLibrary". Nel progetto attuale, ho creato una sottocartella denominata "Temi", quindi la sorgente include ...; componente/Temi/...

Il "MyMainThemeWrapper.xaml" è molto simile ai dizionari nidificati delle risorse unite e vede tutto perfettamente da altre librerie.

+0

Questo non si sarebbe propagato al mio UserControl in fase di progettazione, comunque. E, anche se facessi riferimento alla libreria del dizionario delle risorse nel mio UserControl, allora sarebbe ancora incorporato in UserControl. Potrei aver bisogno di rendere la domanda un po 'più chiara, ma ho bisogno di qualcosa che può essere lavorato con design (il default), che può essere sovrascritto in fase di esecuzione (un contenuto resourcedictionary.xaml, non incorporato) –

0

Ecco la mia soluzione parziale al tuo problema. Non ho provato a gestire le risorse sciolte, ma ho avuto un certo successo con la condivisione di risorse tra WinForms e WPF.

  • Creare una libreria di classi per contenere le risorse in file .ResX (ad esempio Resources.resx, Resources.fr.resx, ecc)
  • Crea i controlli WPF in una libreria di controlli utente WPF
  • Crea le WinForms ospitano
  • riferimento alle risorse presenti nella libreria di risorse da WPF utilizzando l'estensione di markup Infralution.Localization.Wpf e responsabile della cultura, per esempio

    <TextBlock Text="{Resx ResxName=ResourceLib.Resources, Key=Test}"/> 
    
  • Mettere il contenuto dei controlli utente WPF in una o più dizionari di risorse come modelli di controllo, E, G

    <ControlTemplate x:Key="TestTemplate"> 
        <Grid> 
         <Grid.RowDefinitions> 
          <RowDefinition/> 
          <RowDefinition/> 
         </Grid.RowDefinitions> 
    
         <TextBlock Text="{Resx ResxName=ResourceLib.Resources, Key=Test}"/> 
        </Grid> 
    </ControlTemplate> 
    
  • Utilizzare il modello di risorsa nel vostro utente controlla

    <UserControl x:Class="WpfControls.UserControl1" 
          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
          xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
          xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" 
          d:DesignHeight="300" d:DesignWidth="300" > 
        <UserControl.Resources> 
         <ResourceDictionary> 
          <ResourceDictionary.MergedDictionaries> 
           <ResourceDictionary Source="ResourceDictionary.xaml"/> 
          </ResourceDictionary.MergedDictionaries> 
         </ResourceDictionary> 
        </UserControl.Resources> 
        <ContentControl Template="{StaticResource TestTemplate}" /> 
    </UserControl> 
    
  • Aggiungere un paio di righe di codice per far funzionare le cose

    public partial class UserControl1 : UserControl 
    { 
        // we require a reference to the resource library to ensure it's loaded into memory 
        private Class1 _class1 = new Class1(); 
    
        public UserControl1() 
        { 
         // Use the CultureManager to switch to the current culture 
         CultureManager.UICulture = Thread.CurrentThread.CurrentCulture; 
    
         InitializeComponent(); 
        } 
    } 
    

Ecco un semplice demo app chiamato WindowsFormsHost.7z

+0

Come è diverso da cosa ha suggerito @DRapp? –

+0

Bene, ho risolto il problema "Content =" {StaticResource SomeVariableName} "ponendo un ContentControl nel tuo UserControl, senza problemi con app.xaml. UserControl è localizzabile e condivide le risorse con WinForms usando i file .ResX. Come è lo stesso? – Phil

Problemi correlati