Pregunta

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?

¿Fue útil?

Solución 4

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 The Build Action menu

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.

Otros consejos

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:
Public Resx File
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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top