Oud topic ondertussen, maar ik heb eindelijk gevonden (puur bij toeval) wat mijn problemen veroorzaakte... Omdat andere mensen misschien met soortgelijke problemen zitten, geef ik hier even de oorzaken die bij mij de eerder vernoemde foutmelding gaven en meteen ook een korte handleiding over hoe correct resources implementeren.
Even een kort voorbeeldje schetsen:
We maken een nieuwe c# Windows applicatie in Visual Studio. Met de rechtermuisknop op je projectnaam in de Solution Explorer kies je Add -> Add New Item -> Local Project Items -> Resources -> Assembly Resource File. Geef een naam op voor de resource en klik op Open. In mijn voorbeeld ga ik 3 resource bestanden aanmaken: Resource.resx (waarin de objecten komen zoals afbeeldingen), Localization.resx (standaard localisatie - Engels), en Localization.nl-NL.resx (Nederlandse localisatie). Voor beide localisatie resources voer ik met de Visual Studio resource editor (gewoon dubbelklikken op de resource in de Solution Explorer) als voorbeeld één entry in:
Localization.resx
name: test
value: Test
Localization.nl-NL.resx
name: test
value: Probeersel
Visual Studio's resource editor kan enkel overweg met tekststrings. Om afbeeldingen toe te voegen moeten we een 3th party programma gebruiken. Zelf gebruik ik
Lutz Roeder's .NET Resourcer, een simpel maar effectieve resource editor. Aan Resources.resx voegen we nu een icoon toe met als naam test.ico. Sla de resource op.
Nu moeten we de resources initializeren in de code. Bij de variabeleninitialisatie in je class voeg je toe:
C#:
1
2
| private System.Resources.ResourceManager rmObjects;
private System.Resources.ResourceManager rmLocal; |
Niet tegenstaande we 3 resources hebben, initializeren we er maar 2. Eentje voor de objecten, en eentje voor de gelocaliseerde strings. We kunnen onderscheid maken tussen de standaard localisatie en de nl-NL localisatie door gebruik te maken van cultures. We maken 2 cultures aan (eentje voor de default en eentje voor nl-NL):
C#:
1
2
| private System.Globalization.CultureInfo ciEnglish;
private System.Globalization.CultureInfo ciDutch; |
Dit is natuurlijk maar puur initialisatie, dus er moet nog iets met deze variabelen gebeuren. In de constructer van de class zet je:
C#:
1
2
3
4
5
6
| rmObjects = new System.Resources.ResourceManager("test.Resource",
System.Reflection.Assembly.GetExecutingAssembly());
rmLocal = new System.Resources.ResourceManager("test.Localization",
System.Reflection.Assembly.GetExecutingAssembly());
ciEnglish = new System.Globalization.CultureInfo("en-US");
ciDutch = new System.Globalization.CultureInfo("nl-NL"); |
Eigenlijk is de en-US culture niet nodig. Ze bestaat in principe niet eens binnen het project. Wanneer een culture niet gevonden wordt (bijvoorbeeld fr-FR welke we geen resource voor hebben gemaakt) valt het programma terug op de standaard resource (in ons geval Localization.resx). Dat is net een van de fijne dingen van localisatie met resources. en-US bestaat ook niet, maar er wordt dan sowieso teruggevallen op de Engelstalige standaard resource. Dus in principe zou je de default resource in ons geval als en-US kunnen zien.
Nu moeten we nog een standaard culture kiezen voor ons programma. Laten we Nederlands nemen...
C#:
1
| System.Threading.Thread.CurrentThread.CurrentUICulture = ciDutch; |
En klaar is dat. Het resources gedeelte is nu af en zou moeten werken. Als test voegen we volgende regels toe:
C#:
1
2
| this.Icon = (System.Drawing.Icon)rmObjects.GetObject("test.ico");
this.Text = (string)rmLocal.GetObject("test"); |
Wanneer het programma nu uitgevoerd wordt zal de titel van het venster "Probeersel" zijn uit de Nederlandstalige resource, en het icoon van de applicatie is dit uit Resource.resx.
Nog even voor de duidelijkheid het complete programma:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
| using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Resources;
namespace test
{
/// <summary>
/// Summary description for MainForm.
/// </summary>
public class MainForm : System.Windows.Forms.Form
{
private System.ComponentModel.IContainer components;
/************************************/
private System.Resources.ResourceManager rmObjects;
private System.Resources.ResourceManager rmLocal;
private System.Globalization.CultureInfo ciEnglish;
private System.Globalization.CultureInfo ciDutch;
/************************************/
public MainForm()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
/************************************/
rmObjects = new System.Resources.ResourceManager("test.Resource",
System.Reflection.Assembly.GetExecutingAssembly());
rmLocal = new System.Resources.ResourceManager("test.Localization",
System.Reflection.Assembly.GetExecutingAssembly());
ciEnglish = new System.Globalization.CultureInfo("en-US");
ciDutch = new System.Globalization.CultureInfo("nl-NL");
System.Threading.Thread.CurrentThread.CurrentUICulture = ciDutch;
this.Icon = (System.Drawing.Icon)rmObjects.GetObject("test.ico");
this.Text = (string)rmLocal.GetObject("test");
/************************************/
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
//
// MainForm
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(292, 271);
this.Name = "MainForm";
this.Text = "MainForm";
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new MainForm());
}
}
} |
Wat ging er nu steeds mis want dit lijkt toch wel eenvoudig?
Het is eenvoudig, maar er zijn een paar punten die je in acht moet nemen. Ik had talloze testprojecten gemaakt en vaak werkte het resource gedeelte in den beginne wel goed, maar naarmate ik verder programmeerde kreeg ik ineens die melding. Het was me nooit opgevallen, maar het heeft te maken met de namespace. Ik had de neiging om wanneer een test project (met test-naam en test-namespace) leek te werken alle namen om te zetten naar het project dat ik wilde coden. Dus ook de namespaces werden veranderd. Maar blijkbaar zit de namespace op een of andere manier ook in de resource zelf. Ik veranderde de "test.Resource" (in de code) naar bijvoorbeeld project.Resource en kreeg daarna de foutmeldingen. Maar omdat ik na het veranderen van naam meestal al heel wat code schreef eer ik nog eens testte had ik jammer genoeg nooit de link gelegd. Dus hoewel ik de namespace doorheen m'n hele project veranderde, moesten de resources de oude namespace blijven gebruiken. De juiste oorzaak hiervan weet ik niet, want in de resx resource (geopend met een plain text editor) is geen enkele verwijzing naar de namespace.
Toen ik dat doorhad wilde ik een serieus programma schrijven en toen werkte het weer niet... Na het gips van de muren gebonkt te hebben met m'n hoofd vond ik toch de reden. Ik had een project aangemaakt met een liggend streepje in de naam; firma-client. Omdat een liggend streepje geen geldig teken in een namespace is veranderde visual studio de namespace naar firma_client. Dus met een underscore. Ook de interne hardcoded namespace van de resources gebruikten deze naam met underscore. Maar ik had natuurlijk "firma-client.Resource" met liggend streepje in mijn code staan. Gevolg, zelfde foutmelding. Dat was dus eigenlijk een typo, maar ik had compleet niet opgemerkt dat Visual Studio het streepje had vervangen en gebruikte elders lekker de namespace met het origile streepje.
Dus eigenlijk waren het 2 bijzonder kleine probleempjes die aan de oorzaak van al m'n ellende lagen, maar als je er steeds overheen kijkt dan kan het lang duren eer je een oplossing vindt.
Toch blijven resources iets lastigs. Tijdens m'n zoektochten ben ik een ware stortvloed van mensen die er problemen mee hebben tegengekomen. Velen met dezelfde melding als ikzelf, dus waarschijnlijk ook met een gerelateerd probleem. Ik hoop dat deze post ietwat duidelijkheid brengt.
[
Voor 3% gewijzigd door
mahi op 18-08-2005 11:57
]
A bus station is where a bus stops. A train station is where a train stops... On my desk I have a workstation.