2 gangqin6084 gangqin6084 于 2018.01.11 10:05 提问

c#其他类调用主界面的textbox控件

先说明本人水平不高
做一个WPF软件,主界面中有一个TextBox,现在要在一个其他的类中向它中打印。
然后提示,调用线程必须为 STA,因为许多 UI 组件都需要。
现在想知道怎么能让MainWindow向这个类中传递此TextBox。
这个类是定时运行的,非主界面启动的,而且在主界面前加载。

7个回答

hxycsdn9159
hxycsdn9159   2018.01.11 17:25
已采纳

首先不建议楼主这样去进行传递,因为这样做的话会使你定义的类和MainWindow类产生紧耦合,这样的代码是最劣等代码,如果楼主只为了达成功能不在意这些的话方法还是有很多的,比如说你的这个类是class A{ },你在另外一个非UI线程创建了A的一个实例a,然后你想在这个线程内通过a的某个属性或者字段获取MainWindow实例里面一个Textbox输入框的引用,然后你这个类还是先于MainWindow类进行实例化的你可以这样:
首先定义你的类A,有一个公共可访问的属性CurTextBox:
public class A
{
public TextBox CurTextBox{ get; set;}
。。。。。
}
然后这样在你的MainWindow类里面重载一个构造函数,并且为实例a的CurTextBox属性添加对当前Window上TextBox的引用:
public class MainWindow
{
public MainWindow(A a)
{
if(a!== null)
{
a.CurTextBox = this.textbox1;

}
}
}
然后你要刷新这个TextBox的话需要这样:
由于你是跨线程进行UI控件的调用,所以必须使用Dispatcher类的Invoke()或者BeginInvoke()方法:

所以整体代码结构应该大概是这样:
using System.Threading;
namespace Test{
class Program
{
A a = new A();//实例化A
MainWindow win = new MainWindow(a);//实例化MainWindow

    //开启你的刷新线程
    Thread td = new Thread(()=>{
            //定时刷新
            While(true)
            {
                    .......
                    .......
                    Application.Current.Dispatcher.Invoke(new Action(()=>{
                            a.CurTextBox.Text = "abc";//进行刷新        
                    }));
            }
    });
    td.Start();

}
}
这样的话就可以满足你的要求,但是这种方法真的太low,代码紧紧耦合到一起,以后需求一旦变更,你就等着哭吧,例如我把TextBox改成Label,你就需要改MainWindow构造函数的参数类型,还要改各个实例化赋值等,这样会累死你。所以有更好的方法去做这个事情,拿WPF来说,比较好的一个方法就是用资源Resource类,因为资源是全局可以访问的,不需要传递,代码大概是这样的:
你只需要在MainWindow的构造函数内将资源赋值
public class MainWindow
{
public MainWindow()
{
Application.Current.Resource["CurTextBox"] = this.textbox1;//这样就添加了一个全局可以访问的资源
}
}
接下来是调用:

using System.Threading;
namespace Test{
class Program
{
A a = new A();//实例化A(起始此时A根本就没必要了,如果你有其他需要处理的话保存)
MainWindow win = new MainWindow();//实例化MainWindow

    //开启你的刷新线程
    Thread td = new Thread(()=>{
            //定时刷新
            While(true)
            {
                    .......
                    .......
                    Application.Current.Dispatcher.Invoke(new Action(()=>{
                            TextBox tb = Application.Current.Resource["CurTextBox"] as TextBox;
                            tb.Text = "abc";//进行刷新      
                    }));
            }
    });
    td.Start();

}
}
这样不仅在这个线程内可以访问到textbox1,而且在整个Application的任何地方都可以随时访问这个textbox1。

gangqin6084
gangqin6084 谢大神指点,用了第二种方法
4 天之前 回复
caozhy
caozhy   Ds   Rxr 2018.01.11 10:06

不需要传递TextBox,传递一个字符串,用TextBox.Text就可以了。

hxycsdn9159
hxycsdn9159 这位仁兄这样做是不可行的: String s = textbox1.Text; s = "abc"; 难道你认为此时textbox1.Text也变成“abc”了吗?
8 天之前 回复
gangqin6084
gangqin6084 不是很懂,大神能麻烦说的在清楚点吗
8 天之前 回复
caozhy
caozhy   Ds   Rxr 2018.01.11 10:25

新建一个类
public class GlobalData
{
public static string Value;
}
在你界面的构造函数里
textBox1.Text = GlobalData.Value;
在TextBox1的Change事件里
GlobalData.Value = textBox1.Text;

在你其他需要访问的类
GlobalData.Value就是。

qq_41056214
qq_41056214   2018.01.11 13:39

应该使用委托吧 this.invoke,好像是这样

zw198425
zw198425   2018.01.11 14:47

一种是用委托还有一种是传递窗口消息

hxycsdn9159
hxycsdn9159   2018.01.11 16:30

Application.Current.Dispatcher.Invoke(new Action(()=>{ /*TextBox输入框赋值*/ }))

sui0312feng
sui0312feng   2018.01.11 18:26

private void button2_Click(object sender, EventArgs e)
{
Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel2));
thread1.Start("更新Label");
}

    private void UpdateLabel2(object str)
    {
        if (label2.InvokeRequired)
        {
            // 当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它
            Action<string> actionDelegate = (x) => { this.label2.Text = x.ToString(); };
            // 或者
            // Action<string> actionDelegate = delegate(string txt) { this.label2.Text = txt; };
            this.label2.Invoke(actionDelegate, str);
        }
        else
        {
            this.label2.Text = str.ToString();
        }
    }  
Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!