.net 如何从 WPF 中的另一个线程读取 textbox.Text 值?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/710034/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to read textbox.Text value from another thread in WPF?
提问by Boris Callens
In my WPF form I have a textbox.
When a timer elapses, the content of the textbox needs to be fetched.
The timer elapsed is working in a different thread then the UI.
在我的 WPF 表单中,我有一个文本框。
当计时器结束时,需要获取文本框的内容。
经过的计时器在与 UI 不同的线程中工作。
The question is kinda two-fold:
问题有两点:
- What is the easiest, most readable way to read the value from a GUI thread cross thread (I found several and they look too verbose for what should be something really basic)?
- Can't I read the text in a non-blocking way? I don't care about thread safety in this case.
- 从 GUI 线程跨线程读取值的最简单、最易读的方法是什么(我发现了几个,它们看起来太冗长了,不应该是真正基本的东西)?
- 我不能以非阻塞方式阅读文本吗?在这种情况下,我不关心线程安全。
--EDIT--
I used the Dispatcher, but had a more verbose call then what Johnhad:
--编辑--
我使用了调度程序,但有一个比约翰更冗长的调用:
originalTextBox.Dispatcher.Invoke(
DispatcherPriority.Normal,
(ThreadStart) delegate{text=originalTextBox.Text;}
);
Wouldn't mind even terser though. Accessing a text property should be utterly basic.
不介意更简洁。访问文本属性应该是非常基本的。
采纳答案by Johannes Rabauer
Since the question does not specify C#, i wanted to show a simple solution for VB.NET:
由于问题没有指定 C#,我想展示一个简单的 VB.NET 解决方案:
text = originalTextBox.Dispatcher.Invoke(Function() As String
Return originalTextBox.Text
End Function)
回答by John Myczek
Oisin is right, you need to look at Dispatcher. Something like this should work, and is not too verbose:
Oisin 是对的,你需要看看Dispatcher。像这样的事情应该可以工作,而且不会太冗长:
System.Windows.Application.Current.Dispatcher.Invoke(
DispatcherPriority.Normal,
(ThreadStart)delegate { text = MyTextBox.Text; });
回答by Kent Boogaart
You can either:
您可以:
- Use the
Dispatcherto schedule a message to execute on the UI thread from a background thread. ADispatcherPriorityofSendwill get you the fastest response possible. - Use a
DispatcherTimerto periodically execute messages on the UI thread. - Use a
OneWayToSourcebinding to connect theTextproperty to a property on your background component. That way, you won't have to do any work to get the property value - it will already have been supplied to your component.
- 使用
Dispatcher调度消息以从后台线程在 UI 线程上执行。ADispatcherPriorityofSend将为您提供最快的响应。 - 使用 a
DispatcherTimer在 UI 线程上定期执行消息。 - 使用
OneWayToSource绑定将Text属性连接到后台组件上的属性。这样,您无需执行任何工作即可获取属性值 - 它已经提供给您的组件。
回答by CFD
Just happen to stumble into here. A while back I just started building up a static class that I can add to my projects for fast access to some common control properties. It's bloated over time but makes things pretty easy while hiding away a lot of dispatcher code. Crude but effective. Might give you some ideas. I can basically do things like this:
刚好偶然来到这里。不久前,我刚刚开始构建一个静态类,我可以将它添加到我的项目中,以便快速访问一些常见的控件属性。它随着时间的推移而变得臃肿,但在隐藏大量调度程序代码的同时使事情变得非常容易。粗暴但有效。可能会给你一些想法。我基本上可以做这样的事情:
string temp = SafeGuiWpf.GetText(originalTextBox);
Here is what the SafeGuiWpf last looked like, if you find it helpful. (Think it works in NET 3 and up, but it's been a while)
这是 SafeGuiWpf 最后的样子,如果你觉得它有帮助的话。(认为它适用于 NET 3 及更高版本,但已经有一段时间了)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.ComponentModel;
public class SafeGuiWpf
{
public static object GetTag(Control C)
{
if (C.Dispatcher.CheckAccess()) return C.Tag;
else return C.Dispatcher.Invoke(new Func<Control, object>(GetTag), C);
}
public static string GetText(TextBox TB)
{
if (TB.Dispatcher.CheckAccess()) return TB.Text;
else return (string)TB.Dispatcher.Invoke(new Func<TextBox,string>(GetText), TB);
}
public static string GetText(ComboBox TB)
{
if (TB.Dispatcher.CheckAccess()) return TB.Text;
else return (string)TB.Dispatcher.Invoke(new Func<ComboBox,string>(GetText), TB);
}
public static string GetText(PasswordBox TB)
{
if (TB.Dispatcher.CheckAccess()) return TB.Password;
else return (string)TB.Dispatcher.Invoke(new Func<PasswordBox, string>(GetText), TB);
}
public static void SetText(TextBlock TB, string Str)
{
if (TB.Dispatcher.CheckAccess()) TB.Text = Str;
else TB.Dispatcher.Invoke(new Action<TextBlock,string>(SetText), TB, Str);
}
public static void SetText(TextBox TB, string Str)
{
if (TB.Dispatcher.CheckAccess()) TB.Text = Str;
else TB.Dispatcher.Invoke(new Action<TextBox, string>(SetText), TB, Str);
}
public static void AppendText(TextBox TB, string Str)
{
if (TB.Dispatcher.CheckAccess())
{
TB.AppendText(Str);
TB.ScrollToEnd(); // scroll to end?
}
else TB.Dispatcher.Invoke(new Action<TextBox, string>(AppendText), TB, Str);
}
public static bool? GetChecked(CheckBox Ck)
{
if (Ck.Dispatcher.CheckAccess()) return Ck.IsChecked;
else return (bool?)Ck.Dispatcher.Invoke(new Func<CheckBox,bool?>(GetChecked), Ck);
}
public static void SetChecked(CheckBox Ck, bool? V)
{
if (Ck.Dispatcher.CheckAccess()) Ck.IsChecked = V;
else Ck.Dispatcher.Invoke(new Action<CheckBox, bool?>(SetChecked), Ck, V);
}
public static bool GetChecked(MenuItem Ck)
{
if (Ck.Dispatcher.CheckAccess()) return Ck.IsChecked;
else return (bool)Ck.Dispatcher.Invoke(new Func<MenuItem, bool>(GetChecked), Ck);
}
public static void SetChecked(MenuItem Ck, bool V)
{
if (Ck.Dispatcher.CheckAccess()) Ck.IsChecked = V;
else Ck.Dispatcher.Invoke(new Action<MenuItem, bool>(SetChecked), Ck, V);
}
public static bool? GetChecked(RadioButton Ck)
{
if (Ck.Dispatcher.CheckAccess()) return Ck.IsChecked;
else return (bool?)Ck.Dispatcher.Invoke(new Func<RadioButton, bool?>(GetChecked), Ck);
}
public static void SetChecked(RadioButton Ck, bool? V)
{
if (Ck.Dispatcher.CheckAccess()) Ck.IsChecked = V;
else Ck.Dispatcher.Invoke(new Action<RadioButton, bool?>(SetChecked), Ck, V);
}
public static void SetVisible(UIElement Emt, Visibility V)
{
if (Emt.Dispatcher.CheckAccess()) Emt.Visibility = V;
else Emt.Dispatcher.Invoke(new Action<UIElement, Visibility>(SetVisible), Emt, V);
}
public static Visibility GetVisible(UIElement Emt)
{
if (Emt.Dispatcher.CheckAccess()) return Emt.Visibility;
else return (Visibility)Emt.Dispatcher.Invoke(new Func<UIElement, Visibility>(GetVisible), Emt);
}
public static bool GetEnabled(UIElement Emt)
{
if (Emt.Dispatcher.CheckAccess()) return Emt.IsEnabled;
else return (bool)Emt.Dispatcher.Invoke(new Func<UIElement, bool>(GetEnabled), Emt);
}
public static void SetEnabled(UIElement Emt, bool V)
{
if (Emt.Dispatcher.CheckAccess()) Emt.IsEnabled = V;
else Emt.Dispatcher.Invoke(new Action<UIElement, bool>(SetEnabled), Emt, V);
}
public static void SetSelectedItem(Selector Ic, object Selected)
{
if (Ic.Dispatcher.CheckAccess()) Ic.SelectedItem = Selected;
else Ic.Dispatcher.Invoke(new Action<Selector, object>(SetSelectedItem), Ic, Selected);
}
public static object GetSelectedItem(Selector Ic)
{
if (Ic.Dispatcher.CheckAccess()) return Ic.SelectedItem;
else return Ic.Dispatcher.Invoke(new Func<Selector, object>(GetSelectedItem), Ic);
}
public static int GetSelectedIndex(Selector Ic)
{
if (Ic.Dispatcher.CheckAccess()) return Ic.SelectedIndex;
else return (int)Ic.Dispatcher.Invoke(new Func<Selector, int>(GetSelectedIndex), Ic);
}
delegate MessageBoxResult MsgBoxDelegate(Window owner, string text, string caption, MessageBoxButton button, MessageBoxImage icon);
public static MessageBoxResult MsgBox(Window owner, string text, string caption, MessageBoxButton button, MessageBoxImage icon)
{
if (owner.Dispatcher.CheckAccess()) return MessageBox.Show(owner, text, caption, button, icon);
else return (MessageBoxResult)owner.Dispatcher.Invoke(new MsgBoxDelegate(MsgBox), owner, text, caption, button, icon);
}
public static double GetRangeValue(RangeBase RngBse)
{
if (RngBse.Dispatcher.CheckAccess()) return RngBse.Value;
else return (double)RngBse.Dispatcher.Invoke(new Func<RangeBase, double>(GetRangeValue), RngBse);
}
public static void SetRangeValue(RangeBase RngBse, double V)
{
if (RngBse.Dispatcher.CheckAccess()) RngBse.Value = V;
else RngBse.Dispatcher.Invoke(new Action<RangeBase, double>(SetRangeValue), RngBse, V);
}
public static T CreateWindow<T>(Window Owner) where T : Window, new()
{
if (Owner.Dispatcher.CheckAccess())
{
var Win = new T(); // Window created on GUI thread
Win.Owner = Owner;
return Win;
}
else return (T)Owner.Dispatcher.Invoke(new Func<Window, T>(CreateWindow<T>), Owner);
}
public static bool? ShowDialog(Window Dialog)
{
if (Dialog.Dispatcher.CheckAccess()) return Dialog.ShowDialog();
else return (bool?)Dialog.Dispatcher.Invoke(new Func<Window, bool?>(ShowDialog), Dialog);
}
public static void SetDialogResult(Window Dialog, bool? Result)
{
if (Dialog.Dispatcher.CheckAccess()) Dialog.DialogResult = Result;
else Dialog.Dispatcher.Invoke(new Action<Window, bool?>(SetDialogResult), Dialog, Result);
}
public static Window GetWindowOwner(Window window)
{
if (window.Dispatcher.CheckAccess()) return window.Owner;
else return (Window)window.Dispatcher.Invoke(new Func<Window, Window>(GetWindowOwner), window);
}
} // END CLASS: SafeGuiWpf
In retrospect, might have made them even more slick if I did these as class extensions.
回想起来,如果我将这些作为类扩展来做,可能会使它们更加光滑。
回答by Amit Dash
I use the following extension method to get around this:
我使用以下扩展方法来解决这个问题:
public static string GetTextThreadSafely(this TextBoxBase source)
{
if (source.InvokeRequired)
{
var text = String.Empty;
source.Invoke((Action)(() => { text = source.GetTextThreadSafely(); }));
return text;
}
else
{
return source.Text;
}
}
And of course, this method has to be added in a separate static class.
当然,这个方法必须添加到一个单独的静态类中。
回答by Wray Smallwood
Another answer is to use Jeff Wilcox's SmartDispatcher class.
另一个答案是使用 Jeff Wilcox 的 SmartDispatcher 类。
Somewhere in the constructor or Load event do a SmartDispatcher.Initialize() (to set the UI dispatcher)
在构造函数或 Load 事件中的某处执行 SmartDispatcher.Initialize() (设置 UI 调度程序)
Then anywhere you need to set a property or call a method:
然后在任何需要设置属性或调用方法的地方:
Action a = delegate { <statements> };
SmartDispatcher.BeginInvoke(a);
The beauty of this is that you don't need to know whether it is on the UI thread or not (and you may need to do it from both). The SmartDispatcher takes care of the thread switch if necessary.
这样做的好处是您不需要知道它是否在 UI 线程上(并且您可能需要从两者中进行)。如有必要,SmartDispatcher 负责线程切换。
The above is asynchronous, but if you need it synchronous, just add another method to call Invoke instead of BeginInvoke.
上面是异步的,但是如果你需要同步,只需添加另一个方法来调用Invoke而不是BeginInvoke。
回答by x0n
There is no "quick hack" for reading a GUI object's values from a differing thread than the one that created it. WPF will just not allow you to do it all. Windows Forms would complain occassionally, but WPF is way more strict.
从与创建 GUI 对象的线程不同的线程读取 GUI 对象的值没有“快速技巧”。WPF 只是不允许你做这一切。Windows 窗体偶尔会抱怨,但 WPF 更严格。
You need to learn about the Dispatcher. It might look verbose, but it really isn't that hard to understand. You pass a delegate to the dispatcher that points to a method you want called on the GUI thread, and it does it.
您需要了解 Dispatcher。它可能看起来很冗长,但实际上并不难理解。您将一个委托传递给调度程序,该调度程序指向您要在 GUI 线程上调用的方法,并且它会执行此操作。
Here's a nice simple example:
这是一个很好的简单示例:
http://www.switchonthecode.com/tutorials/working-with-the-wpf-dispatcher
http://www.switchonthecode.com/tutorials/working-with-the-wpf-dispatcher
回答by a52
My solutiosn... The XAML:
我的解决方案... XAML:
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Height="23" Margin="28,27,130,0" Name="textBox1" VerticalAlignment="Top" />
<Button Height="23" HorizontalAlignment="Left" Margin="28,56,0,0" Name="button1" VerticalAlignment="Top" Width="75" Click="button1_Click">Button</Button>
<TextBox Margin="34,85,12,54" Name="textBox2" />
</Grid>
and the cs File:
和 cs 文件:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
new System.Threading.Thread(this.Cuenta).Start();
}
private void Cuenta()
{
for (int i = 0; i < 100000; i++)
this.SetValues(string.Format("Counting... {0} ", i));
}
private void SetValues(string str)
{
System.Windows.Application.Current.Dispatcher.Invoke(
System.Windows.Threading.DispatcherPriority.Normal,
(System.Threading.ThreadStart)delegate { textBox1.Text = str; });
}
}
the second textbox is for a type test while the thread is runing
第二个文本框用于线程运行时的类型测试

