I have a MenuStrip control with all translations entered for several languages. When I try to change the culture dynamically all controls are changing the language as it must be except the MenuStrip. Here is the code which I used specifically for MenuStrip:
protected ComponentResourceManager res2;
private void signOutToolStripMenuItem_Click(object sender, EventArgs e)
{
LoginForm login = new LoginForm();
if (login.ShowDialog() == DialogResult.OK)
{
//this is for other controls
resManager = new ComponentResourceManager(this.GetType());
this.ApplyResources(this, Thread.CurrentThread.CurrentCulture);
//this is for MenuStrip
res2 = new ComponentResourceManager(typeof(MenuStrip));
this.ApplyResourcesToolStripMenuItem(Thread.CurrentThread.CurrentCulture);
}
}
private void ApplyResourcesToolStripMenuItem(CultureInfo lang)
{
foreach (ToolStripItem m in this.menuStrip1.Items)
{
//resources.ApplyResources(m, m.Name, lang);
if (m is ToolStripMenuItem)
{
string text = res2.GetString(m.Name + ".Text", lang); //Exception is thrown here
if (text != null)
m.Text = text;
}
}
}
I used another developer's code sample from this forum and made minor changes.
Now when I change culture at runtime, I get the following exception:
An unhandled exception of type 'System.Resources.MissingManifestResourceException' occurred in
mscorlib.dll
Additional information: Could not find any resources appropriate for
the specified culture or the neutral culture. Make sure
"System.Windows.Forms.MenuStrip.resources" was correctly embedded or
linked into assembly "System.Windows.Forms" at compile time, or that
all the satellite assemblies required are loadable and fully signed.
I have checked the resource file and all MenuStripItems have all translations/values there...
I failed to find solution in the internet, so would highly appreciate your help.
Thanks in advance!
So since I'm a beginner in localizing, the solution is much more easier than one that I have posted. Maybe this can help for other beginners too.
So I removed all my code which actually was a bulk of headache and replaced it with the following code:
Thread.CurrentThread.CurrentUICulture = new CultureInfo(StaticValues.currentCulture);
this.Controls.Clear();
this.InitializeComponent();
And it worked easily. And yes, thanks to Adriano Repetti!
Related
When adding a Next and Previous navigation option to my Image viewer coded in C#, when I press Next about 20 or so times, Visual Studio tells me the process ran out of memory. It does this in any folder with many even if the image file sizes for them all are tiny
I get:
An unhandled exception of type 'System.OutOfMemoryException' occurred in System.Drawing.dll Additional information: Out of memory.
This is the code I am using
private void next_Click(object sender, EventArgs e)
{
string[] foldernm = Directory.GetFiles(Path.GetDirectoryName(lfoto_file.FileName));
_pictureIndex++;
if (_pictureIndex >= foldernm.Length)
{
_pictureIndex = 0;
}
ibread_img.Image.Dispose();
ibread_img.Image = Image.FromFile(foldernm[_pictureIndex]);
}
Now as you can see, I have ibread_img.Image.Dispose(); there because I have searched about this and other people said to use that, but it doesn't work and I still get the same problem, a break-point confirms the code is being ran so I am confused to why its still running out of memory. The images I am cycling through are not large. I have tried everything I could find including nulling the previously loaded image, manually calling the garbage collector and nothing seems to work. I am not the best at C# so there might be a horrible mistake or flaw in that code but I don't know, any ideas on how to fix this?
There are a couple of things you could do to improve your viewer. First, you are recreating the list of image files every time; you are loading all of them each time just to access the next one and you dont have to create an image in order to show it.
// class level vars
int picIndex = 0;
IEnumerable<string> files;
int filesCount;
string picPath;
static string[] imgExts = {".png", ".jpg",".gif"};
Since you mentioned a Next and Previous button, you must have almost the same code elsewhere. This will eliminate that duplication, Next:
ShowImage(picIndex);
picIndex+=1;
if (picIndex >= filesCount)
picIndex = 0;
Then a method to show the desired image:
private void ShowImage(int Index)
{
// create image list if needed (once)
if (files == null)
{
files = new DirectoryInfo(picPath).EnumerateFiles().
Where(q => imgExts.Contains(q.Extension.ToLowerInvariant())).
Select( z => z.FullName);
filesCount = files.Count();
}
string thisFile = files.ElementAt(Index);
// no need to dispose an image if you never create one
pb2.ImageLocation = thisFile;
lblImgName.Text = Path.GetFileName(thisFile);
}
Rather that create the list of files each time (in 2 places) this does it once ever, and instead of loading a List of all of them, this leaves it as IEnumerable to get them as needed. It also works off FileInfo, is case insensitive mainly to illustrate a different way which would allow you to sort them (OrderBy) by the created date, if you wanted.
Finally, given the full path and file name, you can use the .ImageLocation property and avoid creating and disposing of Images.
The main thing is to minimize the amount of repeated code so you Dont Repeat Yourself. The code for Next and Previous is going to be almost identical.
Thanks to LarsTech and Plutonix for pointing out my mistake. This new code works fine now:
private void next_Click(object sender, EventArgs e)
{
var filteredFiles = Directory.EnumerateFiles(Path.GetDirectoryName(lfoto_file.FileName))
.Where(file => file.ToLower().EndsWith("jpg") || file.ToLower().EndsWith("png") || file.ToLower().EndsWith("gif") || file.ToLower().EndsWith("bmp") || file.ToLower().EndsWith("tiff") || file.ToLower().EndsWith("ico"))
.ToList();
_pictureIndex++;
if (_pictureIndex >= filteredFiles.Count)
{
_pictureIndex = 0;
}
ibread_img.Image.Dispose();
ibread_img.Image = Image.FromFile(filteredFiles[_pictureIndex]);
init();
}
I just needed to filter out the correct formats.
I have been googling for the last few days but without a result.
I have a Form called MainForm, i placed four user controls on it.
These user controls contain labels and buttons. Now i created a file called Language.cs
In this file i want to change the languages for all labels when a button is pressed in one of my user controls.
When i coded this in ucSettings.cs i would do it like:
this.label1.Text = res_man.GetString("label_text", cul);
But this doesnt work, beccause my Resourcemanager and my Culture info are both in another file.
So i have
MainForm contains four user controls
The user controls are called, ucAnimalInfo, ucAnimalInput, ucSettings and ucMenuStrip
I have a button in ucSettings that would have to change the language
The text should be set in the file Language.cs
As you can see i change the culture info when a language is selected in a ComboBox:
private void Settings_Language_Cbox_SelectedIndexChanged(object sender, EventArgs e)
{
string SelectedIndex = Settings_Language_Cbox.SelectedItem.ToString();
switch (SelectedIndex)
{
case "English(English)":
ci = CultureInfo.CreateSpecificCulture("en-US");
LanguageSelection = 5;
break;
case "Nederlands(Dutch)":
ci = CultureInfo.CreateSpecificCulture("nl-NL");
LanguageSelection = 6;
break;
}
Now i would like to do something like:
private void Settings_Save_Btn_Click(object sender, EventArgs e)
{
Language.Change();
}
So that it calls my Change method in Language.cs and change a the labels to the correct language. But now i am not able to acces any of the labels in Language.cs even tho they are on public, also my Language.cs file isnt able to get the Resourcemanager and CultureInfo from ucSettings.cs. So my question is, wat is the best way to handle this? I tried using a get/set method but this didn't work out at all, now i am not sure if it is because i messed this up or not.
Edit: I got close by doing it like:
ucSettings.cs
private void Settings_Save_Btn_Click(object sender, EventArgs e)
{
Settings_Language_Cbox.SelectedIndex = LanguageSelection;
BusinessClasses.Language language = new BusinessClasses.Language();
language.setLanguage();
}
Language.cs:
public class Language
{
public MainForm mainform;
public ucAnimalInfo animalinfo;
public ucAnimalInput animalinput;
public ucSettings settings;
public void setLanguage()
{
mainform.Animal_Info_Tab.Info_Id_Text.Text = mainform.Settings_Tab.rs.GetString("Info_Save_Btn", mainform.Settings_Tab.ci);
}
it still gives a NullReferenceException so it not really working, but the closest i got so far. rs and ci are the resourcemanager and cultureinfo
Your question is not very clear, you say user controllers but mean? user control...
As you are new to Windows Mobile/Compact Framework you should start with a simple project first and then evolute to what you want, using small steps.
A good starting for language resource localization maybe at http://www.codeproject.com/Articles/28234/Survival-guide-to-do-resource-localization-using-C.
Use that and then extend with a simple user control and so on.
Then also read http://www.codeproject.com/Articles/16091/User-Interface-Localization-with-the-Compact-Frame about changing the languge 'on-the-fly'.
Please just supply your idea / need (I want to allow this and show that) if you need further assistance.
I'm currently developing a metro app in which the user can change current language at runtime and all the custom controls that are loaded must update their text regarding to the new language. Problem is that when I change the language using the following code, the app language changes but it will only update text when I restart my app because the pages and controls that are already rendered are cached.
LocalizationManager.UICulture = new System.Globalization.CultureInfo((string)((ComboBoxItem)e.AddedItems[0]).Tag);
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = ((ComboBoxItem)e.AddedItems[0]).Tag as String;
What should I do to force updating text of all custom controls at runtime without restarting my app?
Use this:
var NewLanguage = (string)((ComboBoxItem)e.AddedItems[0]).Tag;
Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = NewLanguage;
Windows.ApplicationModel.Resources.Core.ResourceContext.GetForViewIndependentUse().Reset();
//Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView().Reset();
Windows.ApplicationModel.Resources.Core.ResourceManager.Current.DefaultContext.Reset();
and then reload your Page, using Navigate method:
if (Frame != null)
Frame.Navigate(typeof(MyPage));
In order to respond right away, you would need to reset the context of the resource manager.
For Windows 8.1:
var resourceContext = Windows.ApplicationModel.Resources.Core.ResourceContext.GetForCurrentView();
resourceContext.Reset();
You will still need to force your page to redraw itself and thus re-request the resources to get the changes to take place. For Windows 8, you can see https://timheuer.com/blog/archive/2013/03/26/howto-refresh-languages-winrt-xaml-windows-store.aspx
You can change the app's language at runtime with the help of this source code. I took help from this and manipulated my app's language settings page as follows:
In languageSettings.xaml.cs:
public partial class LanguageSettings : PhoneApplicationPage
{
public LanguageSettings()
{
InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
if (ChangeLanguageCombo.Items.Count == 0)
{ ChangeLanguageCombo.Items.Add(LocalizationManager.SupportedLanguages.En);
ChangeLanguageCombo.Items.Add(LocalizationManager.SupportedLanguages.Bn);
}
SelectChoice();
}
private void ButtonSaveLang_OnClick(object sender, RoutedEventArgs e)
{
//Store the Messagebox result in result variable
MessageBoxResult result = MessageBox.Show("App language will be changed. Do you want to continue?", "Apply Changes", MessageBoxButton.OKCancel);
//check if user clicked on ok
if (result == MessageBoxResult.OK)
{
var languageComboBox = ChangeLanguageCombo.SelectedItem;
LocalizationManager.ChangeAppLanguage(languageComboBox.ToString());
//Application.Current.Terminate(); I am commenting out because I don't neede to restart my app anymore.
}
else
{
SelectChoice();
}
}
private void SelectChoice()
{
//Select the saved language
string lang = LocalizationManager.GetCurrentAppLang();
if(lang == "bn-BD")
ChangeLanguageCombo.SelectedItem = ChangeLanguageCombo.Items[1];
else
{
ChangeLanguageCombo.SelectedItem = ChangeLanguageCombo.Items[0];
}
}
}
***Note: Before understanding what I did on LanguageSettings page's code behind, you must implement the codes from the link as stated earlier. And also it may be noted that I am working on windows phone 8
What is the proper way to change Form language at runtime?
Setting all controls manually using recursion like this
Save language choice to file > Restart Application > Load languge
choice before InitializeComponent();
Using Form constructor to replace instance of active from (if this is even possible)
Something else
There is so much half written threads about this but none provides real answer on what is proper way to do this?
UPDATE:
To clarify my question:
Doing something like this:
public Form1()
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("de");
this.InitializeComponent();
}
works fine and all my controls and everything else in resources get translated correctly.
And doing something like:
private void button1_Click(object sender, EventArgs e)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("en");
}
does nothing, Form stays in language I set up before InitializeComponent();
I believe the solution shown in Hans Passant's comment might be the only (general) solution.
Personally, I use this base class for all forms that need to be localized:
public class LocalizedForm : Form
{
/// <summary>
/// Occurs when current UI culture is changed
/// </summary>
[Browsable(true)]
[Description("Occurs when current UI culture is changed")]
[EditorBrowsable(EditorBrowsableState.Advanced)]
[Category("Property Changed")]
public event EventHandler CultureChanged;
protected CultureInfo culture;
protected ComponentResourceManager resManager;
/// <summary>
/// Current culture of this form
/// </summary>
[Browsable(false)]
[Description("Current culture of this form")]
[EditorBrowsable(EditorBrowsableState.Never)]
public CultureInfo Culture
{
get { return this.culture; }
set
{
if (this.culture != value)
{
this.ApplyResources(this, value);
this.culture = value;
this.OnCultureChanged();
}
}
}
public LocalizedForm()
{
this.resManager = new ComponentResourceManager(this.GetType());
this.culture = CultureInfo.CurrentUICulture;
}
private void ApplyResources(Control parent, CultureInfo culture)
{
this.resManager.ApplyResources(parent, parent.Name, culture);
foreach (Control ctl in parent.Controls)
{
this.ApplyResources(ctl, culture);
}
}
protected void OnCultureChanged()
{
var temp = this.CultureChanged;
if (temp != null)
temp(this, EventArgs.Empty);
}
}
Then instead of directly changing Thread.CurrentThread.CurrentUICulture, I use this property in static manager class to change UI culture:
public static CultureInfo GlobalUICulture
{
get { return Thread.CurrentThread.CurrentUICulture; }
set
{
if (GlobalUICulture.Equals(value) == false)
{
foreach (var form in Application.OpenForms.OfType<LocalizedForm>())
{
form.Culture = value;
}
Thread.CurrentThread.CurrentUICulture = value;
}
}
}
I have found another way:
Move initialization form code in a private method like below:
private void FormInitialize() {/*Your code here*/}
In the form constructor use it like this:
public Form1()
{
InitializeComponent();
FormInitialize();
}
And from Button, menuItem or other call method like this:
private void ChangeCultureToFrench_Click(object sender, EventArgs e)
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr");
this.Controls.Clear();
this.InitializeComponent();
FormInitialize();
}
I hope this helps ;-)
I've discovered this kind of approach a few minutes ago. Just quick and simple restart of the main form. Meybe it will help to someone. Event is created inside the form on my own, called when user selects the language from menu but after the selected culture's name is saved into the settings. Culture names are then loaded from that settings. Works exactly as I need and looks like proper solution.
static class Program
{
private static bool doNotExit = true;
private static FormMain fm;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
while(doNotExit)
{
System.Threading.Thread.CurrentThread.CurrentCulture = new CultureInfo(Properties.Settings.Default.language);//
System.Threading.Thread.CurrentThread.CurrentUICulture = new CultureInfo(Properties.Settings.Default.language);//
doNotExit = false;
fm = new FormMain();
fm.lanugageChangedEvent += new EventHandler(main_LanugageChangedEvent);
Application.Run(fm);
}
}
static void main_LanugageChangedEvent(object sender, EventArgs e)
{
doNotExit = true;
fm.Close();
}
}
In reference to your ColumnHeader .NET framework bug, I also discovered this bug recently and posted a question about it (to which I haven't received any responses). I was able to fix the problem by hardcoding the changes for the ColumnHeaders. For example:
resources.ApplyResources(_myHeader, "_myHeader", culture);
You basically just replace the call to .Name with a literal string of the name. I have tested this and it works. Unfortunately this means it won't fit as part of the code you use to change all of the controls. You will have to add a line for each ColumnHeader object you need to change. If you have a listview with a variable number of columns, that could get tricky. Another option is to create localized resource files. I assume you probably already have them for stuff like message box text and other strings. Then you can add an entry in your resource file like "columnHeader_myHeader" and set the appropriate language text for each language. Finally, you can manually change the text to your column headers by using:
_myHeader.Text = myResourceFileName.columnHeader_myHeader;
This will select the appropriate language based on the current thread culture.
Hans was correct in that there doesn't seem to be a foolproof "proper" way to perform localization in .NET, though there are a variety of tools you can use. For something like a job application, even though it is probably already too late for this advice, my suggestion would be to learn as many different methods as you can for localization, learn the pros and cons, and then just pick a system and be able to argue why you believe it is the "proper" choice. They are probably more concerned with your logic and reasoning and demonstration of prior experience than they are with the actual method.
Hope this would help anyone, I found it best for me cause i needed to change buttons location according the lang (browse on the right or left of the search box and labels next to text fields).
save a public var on the main that will hold the lang.
created a class which handles the visual part
created xml files that will hold any language data and more (in my xml tag name=object name).
sent that class's constructor the form (to save and work with)
connect to that current xml file
From main form call whenever you want to initialView (part of the view class) and change lang (and more) anytime (just connect to the right xml file):
public void initialView()
{
//Set rightToLeft values
initialIndent(mainForm);
//set visual text values
initialTxt();
}
private void initialTxt()
{
// Are there any more controls under mainObj (Form1)?
Boolean endOfElemsUnderPnl = false;
// The current Control im working on
Object curObj = mainForm;
do
{
// MenuStrip needs to be handled separately
if (typeof(MenuStrip).ToString().Equals(curObj.GetType().ToString()))
{
foreach (ToolStripMenuItem miBase in ((MenuStrip)(curObj)).Items)
{
miBase.Text = mainForm.dbCon.getData(miBase.Name.ToString());
foreach (ToolStripMenuItem miInnerNode in miBase.DropDownItems)
{
miInnerNode.Text = mainForm.dbCon.getData(miInnerNode.Name.ToString());
}
}
}
// Any other Control i have on the form
else
{
((Control)(curObj)).Text = mainForm.dbCon.getData(((Control)(curObj)).Name.ToString());
}
curObj = mainForm.GetNextControl(((Control)(curObj)), true);
// Are there any more controls under mainObj?
if (null == curObj)
{
endOfElemsUnderPnl = true;
}
} while (!endOfElemsUnderPnl);
}
private void initialIndent(frmMyFileManager parent)
{
if (parent.Language.Equals("Hebrew"))
{
parent.RightToLeft = RightToLeft.Yes;
}
else if (parent.Language.Equals("English"))
{
parent.RightToLeft = RightToLeft.No;
}
else
{
parent.RightToLeft = RightToLeft.No;
}
}
And this is an example of how easy it is for my at runtime:
private void selectLanguageToolStripMenuItem_Click(object sender, EventArgs e)
{
DialogResult res = MessageBox.Show(this, "click yes for english and no for hebrew", "Select language", MessageBoxButtons.YesNoCancel);
if (DialogResult.Yes == res)
{
Language = "English";
}
else if (DialogResult.No == res)
{
Language = "Hebrew";
}
dbCon = new CDBConnector("****\\lang" + Language + ".xml");
view.initialView();
}
Project A contains a WPF Window (Data Entry Form) with a stack panel of commands that launch various reports. The menu is a dynamically built list from a database. What I’m attempting to do is launch the corresponding WPF window based on the CommandText associated with the menu choice. I’d like to create a single function that accepts the name of the WPF Window (CommandText) and launches a new instance of the window by name.
I’ve found examples of how to launch methods on classes, but can’t seem to find a method that works with a window. I know it can be done with a switch and just map all the windows, but there are 60-70 and I was trying to avoid bloat.
I’m failed repeatedly trying to use the TypeOf and Activator.CreateInstance. Suggestions? Is this even possible?
Activator works fine for me. What error do you have? Try if below code will work for you
private void Button_Click(object sender, RoutedEventArgs e)
{
Window wnd = (Window)CreateWindow("WpfApplication1.Window2");
wnd.Show();
}
public object CreateWindow(string fullClassName)
{
Assembly asm = this.GetType().Assembly;
object wnd = asm.CreateInstance(fullClassName);
if (wnd == null)
{
throw new TypeLoadException("Unable to create window: " + fullClassName);
}
return wnd;
}
You might try this:
string windowClass = "CreateWindow.MyWindow";
Type type = Assembly.GetExecutingAssembly().GetType(windowClass);
ObjectHandle handle = Activator.CreateInstance(null, windowClass);
MethodInfo method = type.GetMethod("Show");
method.Invoke(handle.Unwrap(), null);
The code above assumes that your window is called "CreateWindow.MyWindow" (with namespace prefix) and that the type "CreateWindow.MyWindow" is in the currently executing assembly.