Ruft Werte aus * .resx-Dateien in XAML ab

74

Ist es möglich, einen Wert aus der Ressourcendatei direkt in das XAML-Markup einzufügen? Oder zur Lokalisierung müssen wir immer so etwas in der * .cs-Datei machen:

txtMessage.Text = Messages.WarningUserMessage;

Wo Messagesist Ressource und txtMessageist TextBlock.

0x49D1
quelle
2
Wie haben Sie die Ressourcendatei "Nachrichten" erstellt? Haben Sie gerade Messages.resx zu Ihrem Eigenschaftenordner hinzugefügt? Wenn ja, kann ich diese Datei nicht erreichen.
Sergey
@ Sergey, egal wo es ist. Natürlich können Sie es auch zum Eigenschaftenordner hinzufügen. Sicher muss sich die resx-Datei im Projektverzeichnis befinden. Möglicherweise können Sie es nicht von VS zum Eigenschaftenverzeichnis hinzufügen? Übrigens ist es eine schlechte Praxis, smth zum Eigenschaftenverzeichnis hinzuzufügen. Erstellen Sie besser ein "Ressourcen" -Verzeichnis, in dem Sie Ihre Res-Dateien speichern können.
0x49D1
Eng verwandt: stackoverflow.com/questions/12363834/…
UuDdLrLrSs

Antworten:

85

Stellen Sie sicher, dass die Codegenerierung im resx-Editor auf Öffentlich gesetzt ist. Dann können Sie einfach Folgendes verwenden:

<TextBlock Text="{x:Static Messages.WarningUserMessage}" />
Julien Lebosquain
quelle
1
Ich bin neu bei WPF und diese Antwort ist genau das, was ich brauchte! Vielen Dank.
0x49D1
2
Beachten Sie, dass es nur funktioniert, wenn Messageses sich im selben Namespace wie der Bildschirm befindet.
Julien N
4
... und wenn Messagesnicht in einer anderen Assembly, da der Konstruktor als intern generiert wird.
Davi Fiamenghi
@DaviFiamenghi: Der Konstruktor spielt keine Rolle, wir greifen hier nur auf statische Mitglieder zu.
Julien Lebosquain
2
Sie müssen den Namespace im Designer noch wie in der @ Yogs-Antwort xmlns: resource = "clr-namespace: YourProject.Properties" hinzufügen und wie {x: Statische Ressource: Resources.ResourceName} verwenden
Ilya Kochetov
70

Es ist viel einfacher, es so zu machen. Fügen Sie eine XML-Datei in die XAML-Datei ein und verwenden Sie die Ressourcen direkt.

xmlns:resx="clr-namespace:wpfapplicationname.Properties"
Title="{x:Static resx:Resources.name}"
Daniex
quelle
3
Der einzige Nachteil dieses Ansatzes ist natürlich, dass Sie die Kultur nicht dynamisch ändern können. Es gibt einen großartigen Artikel darüber, wie dieses Verhalten mithilfe einer Markup-Erweiterung und eines Lokalisierungsmanagers erreicht werden kann.
Stephen Drew
5
hier ist es: grumpydev.com/2009/09/08/…
Davi Fiamenghi
14

Ich verstehe, dass meine Antwort etwas spät ist, aber ich dachte, es lohnt sich, sie zu teilen:

So verwenden Sie eine in der * .resx-Datei gespeicherte Zeichenfolge ohne statisches Schlüsselwort:

  1. Fügen Sie in der Datei App.Xaml einen Namespace für Eigenschaften hinzu xmlns:resource="clr-namespace:YourProject.Properties"
  2. Fügen Sie in ApplicationResources (app.xaml-Datei) eine Ressource für Ihre * .resx-Datei hinzu

    <Application.Resources> <resource:ResourceFileName x:Key="ApplicationStringResources" /> </Application.Resources>

  3. Verwenden Sie in Ihrer XAML-Datei die folgende Bindung. Nehmen wir ein Beispiel für den Fenstertitel

    Title="{Binding TitleString, Source={StaticResource ResourceKey=ApplicationStringResources}}"

    TitleString ist der Name von StringProperty in Ihrer * .resx-Datei

  4. Vergessen Sie nicht, den Modifikator für den Zugriff auf Ressourcendateien in "Öffentlich" zu ändern.

