Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to read textbox.Text value from another thread in WPF?

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.

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.

--EDIT--
I used the Dispatcher, but had a more verbose call then what John had:

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

Wouldn't mind even terser though. Accessing a text property should be utterly basic.

like image 800
Boris Callens Avatar asked Apr 02 '09 14:04

Boris Callens


4 Answers

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.

like image 55
Amit Dash Avatar answered Nov 11 '22 16:11

Amit Dash


Oisin is right, you need to look at Dispatcher. Something like this should work, and is not too verbose:

System.Windows.Application.Current.Dispatcher.Invoke(
DispatcherPriority.Normal,
(ThreadStart)delegate { text = MyTextBox.Text; });
like image 6
John Myczek Avatar answered Nov 11 '22 16:11

John Myczek


You can either:

  • Use the Dispatcher to schedule a message to execute on the UI thread from a background thread. A DispatcherPriority of Send will get you the fastest response possible.
  • Use a DispatcherTimer to periodically execute messages on the UI thread.
  • Use a OneWayToSource binding to connect the Text property 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.
like image 5
Kent Boogaart Avatar answered Nov 11 '22 16:11

Kent Boogaart


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)

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.

like image 2
CFD Avatar answered Nov 11 '22 15:11

CFD