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