Yogs
quelle
1
Wie haben Sie das zum Laufen gebracht? Ich habe Ihre Anweisungen genau befolgt, aber die erstellte Ressourcendatei (mit dem Modifikator "Öffentlicher Zugriff" auf "Öffentlich") enthält im Grunde eine statische Klasse mit einem geschützten Konstruktor. Sowohl in WPF als auch in Metro wird im XAML-Designer die Fehlermeldung "Der Typ ResourceFileName enthält keine zugänglichen Konstruktoren" (und ein weiterer ähnlicher Fehler beim Kompilieren) angezeigt.
Quarkly
Hat für mich gearbeitet. Ich hatte nur Probleme, die Kompilierungsoption 'public' für die .resx-Datei zu finden. Es ist ein winziges Dropdown-Menü über dem .resx-Editor beim Öffnen der .resx-Datei. Zusätzlich muss das Projekt neu erstellt werden, damit die Zeichenfolgen verfügbar sind
Marco
Stellen Sie sicher, dass Sie die Lösung am Ende neu
erstellen
Damit es in VS 2019 v16.6.5 funktioniert, musste ich die Eigenschaften> Aktion erstellen in der Datei Resources.resx von "Eingebettete Ressource" in "Keine" und dann zurück in "Eingebettete Ressource" ändern. Das scheint mir ein Fehler zu sein, aber es hat funktioniert.
Brett Wertz
5

Der einfachste Weg besteht wahrscheinlich darin, die Elemente direkt zu referenzieren (es handelt sich um statische Eigenschaften, standardmäßig intern):

<TextBlock x:Name="txtMessage" Text="{x:Static MyApp.Properties.Resource.TextString}"/>

Wenn Sie jedoch an einer lokalisierten WPF-App arbeiten, empfehlen wir Ihnen, die Anleitung zu CodePlex unter http://wpflocalization.codeplex.com/ zu lesen und eine zusammengesetzte App zu erstellen (mit PRISM oder MEF). Dann habe ich einen Blog-Beitrag über eine gute Möglichkeit, die WPF-Lokalisierung mithilfe von Standardbindungen durchzuführen .

Steven Robbins
quelle
1

Nach einer ganztägigen Untersuchung dieses Kommentars Xaml-Lokalisierung: Verwenden von .resx-Ressourcen in Xaml ohne x: static Ich habe eine einfache Lösung gefunden, um mehrsprachige Unterstützung mit (eingebetteten Ressourcen oder referenzierter Assembly) * .resx-Dateien bereitzustellen. Seit Framework 4 gibt es eine Basisklasse namens DynamicObject zum Festlegen des dynamischen Verhaltens zur Laufzeit im Namespace System.Dynamic.

Ich habe folgenden ResourceLoader von der System.Dynamic.DynamicObject-Klasse abgeleitet:

public class ResourceLoader : DynamicObject
{
    #region Fields ---------------------------------------------------------------

    private const string DefaultResourcesSuffix = "Resource";
    private ResourceManager _resourceMan;
    private CultureInfo culture;
    private readonly string _defaultAssemblyName;
    private readonly Assembly _defaultAssembly;
    private Assembly theAssembly;
    private string resourcesSuffix;
    private string assembly;

    #endregion // Fields

    #region Properties -----------------------------------------------------------

    /// <summary>
    /// Gets or sets the assembly.
    /// </summary>
    public string Assembly
    {
        get { return assembly; }
        set
        {
            assembly = value;
            theAssembly = System.Reflection.Assembly.Load(assembly);
            _resourceMan = null;
        }
    }

    /// <summary>
    /// Gets or sets the resources suffix.
    /// </summary>
    public string ResourcesSuffix
    {
        get { return resourcesSuffix; }
        set
        {
            resourcesSuffix = value;
            _resourceMan = null;
        }
    }

    /// <summary>
    /// Get, set culture
    /// </summary>
    public CultureInfo CurrentCulture
    {
        get { this.culture = this.culture ?? CultureInfo.InvariantCulture; return this.culture; }
        set { this.culture = value; }
    }

    /// <summary>
    /// Creates new instace of <see cref="System.Resources.ResourceManager"/> at initialisation or change of <see cref="ResourceFileAccessSample.ResourceBinding.ResourceLoader.Assembly"/>.
    /// </summary>
    private ResourceManager ResourceManager
    {
        get
        {
            if (ReferenceEquals(_resourceMan, null))
            {
                ResourceManager temp = new ResourceManager(
                    string.Format("{0}.{1}", Assembly ?? _defaultAssemblyName, ResourcesSuffix ?? DefaultResourcesSuffix),
                    theAssembly ?? _defaultAssembly);
                _resourceMan = temp;
            }
            return _resourceMan;
        }
    }

    #endregion // Properties

    #region Methods --------------------------------------------------------------

    private object GetResource(string name, CultureInfo language)
    {
        if (language == null || language == CultureInfo.InvariantCulture)
            return ResourceManager.GetObject(name);
        return ResourceManager.GetObject(name, language);
    }

