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

问题描述 投票:0回答:8

在我的 WPF 表单中,我有一个文本框。
当计时器到时,需要获取文本框的内容。
计时器在与 UI 不同的线程中运行。

这个问题有两个方面:

  • 从 GUI 线程跨线程读取值的最简单、最易读的方法是什么(我找到了几种,对于真正基本的东西来说它们看起来太冗长了)?
  • 我不能以非阻塞的方式读取文本吗?在这种情况下我不关心线程安全。

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

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

不过不介意更简洁。访问文本属性应该是完全基本的。

.net wpf multithreading
8个回答
7
投票

Oisin 是对的,你需要看看Dispatcher。 像这样的东西应该可以工作,并且不会太冗长:

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

5
投票

您可以:

  • 使用
    Dispatcher
    安排消息从后台线程在 UI 线程上执行。
    DispatcherPriority
    Send
    将为您提供最快的响应。
  • 使用
    DispatcherTimer
    在 UI 线程上定期执行消息。
  • 使用
    OneWayToSource
    绑定将
    Text
    属性连接到背景组件上的属性。这样,您不需要做任何工作来获取属性值 - 它已经被提供给您的组件。

3
投票

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

public static string GetTextThreadSafely(this TextBoxBase source)
{
    if (!source.InvokeRequired)
    {
        return source.Text;
    }

    var text = String.Empty;
    source.Invoke((Action)(() => { text = source.GetTextThreadSafely(); }));
    return text;
}

上述方法必须添加到单独的静态类中。


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

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


1
投票

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

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

1
投票

没有“快速破解”可以从与创建 GUI 对象的线程不同的线程读取 GUI 对象的值。 WPF 不会允许您完成这一切。 Windows 窗体偶尔会抱怨,但 WPF 更加严格。

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

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

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


1
投票

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

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

然后在任何需要设置属性或调用方法的地方:

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

这样做的好处在于,您不需要知道它是否在 UI 线程上(并且您可能需要从两个线程上执行此操作)。如果需要,SmartDispatcher 会负责线程切换。

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


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



}

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

© www.soinside.com 2019 - 2024. All rights reserved.