Domanda C'è un modo per utilizzare StaticResource in una libreria di controllo WPF e poter visualizzare in fase di progettazione?


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. Questo è quello che sto facendo ora.

In sostanza, nell'app di Windows Form, prima che l'applicazione principale venga avviata, sto creando un'istanza di un'app.xaml che si trova all'interno dell'app di moduli (contenente i miei collegamenti alle risorse che vivono anche nell'app dei 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.

Cose che ho già provato inutilmente:

  • Crea un'istanza di App.xaml che risiede nella libreria di controllo utente dall'app dei miei moduli. 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 fare leva 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 l'app che per i dizionari, tuttavia, l'istanza App è un riferimento statico a un app.xaml compilato per quanto posso dire. Quindi, App.xaml deve vivere almeno nel formato
    • Ho provato ad avere un'app.xaml duplicata con un post build 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. Il codice decompilato punta a Uri resourceLocater = new Uri("/WindowsFormsApplication3;component/app.xaml", UriKind.Relative);

Quindi, esiste un modo per consentire a questo di funzionare e avere una visualizzazione del tempo di progettazione delle impostazioni predefinite del componente E 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? - Ho in programma di usare comunque il codice di Andre van Heerwaarde, 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 la mia ragione per non volere risorse incorporate è che gli utenti finali possano aggiungere / modificare nuove risorse localizzate dopo che la build è stata distribuita.

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

AGGIORNARE

Ora sto incontrando un ulteriore problema con lo styling e non solo la localizzazione.

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

<Button Style="{StaticResource GrayButton}"

Altre cose che ho provato / pensato:

  • Non riesco a creare un'app.xaml (che non sarebbe mai stata utilizzata) 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.

Ecco un caso di connessione in realtà suona come quello che sto cercando, tuttavia non fornisce alcuna soluzione reale a questo

La soluzione (al di là del top ... che non funziona) che posso pensare che possa funzionare (e deve ancora provarci) mi 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?


22
2018-04-06 04:26


origine


risposte:


Mi sono imbattuto in questo problema una volta e l'ho risolto eliminando l'intera cosa "Le risorse sono oggetti indicizzati per chiave nei dizionari canonici".

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 forte Riferimenti.

La mia soluzione a questo problema era creare un strumento personalizzato che converte i miei file xaml delle risorse in classi statiche con una proprietà per ogni risorsa:

Quindi MyResources.xaml:

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

Diventa 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 referenziare una risorsa, puoi usare il x:Static invece di StaticResource:

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

Ora hai dei riferimenti forti, il completamento automatico e il controllo del tempo di compilazione delle risorse.


12
2018-04-26 13:16



Anch'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.

Quindi, nel modulo di Windows (.xaml), ho appena messo il riferimento a quella libreria, qualcosa di simile

<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 del progetto "MyLibrary" dato. Nel progetto attuale, ho creato una sottocartella chiamata "Temi", quindi l'origine include ...; componente / Temi / ...

Il "MyMainThemeWrapper.xaml" è molto simile ai tuoi dizionari Merged Resource nidificati, e vede tutto perfettamente da altre librerie.


0
2018-04-20 15:58



Ecco il mio parziale soluzione 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 es. Resources.resx, Resources.fr.resx, ecc.)
  • Crea i tuoi controlli WPF in una libreria di controllo utente WPF
  • Crea il tuo host WinForms
  • Fare riferimento alle risorse nella libreria delle risorse da WPF utilizzando Infralution.Localization.Wpf estensione di markup e responsabile della cultura, ad es.

    <TextBlock Text="{Resx ResxName=ResourceLib.Resources, Key=Test}"/>
    
  • Metti il ​​contenuto dei controlli utente WPF in uno 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 risorse nei controlli utente

    <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>
    
  • Aggiungi 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 app demo chiamato WindowsFormsHost.7z


0
2018-04-25 20:11