3

データグリッドとタイマーを備えたフォームがあります。リソースのCalculationSheetを作成し、DUTCH - UK (デフォルト) - DUTCHに翻訳しました

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は、それがメッセージ ループを必要とせず (独自の 1 つを提供する)、任意のスレッドから安全に呼び出すことができるためです。

可能であれば、CultureInfo必要なものをアプリケーション内の 1 か所に保存し、それを他のスレッドで必要なメソッド ( などのメソッドString.Format) に渡します。

それが不可能な場合は、それApplication.CurrentCultureを必要とするすべてのスレッドで設定する必要があります。ただし、タイマー コールバック スレッドで実行する場合、これらのスレッドはスレッド プールからのものであるため、スレッド プール スレッドの現在のカルチャが何であるかはわかりません (リセットされないため)。

とは言っても、これらのコールバックで UI 作業を行っている場合は、クラスのまたはメソッドCultureInfoへの呼び出しを通じて、UI スレッド ( が設定されている場所) への呼び出しを実際にマーシャリングする必要があります (他のスレッドから呼び出すために UI スレッドで呼び出されるプロパティ)。PostSendSynchronizationContextCurrent

あなたの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 に答える