    /// <summary>
    /// Provides the implementation for operations that get member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations such as getting a value for a property.
    /// </summary>
    /// <param name="binder">Provides information about the object that called the dynamic operation. The binder.Name property provides the name of the member on which the dynamic operation is performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, where sampleObject is an instance of the class derived from the <see cref="T:System.Dynamic.DynamicObject"/> class, binder.Name returns "SampleProperty". The binder.IgnoreCase property specifies whether the member name is case-sensitive.</param>
    /// <param name="result">The result of the get operation. For example, if the method is called for a property, you can assign the property value to <paramref name="result"/>.</param>
    /// <returns>
    /// true if the operation is successful; otherwise, false. If this method returns false, the run-time binder of the language determines the behavior. (In most cases, a run-time exception is thrown.)
    /// </returns>
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = GetResource(binder.Name, this.culture);

        if (result != null && result.GetType() == typeof(System.Drawing.Bitmap))
        {
            System.Drawing.Bitmap currentBmp = result as System.Drawing.Bitmap;
            currentBmp.MakeTransparent(System.Drawing.Color.Magenta);
            BitmapSource src = Imaging.CreateBitmapSourceFromHBitmap(currentBmp.GetHbitmap(), IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
            result = src;
        }
        return result == null ? false : true;
    }

    /// <summary>
    /// Switch set culture
    /// </summary>
    public void SwitchCulture(CultureInfo NewCulture)
    {
        this.culture = NewCulture;
    }
    #endregion // Methods

    #region Constructors ---------------------------------------------------------

    /// <summary>
    /// Initializes a new instance of the <see cref="ResourceLoader"/> class.
    /// </summary>
    public ResourceLoader()
        : this(CultureInfo.InvariantCulture, DefaultResourcesSuffix)
    { }

    /// <summary>
    /// Initializes a new instance of the <see cref="ResourceLoader"/> class.
    /// </summary>
    public ResourceLoader(CultureInfo InitCulture, string ResourceSuffix)
    {
        _defaultAssemblyName = GetType().Assembly.GetName().Name;
        _defaultAssembly = GetType().Assembly;
        this.culture = InitCulture;
        this.resourcesSuffix = ResourceSuffix;
    }

    #endregion // Constructors
}

Sie können eine Instanz in xaml folgendermaßen erstellen:

<Application x:Class="ResourceFileAccessSample.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"           
         xmlns:src="clr-namespace:ResourceFileAccessSample.ResourceBinding"             
         StartupUri="Window1.xaml" Startup="Application_Startup" >

<Application.Resources>
    <src:ResourceLoader x:Key="resource" CurrentCulture="(Default)" ResourcesSuffix="Resource"   />
</Application.Resources>

C # -Code:

    /// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
    private ResourceLoader res;
    public Window1()
    {            
        InitializeComponent();
        // load it from WPF Resources 
        this.res = (ResourceLoader)this.FindResource("resource");
        // or create an instance 
        //this.res = new ResourceLoader(CultureInfo.InvariantCulture, "Resource");      
        this.LayoutRoot.DataContext = res;                    
    }

    private void btnSwichLanguage_Click(object sender, RoutedEventArgs e)
    {            
        res.SwitchCulture(new CultureInfo("de"));               
        this.LayoutRoot.DataContext = null;
        this.LayoutRoot.DataContext = res;                      
    }       
}

Jetzt ist es möglich, Zeichenfolgen und Bilder zu binden (Bilder werden in den WPF-Compilanten BitmapSource konvertiert:

    <StackPanel Name="LayoutRoot" Orientation="Vertical">
    <Label Name="lblText" Content="{Binding Path=rsName, Mode=OneWay}" HorizontalContentAlignment="Center" Margin="5" Padding="0" />
    <Image Source="{Binding Path=AlignObjectsTop}" Height="16" Width="16" Margin="5" />
    <Button Name="btnSwichLanguage" Content="Switch to de" Click="btnSwichLanguage_Click" MinHeight="25" Width="100" />

</StackPanel>
user3318309
quelle
Dies ist nicht mit dem MVVM-Muster kompatibel, da ResourceLoader DataContext zugewiesen ist, der nicht mehr mit einem ViewModel verwendet werden kann.
Jinjinov
0

Der einfachste Weg, bei dem Sie die Breite des Textfelds auch entsprechend der Textlänge in jeder Sprache definieren können.

Xaml-Code

<TextBlock x:Uid="Greeting" Text="" />

Schauen Sie sich die Ressourcendatei an: - Klicken Sie auf Ansicht

Vikrant Singh
quelle
Bitte formatieren Sie Ihren Code so, geben Sie für alle Codezeilen 4 Leerzeichen davor ein.
Luuklag
-2

Blenden Sie einen anderen Textblock aus und binden Sie den Text. In diesem Textblock befindet sich die Ressource aus .cs

xrhstos
quelle