如何从WPF中的另一个线程读取textbox.Text值?

时间:2009-04-02 14:53:42

标签: .net wpf multithreading

在我的WPF表单中,我有一个文本框 当计时器过去时,需要获取文本框的内容 经过的计时器工作在与UI不同的线程中。

这个问题有点双重:

  • 从GUI线程交叉线程中读取值的最简单,最易读的方法是什么(我发现了几个并且它们看起来太冗长了什么应该是真正基本的东西)?
  • 我不能以非阻塞方式阅读文字吗?在这种情况下我不关心线程安全。

- 编辑 -
我使用了调度程序,但有一个更详细的调用,然后John有:

originalTextBox.Dispatcher.Invoke(
    DispatcherPriority.Normal, 
    (ThreadStart) delegate{text=originalTextBox.Text;}
);

不过不会介意甚至更加疲惫不堪。访问文本属性应该是完全基本的。

8 个答案:

答案 0 :(得分:6)

Oisin是对的,你需要看看Dispatcher。这样的事情应该有效,而且不是太冗长:

System.Windows.Application.Current.Dispatcher.Invoke(
DispatcherPriority.Normal,
(ThreadStart)delegate { text = MyTextBox.Text; });

答案 1 :(得分:5)

你可以:

  • 使用Dispatcher计划在后台线程的UI线程上执行消息。 Send OneWayToSource将为您提供最快的响应。
  • 使用DispatcherPriority定期在UI线程上执行消息。
  • 使用Text绑定将{{1}}属性连接到后台组件上的属性。这样,您就不必进行任何工作来获取属性值 - 它已经提供给您的组件。

答案 2 :(得分:2)

偶然碰到了这里。前段时间我刚刚开始构建一个静态类,我可以将其添加到我的项目中,以便快速访问一些常见的控件属性。随着时间的推移它变得臃肿但是在隐藏大量调度程序代码的同时使事情变得非常简单。原油但有效。可能会给你一些想法。 我基本上可以这样做:

string temp = SafeGuiWpf.GetText(originalTextBox);

以下是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

回想起来,如果我把它们作为类扩展,可能会让它们变得更光滑。

答案 3 :(得分:1)

从创建它的不同线程中读取GUI对象的值没有“快速入侵”。 WPF将不允许您完成所有操作。 Windows Forms会偶尔抱怨,但WPF更严格。

您需要了解Dispatcher。它可能看起来很冗长,但实际上并不难理解。您将委托传递给指向您希望在GUI线程上调用的方法的调度程序,并且它执行此操作。

这是一个很好的简单例子:

http://www.switchonthecode.com/tutorials/working-with-the-wpf-dispatcher

答案 4 :(得分:1)

另一个答案是使用Jeff Wilcox的SmartDispatcher类。

在构造函数或Load事件中的某处执行SmartDispatcher.Initialize()(以设置UI调度程序)

然后您需要设置属性或调用方法:

Action a = delegate { <statements> };
SmartDispatcher.BeginInvoke(a);

这样做的好处是你不需要知道它是否在UI线程上(你可能需要从两者中做到)。如有必要,SmartDispatcher负责线程切换。

以上是异步的,但如果你需要它同步,只需添加另一个方法来调用Invoke而不是BeginInvoke。

答案 5 :(得分:1)

我使用以下扩展方法来解决这个问题:

    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;
        }
    }

当然,这个方法必须添加到一个单独的静态类中。

答案 6 :(得分:0)

我的解决方案...... 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>

和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; });
    }



}

第二个文本框用于线程运行时的类型测试

答案 7 :(得分:0)

由于问题没有指定C#,我想展示一个简单的VB.NET解决方案:

text = originalTextBox.Dispatcher.Invoke(Function() As String
    Return originalTextBox.Text
  End Function)