3

我有一个带有数据网格和计时器的表单。我创建了一个资源 CalculationSheet 并在 DUTCH - UK(默认)- DUTCH 中翻译

我用荷兰语启动应用程序。当我选择一条新记录时,会弹出一个消息框。它显示正确的语言,荷兰语。我也设置了计时器。

当计时器过去并再次显示消息框时,资源将以默认语言显示。

这是应用程序的主要入口点:

/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
    System.Threading.Thread.CurrentThread.CurrentUICulture = 
        new System.Globalization.CultureInfo("nl", true);

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

这是回调代码:

void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // shows in UK
    MessageBox.Show(Properties.Resources.CalculationSheet);
}

private void Form1_Load(object sender, EventArgs e)
{
    List<CalculationSheet> calculationSheets = new List<CalculationSheet>();

    calculationSheets.Add(new CalculationSheet("a"));
    calculationSheets.Add(new CalculationSheet("b"));
    calculationSheets.Add(new CalculationSheet("c"));

    this.dataGridView1.DataSource = calculationSheets;

    this.m_Timer = new System.Timers.Timer();
    this.m_Timer.Enabled = false;
    this.m_Timer.Interval = 5000;
    this.m_Timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);

    this.dataGridView1.SelectionChanged += new System.EventHandler(this.dataGridView1_SelectionChanged);
}

private void dataGridView1_SelectionChanged(object sender, EventArgs e)
{
    // shows in DUTCH
    MessageBox.Show(Properties.Resources.CalculationSheet);
    this.m_Timer.Enabled = true;
}
4

1 回答 1

1

System.Timers.Timer的回调在单独的线程上执行回调。

手动设置Application.CurrentCulture属性(或Thread.CurrentUICulture属性)时,它不会流向CultureInfo创建的其他线程(CultureInfo不会ExecutionContext跨线程流动),这就是您看到这个的原因;回调在另一个线程上执行并且CultureInfo未设置。

这个测试用例表明CultureInfo.CurrentCulture没有流向其他线程(因此不在 的回调中Timer):

[TestMethod]
public void TestApplicationCurrentCultureInOtherThreads()
{
    // Create the timer.
    using (var t = new System.Timers.Timer(1000))
    {
        // The task completion source.
        var tcs = new TaskCompletionSource<object>();

        // Captured name.
        CultureInfo capturedCulture = null;

        // Set the current culture.
        // The default for the system needs to be something other
        // than "en-GB", mine is "en-US", which is why this
        // test passes.
        Thread.CurrentThread.CurrentUICulture = new CultureInfo("en-GB");

        // Copy t.
        var tCopy = t;

        // Event handler.
        t.Elapsed += (s, e) => {
            // Stop the timer.
            tCopy.Stop();

            // What's the captured name.
            capturedCulture = CultureInfo.CurrentCulture;

            // Complete the task.
            tcs.SetResult(null);
        };

        // Start.
        t.Start();

        // Wait.
        tcs.Task.Wait();

        // Compare.
        Assert.AreNotEqual(Thread.CurrentThread.CurrentUICulture, 
            capturedCulture);
    }
}

您的回调在显示该方法时没有失败的原因MessageBox.Show是它不需要消息循环(它提供自己的一个)并且可以从任何线程安全地调用。

如果可能,我CultureInfo会将您需要的内容存储在应用程序的一个位置,然后将其传递给其他线程上需要它的任何方法(例如 方法String.Format)。

如果不可能,则必须Application.CurrentCulture在每个需要它的线程上进行设置。但是要小心,如果在计时器回调线程上执行此操作,这些线程来自线程池,因此您永远不会知道线程池线程上的当前文化是什么(因为它没有被重置)。

也就是说,如果你在这些回调中做 UI 工作,那么你真的应该CultureInfo通过调用类上的PostorSend方法将调用编组回 UI 线程(设置的地方) SynchronizationContext(你可以存储来自在 UI 线程上调用的Current属性以从其他线程调用)。

您的Form1_Load方法将存储SynchronizationContext(在创建表单时设置):

private SynchronizationContext _synchronizationContext;

private void Form1_Load(object sender, EventArgs e)
{
    // Capture the context.
    _synchronizationContext = SynchronizationContext.Current;

    // Rest of code.
    ...
}

然后您的timer_Elapsed回调将如下所示:

void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    // Marshal back to the UI thread.
    _synchronizationContext.Send(s => {
        // Will now show in Dutch, as this call is taking place
        // on the UI thread.
        MessageBox.Show(Properties.Resources.CalculationSheet);
    }, null);    
}
于 2012-10-25T12:41:54.483 回答