Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Tuesday, January 20, 2009

Application.Settings, complex types, and you

So i came across the need to serialize some user settings between instances of an application. I happen to find the Application.Settings object and its "supposed" simple and straightforward uses.
With the Settings object you can add types at design time (or run time) in a key-value pair that load automatically with the application. and when set properly, you could also save values at runtime too. Best of all, the designer even allows you to add complex types. What luck!
...
...
or not...

Apparently while simple primitive types (int, string, colour) are supported, complex ones (youClass, PageSettings, PrinterSettings) arent... but the designer didnt get the memo. This may be an uninformed rant, but since i could find jack squat on the subject in my few days of research, i feel i can rant freely.

So yes, you can add complex types to the Application.Settings file. and yes, when you save you will see your super duper object all serialized in XML just the way you'd think it would be. But when you fire that bad boy up again and try to load that object, well... dont be surprised you get a null value instead.

to get around that, i did your simple serialize/deserialize manually. so what i needed was to save the user's printer settings after they saved them and wanted to keep them persistent between sessions.
with a little help from the following assemblies
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;


we had the simple following:

private string str_page_settings = "page.set";
private string str_printer_settings = "printer.set";

internal void LoadSettings()
{
try
{
FileStream fs;
BinaryFormatter bf = new BinaryFormatter();

// load the PageSettings
if (File.Exists(str_page_settings))
{
fs = new FileStream(str_page_settings, FileMode.Open);
pageSetupDialog1.PageSettings = (PageSettings)bf.Deserialize(fs);

if (pageSetupDialog1.PageSettings == null)
{
pageSetupDialog1.PageSettings = new PageSettings();
}
}
else
pageSetupDialog1.PageSettings = new PageSettings();

// load the PrinterSettings
if (File.Exists(str_printer_settings))
{
fs = new FileStream(str_printer_settings, FileMode.Open);
printDialog1.PrinterSettings = (PrinterSettings)bf.Deserialize(fs);

if (printDialog1.PrinterSettings == null)
{
pageSetupDialog1.PrinterSettings = new PrinterSettings();
printDialog1.PrinterSettings = new PrinterSettings();
}
}
else
{
pageSetupDialog1.PrinterSettings = new PrinterSettings();
printDialog1.PrinterSettings = new PrinterSettings();
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + " " + ex.StackTrace);
}
}


for loading, and for saving after the user saved:

private void btnPrint_Click(object sender, EventArgs e)
{
if (printDialog1.ShowDialog() == DialogResult.OK)
{
printDocument1.Print();
// serialize the printer settings the user chose
FileStream fs = new FileStream(str_printer_settings, FileMode.Create);
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, printDialog1.PrinterSettings);
fs.Close();
}
}

Wednesday, August 20, 2008

Resource files, satellite assemblies, and you

So while at work doing a POC for a C# windows form, i was fiddling with the resource files for my app. If you are not aware of them, resource files are compiled text or XML files that contain key-value pairs. You create a form (or a website if yer doing ASP.NET) that has some default textboxes, labels, and such. Then you create some text/resx files that contain the key-value pairs and rename them to match the culture you are using (en-US for US english, de-DE for german, etc).
After adding a custom section in the app.config file via an example from this guy Derik, i was now able to add languages to a drop down on the fly.
The next step was to create the resource files for each of the languages. Now creating them through the 2k5 IDE is obviously the simple and easier way to go as the compilation and generation of resources is done for you at compile time.

But my requirements needed to be able to compile the app once and add language resource files at any given time. While there is still a process to do it, it can be done with very little effort.
The steps include:
- create a text file or resx file and add some key-value pairs
- compile the file into a resource file using the resgen executable that comes along with Visual Studio
- link the resource file into a DLL
- create a new folder in the working folder, named after the culture you are adding
- plop it in, edit the app.config and POOF, yer off

Now as easy as it sounds, i did run into issues. The main error message being:

Could not find any resources appropriate for the specified culture or the neutral culture. Make sure "YOUR RESOURCE HERE" was correctly embedded or linked into assembly "YOUR ASSEMBLY" at compile time, or that all the satellite assemblies required are loadable and fully signed.


There were some details left out in the many examples out on the interwebs and it seemed a lot of people came across the same issue i had. Basically, after compiling the resource file and linking to a DLL, the application still couldnt find said DLL and its resources.
It turns out that when you compile the resource file, you need to add the namespace of the calling application thats using it.
Before, i was doing this:

resgen german.txt Form1.de-DE.resources


where Form1 was the default form name from Visual Studio. Where as the correct way to do it was to run it as
resgen german.txt TestAPP.Form1.de-DE.resources


and then from there we need to link it to a DLL

Al.exe /t:lib /embed:TestAPP.Form1.de-DE.resources /culture:de-DE /out:TestAPP.resources.dll


After putting it in the correct folder, things went great. Now im currently working on getting a datetime object to format correctly for neutral cultures... if anyone has info on that let me know