how would I edit a string in the resources of my project? I get this error when I try it:
Property or indexer 'Project.Properties.Resources.ExternalIp' cannot be assigned to -- it is read only
This is what I do:
Resources.ExternalIp = utf8.GetString(webClient.DownloadData("http://automation.whatismyip.com/n09230945.asp"));
Properties.Ressources are readonly ("compiled"), you have to use Properties.Settings & put the Scope to "User" so it will be 'ReadWrite'
Project.Properties.Settings.Default.ExternalIp = utf8.GetString(webClient.DownloadData("http://automation.whatismyip.com/n09230945.asp"));
Project.Properties.Settings.Default.Save();
Resources are not supposed to be written to; they're embedded in the executable, so changing them would involve modifying the executable.
From your code, it looks like you actually need application settings, not resources.
Related
I know it's easy to localize Windows Forms App: set Localizable=True, change Language and set text in Controls for every Language. This information saves in resx-files and application will automatically select the required file. Great!
I know about disadvantages of this solution (you need to rebuild the app if there a typo, it's impossible to change language in runtime, etc), but it's not a problem for me and "resources" is the simpliest, built-in solution.
But this mechanism uses the property Culture of app's thread.
My app is the part ("plugin") of the bigger application and works in the same Thread.
The main application is multilingual too, but it doesn't use Culture to change interface's language. I can change the thread's culture globally, but it crushes the main app's interface.
So my question:
is it possible to manually set the resx-localizable resurce file that will be used? Not based on Culture, but, for example, on some variable in my app:
if (this.Language == "fr")
this.Resources.Add("Form1.fr.resx");
else
this.Resources.Add("Form1.en.resx");
Or something else.
Thank you!
My sandbox:
https://github.com/Tereami/WindowsFormsTestLanguage
The built resources file has a property ResourceManager that's used to return the desired content. This has an overload with a CultureInfo parameter. You can use it to request resources in individual languages:
var desiredCulture = new CultureInfo("en-us");
var text = MyStrings.ResourceManager.GetString(nameof(Resources.ExitMessage), desiredCulture);
If you want to set the culture globally for your resource file, you could also set it through the corresponding property:
var desiredCulture = new CultureInfo("en-us");
MyStrings.Culture = desiredCulture;
var text = MyStrings.ExitMessage;
Background:
On an application I am working on, I am writing tests using a mixture of Visual Studio 2015, SpecFlow, and ReSharper 2016.3 (I'll abbreviate this as R#, because I'm lazy.)
The application I am working on sends HTML-formatted emails, based on a template, which are stored in HTML files that are set as Copy Always in Visual Studio 2015.
Problem:
When I attempt to run my tests, I get the following exception:
System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\Users\[Me]\AppData\Local\JetBrains\Installations\ReSharperPlatformVs14_001\Resources\SomeEmailTemplate.html`
The directory was not the output directory of the project I am working on, so I double-checked my R# settings, and confirmed that Shadow Copy was turned off. To be perfectly clear, my R# Shadow Copy checkbox is indeed unchecked.
The offending code is really pretty simple. Normal remedies like TestContext.CurrentContext.TestDirectory is not something I can, should, or even want to do, due to the fact that this code is needed by the application itself. It would be in appropriate to put test framework code in the application under test.
public class HtmlTemplateLog : ISectionedLog, IRenderableLog
{
#region Variables / Properties
private readonly string _rawHtml;
private readonly Dictionary<string, StringBuilder> _sectionDictionary = new Dictionary<string, StringBuilder>();
private StringBuilder _workingSection;
#endregion Variables / Properties
#region Constructor
public HtmlTemplateLog(string templateFile)
{
// This is specifically what breaks the tests.
_rawHtml = File.ReadAllText(templateFile)
.Replace("\r\n", string.Empty); // Replace all newlines with empty strings.
}
#endregion Constructor
#region Methods
// Methods work with the section dictionary.
// The RenderLog method does a string.Replace on all section names in the HTML.
// These methods aren't important for the question.
#endregion Methods
This is invoked as in the example below:
_someLog = new HtmlTemplateLog("Resources/SomeEmailTemplate.html");
// ...code...
_someLog.WriteLineInSection("{someSection}", "This is a message!");
string finalHtml = _someLog.RenderLog();
Questions:
1. I've turned off Shadow Copy on my R# tests. Why is this still doing Shadow Copies?
2. In what ways can I work around the fact that R# is not respecting the Shadow Copy checkbox, given that this is not test code, and thus that remedies that would normally be appropriate for test code aren't for this case?
I've discovered an answer for #2...though, it's rather clunky. I was inspired by the answer from #mcdon for a less-detailed version of the question.
Pretty much, if I don't want to resort to TestContext.CurrentContext.TestDirectory, then I need to make my local filenames into absolute paths. Unfortunately, R#'s broken Shadow Copy setting creates more work, since I can't just interrogate the currently-executing assembly - it will tell me the wrong thing. I need to get at the codebase instead and interrogate that.
I'm still a bit worried about what this code when we try to run it on the build server, however - I'm expecting 'unexpected' results. In that light, I'm wondering if the unexpected results can truly be called unexpected, given that I'm expecting that this won't work...
Anyways, the fix I came up with was this field-property system:
private string _presentWorkingDirectory;
private string PresentWorkingDirectory
{
get
{
if (!string.IsNullOrEmpty(_presentWorkingDirectory))
return _presentWorkingDirectory;
var assembly = Assembly.GetExecutingAssembly();
var codebase = new Uri(assembly.CodeBase);
var filePath = codebase.LocalPath;
var path = Directory.GetParent(filePath);
_presentWorkingDirectory = path.ToString();
return _presentWorkingDirectory;
}
}
I have put 2 strings in the built-in resource file of my main project (not sure how I define this properly). How can I access these values? How would I change them? The resource is public so that the user can change them.
eg:
Project1.Resources.Get("string1").Value();
and
Project1.Resources.Set("string1") = "whatever";
This is pseudo code.
Resources are not intended to be changed at runtime. You should consider using user settings instead. For details, see Using Application Settings and User Settings on MSDN.
This will allow you to use the designer to build the settings, and write:
string string1 = Properties.Settings.Default.String1;
And:
Properties.Settings.Default.String1 = "whatever";
Properties.Settings.Default.Save();
You can get resource by writing the following piece of code
Project1.Resources.String1
You can not change it at runtime.
I know I can read environment variables like this:
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
However, it would be really helpful to me if I could do something like this:
Environment.ExpandEnvironmentVariables(#"%MyDocuments%\Foo");
Is there an environement variable that equals SpecialFolder.MyDocuments?
I also tried to do something like this, but this doesn't lead to the expected result:
Environment.ExpandEnvironmentVariables(#"%USERPROFILE%\My Documents\Foo");
This way I ended up with something like #"C:\Users\<MyUser>\My Documents\Foo" but I what I need is #"\\someservername\users$\<MyUser>\My Documents\Foo".
EDIT: My Goal is NOT to hardcode either environment variable nor the part after that.
Any other suggestions?
No there is no environment variable for the MyDocuments special folder (the same is true for most members of the SpecialFolder enumeration).
Check out this snippet, it might be exactly what you are searching for.
It allows you to do something like that:
string fullPath = SpecialFolder.ExpandVariables(#"%MyDocuments%\Foo");
Note: SpecialFolder.ExpandVariables is a static method of a helper class introduced in the above snippet.
Is there an environment variable that equals SpecialFolder.MyDocuments?
Short answer: No.
Long answer:
Still no. You can type "set" into a Command Prompt to see all you current environment variables. I couldn't find any for my documents folder on my profile (tried on WinXP and Win7).
Also, expanding "%USERPROFILE%\My Documents" would be incorrect since the user's documents folder could be anywhere else (e.g., on my home PC I always change mine to D:\Documents).
If you really need to use environment variables, one solution might be to set the variable yourself:
// this environment variable is created for the current process only
string documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Environment.SetEnvironmentVariable("MYDOCUMENTS", documents);
Another solution might be to use a "fake" environment variable in the path and expand it yourself, something like:
string path = "%MYDOCUMENTS%\\Foo"; // read from config
// expand real env. vars
string expandedPath1 = Environment.ExpandEnvironmentVariables(path);
// expand our "fake" env. var
string documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string expandedPath2 = path.Replace("%MYDOCUMENTS%", documents);
What exactly are you trying to do? Is there any reason why you can't just use Path.Combine?
string docs = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string foo = Path.Combine(docs, "Foo");
I'm not sure if there is a good way to do this but instead of trying to do environment expansion to get the path why not use the Path.Combine API instead?
Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"Foo");
You can expand environment variables using then Environment.GetEnvironmentVariable method. Given your comment, I would suggest breaking your path up into 2 separate config settings to make expanding it easier:
string variablePath = "%appdata%".Trim('%'); //read from some config setting
string appdataPath = Environment.GetEnvironmentVariable(variablePath);
string subdir = "foo"; //some other config setting
string myDir = Path.Combine(appdataPath, subdir);
No it does not exist. The easiest way to check is to run "set" from command line and see yourself.
Start-> run -> cmd
set
set |findstr /i documents
If I try to access this system variable from the Run... dialog, Windows tells me the directory doesn't exist. Some system variables, like %SYSTEMROOT% and %USERPROFILE%, do work. Consequently, if I try to use a supposedly nonexistent variable like %DEFAULTUSERPROFILE% or %PROFILESFOLDER% in C#, I get nothing in return. Is there something special I need to do to get access to these variables?
Have you tried %ALLUSERSPROFILE%?
I need to point to
C:\Users\Default\AppData.
Are you sure? Be aware that this folder is used to populate the inital AppData directory for each new user added to the system.
If you want the actual shared application data directory in .NET, it's this:
String commonAppData = Environment.GetFolderPath(Environment.SpecialFolders.CommonApplicationData)
My suggestion is to retreive that value directly from the registry - in case you can't expand it:
public static string GetDefaultUserProfilePath() {
string path = System.Environment.GetEnvironmentVariable("DEFAULTUSERPROFILE") ?? string.Empty;
if (path.Length == 0) {
using (Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList")) {
path = (string)key.GetValue("Default", string.Empty);
}
}
return path;
}
You mention C# - you can't use environment variables inside C# path strings, you need to replace them using System.Environment.
System.Environment.GetEnvironmentalVariable("USERPROFILE");
I haven't seen %DefaultUserProfile% before - should it point to the first username that was installed?
Call SHGetFolderLocation with CSIDL_PROFILE and -1 as the token parameter