Thread.CurrentThread.CurrentUICulture changes .net 4.8 when async is used - wpf

I started to use async operations in my WPF application and realized that Buttons, Labels are on the correct culture (hu-HU), but MessageBox.Show works on the culture of the operating system (en-US). I have resource files for both languages.
App.OnStartup contains
Thread.CurrentThread.CurrentCulture = new CultureInfo("hu-HU");
Thread.CurrentThread.CurrentUICulture = new CultureInfo("hu-HU");
When I press a button in my application and go to the VM I see that Thread.CurrentThread.CurrentUICulture is changed to "en-US" (Thread.CurrentThread.CurrentCulture remained "hu-HU"). I understand this was an issue before .net 4.6 (https://stackoverflow.com/a/30664385/5852947) but it should not be in 4.8. As I understand https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo?view=netcore-3.1#Async states the both culture should be inherited from the originating thread.
I also tried this without success.
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("hu-HU");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("hu-HU");
UPDATE 1
I have a WPF application and I set the culture in OnStartup(). The culture is not hard coded I just wanted to simplify the code.
On the image you can see that the gui is in Hungarian but when I press a button and the corresponding ICommand run in the VM then CurrentUICulture is not correct. In debug mode at the execution of ICommand I also see that CurrentUICulture is not correct. I only set the culture in App.OnStartup. I had no problem until I did not use async. I changed OnStartup() to async because at one point it contains await.
protected async override void OnStartup(StartupEventArgs startupEventArgs)
{
base.OnStartup(startupEventArgs);
var cultureString = ConfigurationManager.AppSettings["Language"];
Thread.CurrentThread.CurrentCulture = new CultureInfo(cultureString);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(cultureString);
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(cultureString);
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(cultureString);
var mainWindowView = new MainWindowView();
// ...
mainWindowView.Show();
ScrollDownInTableTab(mainWindowViewModel);
}

Calling Wait() on a task blocks and potentially also deadlocks. Don't do this. A task represents an asynchronous operation that should be awaited.
As for the issue with culture, you can switch back to the old behaviour by setting the NoAsyncCurrentCulture switch to true in your App.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/>
</startup>
<runtime>
<AppContextSwitchOverrides value="Switch.System.Globalization.NoAsyncCurrentCulture=true"/>
</runtime>
</configuration>

Related

Setting Culture in .net5 WPF application

In a WPF application I try to set the culture in OnStartup.
protected override async void OnStartup(StartupEventArgs startupEventArgs)
{
base.OnStartup(startupEventArgs);
var a = new CultureInfo(ConfigurationManager.AppSettings["Language"]);
Thread.CurrentThread.CurrentCulture = a;
Thread.CurrentThread.CurrentUICulture = a;
CultureInfo.DefaultThreadCurrentCulture = a
CultureInfo.DefaultThreadCurrentUICulture = a;
CultureInfo.CurrentCulture = a;
}
If I start a method from the MainWindow with Click event or with ICommand then in the method the Thread.CurrentThread.CurrentUICulture will be always en-US, which is very strange (can sombody exaplain?). I can set again to the desired Culture but I have to do it in each called method one by one. Is there an alternative?
In .net4.7 there was a workaround but it does not work in .net5.
The reason for this behavior is how async methods are implemented. async methods have their own special execution context. This context has it's own CultureInfo, which is inherited from the non-async context that invokes the async method.
In your case, the async context's culture is inherited from the main thread, before the culture is changed.
What you can do is to implement the already suggested solution using the Dispatcher.InvokeAsync to postpone the CultureInfo configuration. This way the configuration is executed outside the async context:
Dispatcher.InvokeAsync(() => CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-EN"));
Since this can mess up your initialization routine, as the real context would be avaailable after the application is configured and the main Window displayed, you would prefer a different solution.
You can for example used an event based initialization routine, where you run low-level application configuration like culture configuration first and continue with the remaining initialization that involves asynchronous operations in an async context:
App.xaml.cs
// Event may be defined on a different class
private event EventHandler ConfigurationCompleted;
private void OnConfigurationCompleted() => this.ConfigurationCompleted?.Invoke(this, EventArgs.Empty);
protected override void OnStartup(StartupEventArgs startupEventArgs)
{
this.ConfigurationCompleted += ConfigureInAsyncContext;
// Do "low-level" application configuration. Code may be executed in a different class context
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-EN");
...
// Continue in async context
OnConfigurationCompleted();
}
private async void ConfigureInAsyncContext(object sender, EventArgs e)
{
// TODO::Execute async operations
new MainWindow().Show();
}
The key is to separate the non-async configuration from the async initialization.

WebBrowser control from Console app using F#

I have a .netcoreapp3.1 Console App
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<UseWpf>true</UseWpf>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
</Project>
In Program.fs, I am instantiating a WebBrowser control and handling the DocumentCompleted event
let run() =
let uri = "https://www.microsoft.com"
let browser = new WebBrowser()
browser.DocumentCompleted.Add(fun _ -> handlePage browser uris)
browser.Navigate(uri)
[<STAThread>]
[<EntryPoint>]
let main argv =
run()
Console.ReadKey() |> ignore
0
When I run it, the DocumentCompleted event is never fired or handled - the program runs through to the end.
Am I missing?
Thanks in advance.
Am I missing?
The message pump. You need a Dispatcher to run a WPF app.
On another note, you're using the WinForms WebBrowser, that one exposes the DocumentCompleted event. The WPF variant has the LoadCompleted event, ref this post.
In WPF, however, the concept of Loaded is related to the visual tree. As you're not rendering the control, the event will never be raised. If we instead use the Navigated event, we can get there with minimal fuss.
open System
open System.Windows
open System.Windows.Controls
type BrowserApplication() =
inherit Application()
let run() =
let uri = "https://www.microsoft.com"
let browser = new WebBrowser()
browser.Navigated.Add(fun _ -> Console.WriteLine("Done navigating"))
browser.Navigate(uri)
do run();
[<EntryPoint;STAThread>]
let main argv =
BrowserApplication().Run()

How do i call WCF services in Silverlight?

Can anybody tell me why is this not working. I have created a WCF service which returns a list of customers from Northwind database.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.ServiceModel.Activation;
namespace WCFSilverlight.Web
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Customers" in code, svc and config file together.
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class Customers : ICustomers
{
IEnumerable<Customer> ICustomers.GetAllCustomers()
{
NorthwindEntities objNorthwindEntities = new NorthwindEntities();
var query = from cust in objNorthwindEntities.Customers
select cust;
return query.ToList();
}
}
}
And this is my App.xaml.cs code fragment :-
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new MainPage();
CustomersClient objCustomersClient = new CustomersClient();
objCustomersClient.GetAllCustomersCompleted += new EventHandler<GetAllCustomersCompletedEventArgs>(client_GetNameCompleted);
objCustomersClient.GetAllCustomersAsync();
}
void client_GetNameCompleted(object sender, GetAllCustomersCompletedEventArgs e)
{
MessageBox.Show(e.Result.ToString());
}
If I am not wrong the methods in Silverlight are called asynchronously. So I have added a event handler to handle it and then called the method to retrieve customers. But I don't get anything in Messagebox. Further when I try to keep a breakpoint on client_GetNameCompleted, it never executes. But if I keep it in Application_Startup it does execute. What can be the problem?
Also explain me am I doing it correct? I've seen one example where one person directly defines the function using some strange symbols like =>.
EDIT 1:- Kindly also explain me what is e.UserState in e. What does it contain and what can I possibly do with it?
EDIT 2 :- :- I get this error http://img178.imageshack.us/img178/9070/53923202.jpg
The WCF service is working perfectly i have tested the link query. So there is no problem with Sql Server connection or WCF. Something is wrong with my client only.
This is my ServiceReference.ClientConfig :-
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_ICustomers" maxBufferSize="2147483647"
maxReceivedMessageSize="2147483647">
<security mode="None" />
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:50622/Customers.svc" binding="basicHttpBinding"
bindingConfiguration="BasicHttpBinding_ICustomers" contract="CustomerServ.ICustomers"
name="BasicHttpBinding_ICustomers" />
</client>
</system.serviceModel>
</configuration>
Can you now tell me what is wrong?
Thanks in advance :)
Update :- I read in google you need to set serialization mode to unidirectional. But where do i set this? What do i write where?
You are correct, All network calls in Silverlight are done asynchronously.
The => syntax you mention is shorthand for defining a delegate method, its called a lambda. (see below)
You should be able to set a break-point in the Completed event handler, if not try restarting Visual Studio (I've seen it act strangly before).
e.UserState will have a reference to whatever object you put in the UserState variable for the async call (note the extra overload).
Code:
objCustomersClient.GetAllCustomersCompleted = delegate(object Sender, GetAllCustomersCompletedEventArgs e)
{
MessageBox.Show(e.Result.ToString());
};
// is the same as
objCustomersClient.GetAllCustomersCompleted += new EventHandler<GetAllCustomersCompletedEventArgs>(client_GetNameCompleted);
void client_GetNameCompleted(object sender, GetAllCustomersCompletedEventArgs e)
{
MessageBox.Show(e.Result.ToString());
}
// which is same as
objCustomersClient.GetAllCustomersCompleted += (sender, e) => { MessageBox.Show(e.Result.ToString()); };

How to edit an external web.config file?

I am trying to write a winform application that would be able to edit the web.config file of an installed web application.
I have read through the ConfigurationManager and WebConfigurationManager class methods but I am unsure as to how I can open the configuration file of a web app and edit it.
I am looking for a method that does not require me to load the config file as a regular XmlDocument, although I am willing to do that if that is the only option available.
Any advice would be appreciated.
Ok so here is the answer, I have the EXACT same scenario. I wanted to write a winforms app to allow normal users to update the web.config. You have to go about getting the config a goofy way...
// the key of the setting
string key = "MyKey";
// the new value you want to change the setting to
string value = "This is my New Value!";
// the path to the web.config
string path = #"C:\web.config";
// open your web.config, so far this is the ONLY way i've found to do this without it wanting a virtual directory or some nonsense
// even "OpenExeConfiguration" will not work
var config = ConfigurationManager.OpenMappedExeConfiguration(new ExeConfigurationFileMap() { ExeConfigFilename = path }, ConfigurationUserLevel.None);
// now that we have our config, grab the element out of the settings
var element = config.AppSettings.Settings[key];
// it may be null if its not there already
if (element == null)
{
// we'll handle it not being there by adding it with the new value
config.AppSettings.Settings.Add(key, value);
}
else
{
// note: if you wanted to you could inspect the current value via element.Value
// in this case, its already present, just update the value
element.Value = value;
}
// save the config, minimal is key here if you dont want huge web.config bloat
config.Save(ConfigurationSaveMode.Minimal, true);
Here is an example of what it does
Before:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="MyKey" value="OldValue" />
</appSettings>
<connectionStrings>
<add name="myConnString" connectionString="blah blah blah" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
After:
<?xml version="1.0"?>
<configuration>
<appSettings>
<add key="MyKey" value="This is my New Value!" />
</appSettings>
<connectionStrings>
<add name="myConnString" connectionString="blah blah blah" providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<trust level="Full" />
<webControls clientScriptsLocation="/aspnet_client/{0}/{1}/" />
</system.web>
</configuration>
Just be careful, if you give it an invalid path, it will just create a config file at that path / filename. Basically, do a File.Exists check on it first
BTW, while you're at it, you could write a class that represents your settings in your web.config. Once you do this, write your getters/setters to read/write the settings in the web.config. Once THIS is done, you can add this class as a datasource and drag the databound controls onto your winform. This will give you a completely databound winform web.config editor that you can easily hammer out in a matter of minutes. I've got an example at work that I'll post tomorrow.
Fully featured winforms solution
So this is a relatively simple solution to writing a Gui to edit a web.config, some may say its overly complicated when notepad will do just fine but it works for me and my audience.
It basically works as described above, I wrote a class that had the configuration points that I wanted as properties. The ctor opens the file from a path and the getters / setters pull the data out of the returned configuration object, finally it has a save method that writes it out. With this class, I'm able to add the class as a datasource and drag / drop bound controls onto winforms. From there all you have to do is to wire up a button that calls the save method on your class.
Configuration class
using System.Configuration;
// This is a representation of our web.config, we can change the properties and call save to save them
public class WebConfigSettings
{
// This holds our configuration element so we dont have to reopen the file constantly
private Configuration config;
// given a path to a web.config, this ctor will init the class and open the config file so it can map the getters / setters to the values in the config
public WebConfigSettings(string path)
{
// open the config via a method that we wrote, since we'll be opening it in more than 1 location
this.config = this.OpenConfig(path);
}
// Read/Write property that maps to a web.config setting
public string MySetting
{
get { return this.Get("MySetting"); }
set { this.Set("MySetting", value); }
}
// Read/Write property that maps to a web.config setting
public string MySetting2
{
get { return this.Get("MySetting2"); }
set { this.Set("MySetting2", value); }
}
// helper method to get the value of a given key
private string Get(string key)
{
var element = config.AppSettings.Settings[key];
// it may be null if its not there already
if (element == null)
{
// we'll handle it not being there by adding it with the new value
config.AppSettings.Settings.Add(key, "");
// pull the element again so we can set it below
element = config.AppSettings.Settings[key];
}
return element.Value;
}
// helper method to set the value of a given key
private void Set(string key, string value)
{
// now that we have our config, grab the element out of the settings
var element = this.config.AppSettings.Settings[key];
// it may be null if its not there already
if (element == null)
{
// we'll handle it not being there by adding it with the new value
config.AppSettings.Settings.Add(key, value);
}
else
{
// in this case, its already present, just update the value
element.Value = value;
}
}
// Writes all the values to the config file
public void Save()
{
// save the config, minimal is key here if you dont want huge web.config bloat
this.config.Save(ConfigurationSaveMode.Minimal, true);
}
public void SaveAs(string newPath)
{
this.config.SaveAs(path, ConfigurationSaveMode.Minimal, true);
// due to some weird .net issue, you have to null the config out after you SaveAs it because next time you try to save, it will error
this.config = null;
this.config = this.OpenConfig(newPath);
}
// where the magic happens, we'll open the config here
protected Configuration OpenConfig(string path)
{
return ConfigurationManager.OpenMappedExeConfiguration(
new ExeConfigurationFileMap() { ExeConfigFilename = path },
ConfigurationUserLevel.None);
}
}
Build and then from there you can just goto your winform designer, goto Data > Show Data Sources (Shift+Alt+D). Right click > Add New Data Source and add it as an object as shown
Data Source Configuration Wizard 1 of 2 http://img109.imageshack.us/img109/8268/98868932.png
Data Source Configuration Wizard 2 of 2 http://img714.imageshack.us/img714/7287/91962513.png
Drag it (WebConfigSettings, the topmost) onto the winform. In my case, I will remove the navigator as that is for a List and I just have one.
Freshly added databound controls http://img96.imageshack.us/img96/8268/29648681.png
You should have something like webConfigSettingsBindingSource at the bottom of the designer (shown in the next pic). Goto the code view and change the ctor to this
public Form1()
{
InitializeComponent();
// wire up the actual source of data
this.webConfigSettingsBindingSource.DataSource = new WebConfigSettings(#"c:\web.config");
}
Add a save button to your winform
Save button added http://img402.imageshack.us/img402/8634/73975062.png
Add the following event handler
private void saveButton_Click(object sender, EventArgs e)
{
// get our WebConfigSettings object out of the datasource to do some save'n
var settings = (WebConfigSettings)this.webConfigSettingsBindingSource.DataSource;
// call save, this will write the changes to the file via the ConfigurationManager
settings.Save();
}
There, now you have a nice simple databound web.config editor. To add / remove fields, you just modify your WebConfigSettings class, refresh the datasource in the Data Sources window (after a build), and then drag n drop the new fields onto the UI.
You'll still have to wire up some code that specifies a web.config to open, for this example I just hard coded the path.
The cool thing here is all the value that a GUI adds. You can easily add directory or filebrowser dialogs, you can have connection string testers etc. All are very easy to add and very powerful to the end user.
I highly recommend you to use XElement along with LINQ enabled (LINQ to XML).
For instance you want to change the connectionString. This type of code is good enough
var connString = from c in webConfigXElement.appSettings.connectionString
where c.name == "myConnection"
select c;
and now you have full control over the <connectionString /> element, and do whatever you want to do with it.
I am referring you to MSDN for learning and also a Kick Start for instant working.
Hope this helps you to have full control over your .xml without pain.
Here you go, I wrote a toy app (VB.NET Windows client) that edits XML files using a Tree / Grid for navigating and editing.
You might get some ideas from it. The VS project file for it is here or just an MSI to install it is here, if you want to try it out on your web.config.
It loads the file into a DataSet (DataSet.ReadXML()) which parses it into DataTables, then displays and allows editing of the contents in a standard DataGrid. Then it will save the edited content back to the XML file (DataSet.WriteXML()).
All app.config and web.config are just XML files. You can open and edit them using XMLDocument, XMLWriter, etc.

WPF - How do i insert my proxy credentials in WebBrowser Control

I'm using a webbrowser control in WPF to show a virtual earth map, but because I'm developing behind a proxy of my company, every time I try to see the map, I have to insert my credentials.
I would like to insert them automatically in the c# code, how can I achieve that?
I already try in the navigating event of the web browser using:
void webBrowser_Navigating(object sender, NavigatingCancelEventArgs e)
{
//NetworkCredential credential = new NetworkCredential("u007239", "****", "****");
//e.WebRequest.Proxy.Credentials = credential;
}
But this doesn't work because I'm getting that e.webrequest is a null object.
all the help is welcome.
Can you not just put the following in your app.config?
<configuration>
<system.net>
<defaultProxy useDefaultCredentials="true" enabled="true"></defaultProxy>
</system.net>
</configuration>

Resources