2009-04-02 7 views
8

In meinem WPF-Formular habe ich eine Textbox.
Wenn ein Timer abgelaufen ist, muss der Inhalt der Textbox abgerufen werden.
Der verstrichene Timer arbeitet in einem anderen Thread als der Benutzeroberfläche.Wie Textbox.Text Wert von einem anderen Thread in WPF gelesen?

Die Frage ist irgendwie zweifach:

  • Was die einfachste und lesbare Weise ist den Wert von einem GUI-Thread Querfaden (fand ich mehr und sie schauen zu ausführlich zu lesen, was etwas sein soll, wirklich einfach)?
  • Kann ich den Text nicht blockfrei lesen? In diesem Fall ist mir die Fadensicherheit egal.

--EDIT--
verwenden ich den Dispatcher, hatte aber einen ausführlicheren Anruf dann, was John hatte: obwohl

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

nicht einmal knapper etwas dagegen hätte. Der Zugriff auf eine Texteigenschaft sollte äußerst einfach sein.

Antwort

1

Es gibt keinen "schnellen Hack" zum Lesen der Werte eines GUI-Objekts von einem anderen Thread als dem, der es erstellt hat. WPF erlaubt dir einfach nicht, alles zu tun. Windows Forms würde sich gelegentlich beschweren, aber WPF ist viel strenger.

Sie müssen etwas über den Dispatcher erfahren. Es mag vielleicht etwas ausschweifend aussehen, aber es ist wirklich nicht so schwer zu verstehen. Sie übergeben einen Delegaten an den Dispatcher, der auf eine Methode verweist, die Sie im GUI-Thread aufrufen möchten, und zwar.

Hier ist ein schönes, einfaches Beispiel:

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

+1

Also keine Möglichkeit, den Wert Thread-unsicher dann aufzurufen? Ich muss es nur bekommen, nicht einstellen. –

+0

Ich suche nicht wirklich nach einem schnellen Hack, ich suche nach etwas Lesbarem. Eine kurze Hand, wenn du willst. –

+0

Entschuldigen Sie Ihre Blase zu platzen, aber mit dem Dispatcher IS Kurzschrift. :) Sie möchten einen eigenen Dispatcher/Marshaller schreiben? – x0n

5

Oisin richtig ist, müssen Sie bei Dispatcher suchen. So etwas sollte funktionieren, und nicht zu ausführlich:

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

Nun, es ist besser, dann, was ich hatte originalTextBox.Dispatcher.Invoke ( DispatcherPriority.Normal, neue Aktion ( Delegat() { text = originalTextBox.Text; })); –

5

Sie können entweder:

  • die Dispatcher Verwenden Sie eine Nachricht planen auf dem UI-Thread von einem Hintergrund-Thread auszuführen. Ein DispatcherPriority von Send wird Ihnen die schnellste Antwort möglich.
  • Verwenden Sie eine DispatcherTimer, um regelmäßig Nachrichten auf dem UI-Thread auszuführen.
  • Verwenden Sie eine OneWayToSource Bindung, um die -Eigenschaft mit einer Eigenschaft auf Ihrer Hintergrundkomponente zu verbinden. Auf diese Weise müssen Sie keine Arbeiten ausführen, um den Eigenschaftswert zu erhalten - er wurde bereits an Ihre Komponente geliefert.
0

Meine Lösung ... Die 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> 

und die Datei 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; }); 
    } 



} 

das zweite Textfeld ist für einen Typ-Test während der Faden

1

runing Eine andere Antwort ist Jeff zu verwenden Wilcox SmartDispatcher-Klasse.

Irgendwo im Konstruktor oder Load-Ereignis tun ein SmartDispatcher.Initialize() (die UI-Dispatcher eingestellt)

Dann überall Sie eine Eigenschaft oder rufen Sie eine Methode festlegen müssen:

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

Die Das Schöne daran ist, dass Sie nicht wissen müssen, ob es auf dem UIhread ist oder nicht (und Sie müssen es möglicherweise von beiden tun). Der SmartDispatcher kümmert sich bei Bedarf um den Thread-Switch.

Das obige ist asynchron, aber wenn Sie es synchron benötigen, fügen Sie einfach eine andere Methode hinzu, um Invoke anstelle von BeginInvoke aufzurufen.

1

Zufälligerweise stolpern Sie hier hinein. Vor einiger Zeit habe ich gerade damit begonnen, eine statische Klasse aufzubauen, die ich meinen Projekten hinzufügen kann, um schnell auf einige gängige Steuerungseigenschaften zugreifen zu können. Es ist im Laufe der Zeit aufgebläht, macht aber die Dinge ziemlich einfach, während es eine Menge Dispatcher-Code versteckt. Roh, aber effektiv. Könnte dir ein paar Ideen geben. Ich kann im Grunde Dinge tun, wie folgt aus:

string temp = SafeGuiWpf.GetText(originalTextBox); 

Hier ist, was die SafeGuiWpf zuletzt aussah, wenn Sie es nützlich finden. (Denken Sie sich in NET arbeiten 3 und höher, aber es ist schon eine Weile)

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 

Im Nachhinein hätte sie noch glatt, wenn ich tat diese als Klassenerweiterungen gemacht.

1

Ich verwende die folgende Erweiterung Methode, dies zu umgehen:

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

Und natürlich diese Methode in einer separaten statischen Klasse hinzugefügt werden muss.

+1

Super! Wirklich tolle Lösung! Ich habe es in meinem WinForm-Projekt angewendet (ich habe es trotzdem generisch gemacht)! Es funktioniert super! –

Verwandte Themen