In a separate project I have a de-serialized xml file with strings that I want to use in my WPF application, these strings are also used within a different project in the same solution, so I can't just move the strings over to the project holding the wpf application.
The program is structured like this:
Project A references B and C
WPF application with event handlers
Project B references C
GUI logic in F#
Project C
XML resource file and de-serializer (written in F#)
Is there a way for me to make a resource or resource dictionary based on the objects from the deserialized xml file? or can I reference the strings stored in the xml file directly?
I would suggest you to use the built-in .resx file, which is included by default in freshly created WPF application. (You can also add it at later time using Project -> Add New Item... -> Resource File.) Just be sure to set the access modifier to "Public" and you will be able to use the strings inside other projects that references the current one:
You will be able to access strings (and other resources as well!) through a strongly-typed, automatically generated wrapper class:
MessageBox.Show(WpfApplication1.Properties.Resources.String1);
You can also set properties using XAML to the strings stored in this file, using the x:Static markup extension, as desribed in (for example) this answer:
xmlns:resx="clr-namespace:WpfApplication1.Properties;assembly=WpfApplication1"
Title="{x:Static resx:Resources.String1}"
Additionally you'll get nice, Visual Studio built-in, UI for editing the file (also visible in the picture).
From what it seems your projects that will use the common resource strings will be implemented using WPF technology. In this case you could change a little the format of your xml and have directly a xaml file containing the serialized form of a ResourceDictionary. After that you can just deserialize your file and you have a ResourceDictionary. Serialization and deserialization can be done using the XamlWriter and XamlReader.
Code example:
var pc = new ParserContext
{
BaseUri = new Uri(runtimeResourcesDirectory , UriKind.Absolute)
};
...
using (Stream s = File.Open(resourceDictionaryFile, FileMode.Open, FileAccess.Read))
{
try
{
var resourceDictionary = XamlReader.Load(s, pc) as ResourceDictionary;
if (resourceDictionary != null)
{
Resources.MergedDictionaries.Add(resourceDictionary);
}
}
catch
{
MessageBox.Show("Invalid xaml: " + resourceDictionaryFile);
}
}
}
The tooling in F# project is not so good, so what I have suggested in my previous answer won't work.
In C# projects, there is custom tool named "ResXFileCodeGenerator" that is run (just before the build) over the .resx file and generates the C# wrapper class. This is obviosly not available in F# projects. You have to workarounds though.
The easiest approach that comes to my mind - that is if you want to use the strongly typed wrapper generated from the C# custom tool - is to add new C# class library to your solution, which will contain the resx file and will generate the wrapper class. You can add the .resx file from Project -> Add New Item... -> Resource File. Then you can reference this library from all your F# projects and use the generated wrapper class:
open ClassLibrary1; // this is the C# library
...
let x = Resources.String1;
The second approach is to add a .resx file directly to your F# project. The funny thing here is that Visual Studio won't allow you to "Add new item" of type Resource. You can workaround this by creating Resource file in another, temporary project - for example of type C# class library, and then add the generated .resx file to your F# project using the Project -> Add Existing Item command. Then you can use the resources from the resx file using the ResourceManager class (of course without the generated properties for every resources, as in the previous case):
open Library1
open System.Resources
[<EntryPoint>]
let main argv =
// We are in the console application, which references Library1
// Library1 contains the .resx file.
let resourceManager = ResourceManager("Resources", typeof<Library1.Class1>.Assembly);
let x = resourceManager.GetString("String1");
0
Note that in the upper case I have added the resource file directly in the project (not in subfloder), as a file named Resources.resx.
You can additionally create your custom wrapper which encapsulates such calls within properties.
Turns out I managed to do something that works like I intended, but instead of using the deserialized objects, I used the xml file itself.
Here's what I did:
First I changed the XML file's Build Action from Content to Resource by going Right-click the file -> Properties -> Build Action -> Resource
Then I went into the Xaml file, and added the following to Window.Resources
<XmlDataProvider
x:Key="DropDownData"
Source="/Resource;component/Strings.xml"
XPath="/Strings/String" />
In the drop-down menu that I needed the strings I added this:
ItemsSource="{Binding Source={StaticResource DropDownData}}"
And now my strings are beautifully displayed in the WPF gui.
Thanks for your suggestions though; they may come in useful in the future.
Related
I have inherited an Xamarin Crossplatform project, (Android only,) that had a 'strings.xml' file with some button-names in it.
Need to find a way to add more languages easily and convert code-strings to translatable too (>300+), not just component-Texts.
I've tried many things so far, but nothing worked as expected: [*]
( Most of the examples and help are for Xamarin.Forms only. )
1.) installed ResX manager
it did not recognized my XML files
created .resx files manually, but those have nothing to do with existing xml-translations
could not get back those texts from C# code I've inserted at the manager window
2.) created a second strings.xml >> placed into values-hu folder >> copied XML content >> translated inside the XML.
It works only for buttons in the IDE placed into the activity
can NOT get text with GetText(...,...) from my C# code because it needs an (int)...I don't know how to generate and pair with my own constants
Resources.Strings... does not pop up any of my own string either
whenever I try to add a new element at the IDE,
I have to search and copy manually the new lines to each XML one by one
there is no "translator window" for them like ResX
can not group strings inside the XML file
3.) installed Multilingual App Toolkit 4.0
seems to be just a different file format than .resx, but same problems
complained about not connected to Azure
offered me a "new english" translation from my original english XML
could not add NEW lines, etc.
4.) installed POEdit + Nuget>GetText
at first sight this seemed to be the perfect solution, but
PoEdit has found only 4 strings in my .cs files
( preferences' I/O strings with GetText() method to read from my config.xml )
could not import string.xml files to translate for 3. lang.
do not understand the .po > .mo conversion concept
installed the nuGet GetText > but no new sub-menu appeared anywhere inside VS.
5.) upgraded to VS2017
6.) asked on a local forum, but nobody answered.
7.) Searched through ca 100 topics here >> ... most of them are about Xamarin Forms ... but those seems to be invalid for droid.
[*] by expected I've imagined having a simple wizard where I can:
go through each string in my *.cs files, where I can give a constant name and it's converting "Really Exit" > to: _tr(Res.Main.really_exit)
or mark to skip
should have a manager to easy translate (like ResX), or ADD new lines!
having both component-texts and code-string inside ONE(/ language?) file
possibly group strings by activity / logic. (So translators can see where is what)
... but nothing seems to be able to handle strings.xml files and .cs file-string all in one easily.
So... Now I am totally confused. I've spent a whole week to see some kind of "order" in this chaos, but could not find a proper writing that explains, WHAT METHOD should someone use to do things easily for droid translation and why? Thank you!
I would go with your second approach because it uses the Android build-in localization system. A benefit of using it is that it already has a fallback-solution included, if a language or a key for one language could not be found.
You can use it in your layout files:
<Button
android:text="#string/LogOn_Login"
or inside code:
var progressDialog = new ProgressDialog(this);
progressDialog.Indeterminate = true;
progressDialog.SetCancelable(false);
progressDialog.SetMessage(GetString(Resource.String.LogOn_ProgressMessage));
But you need to know the difference between an Activity and a Fragment.
In an Activity you just need to call GetString(). Which requires an int, that you get from Resource.String.YOUR_KEY. But in a Fragment you have to give the context. The call changes to Activity.GetString().
New languages or strings can easily added. I usually add new string to the strings.xml inside values-folder first. That way I make sure I have the fallback ready. A new string has the following semantics:
<string name="KEY">VALUE</string>
Filled with real data:
<string name="LogOn_ProgressMessage">Authenticating...</string>
After saving the strings.xml file, it turns out to be good to rebuild the project to make sure the Resource.designer.cs is updated. I sometimes saw the behavior that otherwise my newly added keys where not accessible with Resource.Strings.
When it comes to editing the strings.xml, I am with you that there is no "translator window" and that you have to copy each new key to all supported languages. To overcome this issue we build us a tool that creates all those files for use on every build. This way we support a quite large app with more than 21 languages.
With Xamarin I would go down the .Resx files route. string.xml files are an Android solution and don't fit very well into the .NET world.
How to localize your code is nicely explained here:
https://developer.xamarin.com/guides/xamarin-forms/advanced/localization/
And with Reshaper you can extract all the strings from your code:
https://www.jetbrains.com/resharper/features/internationalization.html
Yes, you will have to convert your XML files. In what format are they? You might be able to use this tool:
http://converter.webtranslateit.com/
To use .NET Resource files inside your Library, you have to add the resources to your project first and configure them like below:
Now you can use them for example like this:
BillManagement.ResourceManager.GetString("TaxSetupWrong")
Or
LibraryName.Resources.BillManagement.TaxSetupWrong
I am relatively new to T4 templates. I am working in visual studio 2012 and using the tangible T4 editor. I have my text templates in one project and I want to read a class in another project and do some processing and write the generated code to a third project. I want to pass in the class file path to my template For example "C:/Code/Project2/ClassFooBar.cs" and the template will read the class from the given location and do some processing with the class properties and write the generated code to project3. I want to pass in the file path since my project has a number of class files and there is not a pattern that I can specify in the template.
My solution structure is:
SolutionFoo:
- Project1
-TextTemplate.tt
- Project2
- ClassFooBar.cs
- Project3
-GeneratedCode.cs
anyone can guide me with a clean way of passing in the class path and a way to writing the generated code to Project3?
In order to avoid the file locking issue or if you do not want to reference your Project2, try what I like to call "Design Time Reflection".
Since you have both projects (The one containing the T4 and the one containing the class you want to reflect) in the same solution, you can use the EnvDTE.CodeModel to transverse all code artifacts in your solution (Namespaces, Classes, ...) without the need to have Project2 built and referenced.
Have a look at the VisualStudio Automation. And if you are using tangible's T4 Editor, there is a free reusable template in their gallery that eases the access to the EnvDTE.CodeModel from within a T4 template.
This might be a good start point: http://t4-editor.tangible-engineering.com/blog/walking-the-visual-studio-code-model-with-t4-template.html
I recently wrote up a description of this for another question. Do not read the class file. Instead, use reflection to work with the generated class. It will be a lot simpler than trying to parse c#.
[Edit - Appending untested pseudo code]
using System.Reflection;
private void Reflect()
{
var properties = typeof(ClassFoobar).GetProperties();
var myRuntimeTemplate = new MyRuntimeTemplate();
myRuntimeTemplate.DoSomeMethodDefinedInT4(properties);
}
I have an application with an existing Form that has images on each ToolStripButton at the top. These images are in the resx file and look as though they are only available to this Form. I want to make another Form with the same images.
What is the proper way to import these images so that all my Forms can use them. For example, the Save and Open buttons will be on most of the Forms.
In a big project at work we have a solution that has many projects with user controls and forms. That is why we have one extra project called Resources that holds different kind of resources (separate resource files) for the whole solution:
strings (translations)
images
property names
and so on
We reference the project to every other project in the solution where a resource is needed. This way the resources are kept separately, the dll can be easily replaced and nothing will be broken, as long the same resource names are used.
We also use the dll assembly in complete different projects(solutions) where we need for example the same images. When named correctly (for example Company.Resources.dll -> Company.Resources.Images.Toolbar.Add/Remove/Settings) it fits very well into every new project.
In your case it is of cource possible to reference ProjectA in ProjectB in order to use the same resources (as long the projects are in the same solution), but it could be that this will be not always possible (for example in order to avoid circular references).
Create new WinForm solution (in my example one solution with two projects). Add new project of type Class Library:
Add new resources to the resources project (in my example for strings and images):
Adding a resource file for images:
Add some images:
Now reference the resources project from the other projects:
Important => In order to use the resources from outside the assembly, you need to set the visibility to public:
Compile the solution once and use the references resources project from everywhere:
Can you please explain the life-cycle of an XAML file in terms of compiling?
When I build a Silverlight project what happens to a XAML file in the build process?
The answer about the intermediate .g.cs file by Jon Skeet (now deleted, but that part quoted below for context) was partly correct but did not answer your actual question in full:
JS: An early part of the build process creates a Foo.i.g.cs file in the
obj directory containing a partial class, and that gets compiled in
the normal way along with your own .cs files.
The .g.cs files contain pieces missing from the code-behind required to connect named elements to class members during InitialiseComponent(). e.g. this is from a basic MainPage.g.cs:
public void InitializeComponent() {
...
System.Windows.Application.LoadComponent(this, new System.Uri("/SilverlightApp1;component/MainPage.xaml", System.UriKind.Relative));
this.LayoutRoot = ((System.Windows.Controls.Grid)(this.FindName("LayoutRoot")));
}
This is bit like the designer files generated for WinForms controls/dialogs, but happens at compile time instead of design time.
The compiler parses to validate the XAML and to determine what named elements need to be generated (into the partial class).
The XAML file itself gets stored as a resource in the DLL during the build (in the example above it is stored as "/SilverlightApp1;component/MainPage.xaml")
Note: For WPF only, the embedded XAML file is actually converted to a more memory-efficient binary version referred to as a BAML file.
Answering your comment to Jon Skeet (now deleted), you are partially correct in asking:
So parsing the whole XAML document -all the elements, attributes etc.-
is not part of the WPF or SL builds? Am I correct?
Aside from the parsing mentioned above, for validation and named elements, the rest of the parsing (of the element tree and templates etc) is actually done at runtime when LoadComponent() effectively deserializes the XAML and creates a visual tree of elements you authored.
When you build a Silverlight project a .xap file is created, this is basically just a .zip-file with another extension, containing an AppManifest.XAML and your project in a DLL-file (together with DLL:s for other dependencies).
If you run the DLL through dotPeek or Reflector you'll see that the XAML-files you created are found intact in the resources of the dll.
I am trying to replace a Resource of an exe (.NET, C#) file using C# code.
I have found this article and made this code (using Mono.Cecil 0.6):
AssemblyDefinition asdDefinition = AssemblyFactory.GetAssembly("C:\\File.exe");
EmbeddedResource erTemp = new EmbeddedResource("encFile", ManifestResourceAttributes.Public);
erTemp.Data = myNewFileBytes;
asdDefinition.MainModule.Resources.RemoveAt(0);
asdDefinition.MainModule.Resources.Add(erTemp);
AssemblyFactory.SaveAssembly(asdDefinition, "C:\\newFile.exe");
The code is actually removing the resource and then adding a new one with the same name.
The resource name is encFile and stored as encFile.exe (tried both).
I tested the code and the remove is working (i can tell by the size of the file) and the adding too, but the new file crash just like the file i created with the remove only (for the testing) - it acts like he can't see the replaced resource.
What can i do to fix it? Maybe some changes in the edited EXE file?
The EXE file reads its resource this way:
byte[] buffer = ProjectName.Properties.Resources.encFile;
Trying to do this seems overly complex. If you need dynamic update of resources, ship your resources as a folder for your application (set items in the folder as content and copy if newer in project properties).
If you need dynamic update at runtime, then it's as simple as either:
1] Allow user to replace items in place or
2] Even better, treat it like word-press themes and allow an override folder for each resource.
If you need to tag each resource with metadata you could use a sqlite database or even easier, allow a matching .meta file for each resource to describe it in more detail.
Finally, if you are allowing digital download of your software, then you might consider code-signing your executable - in which case modifying the executable in any way will not be an option.