WPFクロススレッド操作GUIコントロール


今日Lua+WPFの実験で質問がありました.
メインスレッドに書いたLUA仮想マシンのdofileですが、LUAスクリプトにブロックを実行させるとUIスレッドもブロックされます.
 
そこで、LUAに対するインタフェースを別のバックグラウンドスレッドに移動し、LUAスクリプトとのインタラクションを担当しました.
コンパイルはすべて正常で、実行時にTHROWが異常で、UI要素を作成しないスレッドでUI要素の属性を変更することはできません.
 
調べてみると、C#はこのメカニズムで、GUIシステムの下部が内部単一スレッドメカニズムであるため、様々なめちゃくちゃなエラーを避けるために、スレッド間GUI要素の操作を直接無効にしています.
 
調べてみたら、Dispatcherの「BeginInvoke」という方法でこの問題を解決しました.
 
その考え方は非同期呼び出しであるが,GUI要素のdispatcherからタイムスライスを分けてスレッドを起動すると推定する.(深く研究していないが、やっとこの問題を解決した)
 
主なコードは以下の通りで、注記された部分はもとは通用しなかった方法です.
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using System.Windows.Threading;

using LuaInterface;

namespace CodersLife
{
    /// <summary>
    ///    
    /// </summary>
    class Super
    {
        /// <summary>
        ///     
        /// </summary>
        /// <param name="address">    </param>
        /// <returns>  </returns>
        static public BitmapSource GetImage(string address)
        {
            return new BitmapImage(new Uri(AppDomain.CurrentDomain.BaseDirectory + string.Format("/Resource/Image/{0}", address), UriKind.Relative));
        } 
    }


    /// <summary>
    /// LuaAPI 
    /// </summary>
    class LuaAPI
    {
        public Lua lua = new Lua();//   lua   
        //public Thread luaThread;
        public Window1 MainWindow { get; set; }

        public void Start()
        {
            try
            {
                //  lua  
                lua.RegisterFunction("BackImage", this, this.GetType().GetMethod("BackImage"));
                lua.RegisterFunction("Music", this, this.GetType().GetMethod("Music"));
                lua.RegisterFunction("Debug", this, this.GetType().GetMethod("Debug"));
                lua.RegisterFunction("BindRolePic", this, this.GetType().GetMethod("BindRolePic"));
                lua.RegisterFunction("Dialog", this, this.GetType().GetMethod("Dialog"));
                //  lua  
                lua.DoFile("Script/Test.lua");

            }
            catch (System.Exception e)
            {
                MessageBox.Show(e.ToString());
            }
        }

        //        
        public void BackImage(string image)
        {
            Brush brushInstance = new ImageBrush { ImageSource = Super.GetImage(image) };
            MainWindow.Carrier.Background = brushInstance;
        }

        //    
        public void Music(string music)
        {
            try
            {
                MainWindow.BackMusic.Stop();
                MainWindow.BackMusic.Source = new Uri(AppDomain.CurrentDomain.BaseDirectory + string.Format("/Resource/Music/{0}", music), UriKind.Relative);
                MainWindow.BackMusic.Play();
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
        }

        private Dictionary<string, BitmapSource> _rolePic = new Dictionary<string, BitmapSource>();

        //      
        public void BindRolePic(string name, string pic)
        {
            _rolePic.Add(name, Super.GetImage(pic));
        }

        //      
        public void Dialog(string name, string dialog)
        {
            BitmapSource Pic = _rolePic[name];
            MainWindow.DialogInfo.Content = dialog;
            MainWindow.RoleName.Content = name;
            Brush brushInstance = new ImageBrush { ImageSource = Pic };
            MainWindow.RoleHeadPic.Background = brushInstance;
        }

        //debug
        public void Debug(string info)
        {
            MessageBox.Show("debug info:" + info);
        }
    }


    /// <summary>
    /// Window1.xaml      
    /// </summary>
    public partial class Window1 : Window
    {
        LuaAPI luaApi = new LuaAPI();

        public delegate void LuaThreadDelegate();

        public Window1()
        {
            InitializeComponent();
            InitLua();
        }

        //   lua
        public void InitLua()
        {
            //luaApi.luaThread = new Thread(new ThreadStart(luaApi.Start));
            luaApi.MainWindow = this;
            //luaApi.luaThread.Start();

            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new LuaThreadDelegate(luaApi.Start));
        }

        //      
        public void ReplayMusic(object sender, EventArgs e)
        {
            BackMusic.Stop();
            BackMusic.Play();
        }

    }
}