2009-04-02 13 views
8

En mi formulario WPF tengo un cuadro de texto.
Cuando transcurre un tiempo, el contenido del cuadro de texto debe buscarse.
El temporizador transcurrido funciona en un subproceso diferente de la interfaz de usuario.Cómo leer el cuadro de texto. ¿Valor del texto de otro hilo en WPF?

La pregunta es un poco doble:

  • ¿Cuál es la manera más fácil, más legible para leer el valor de un hilo transversal hilo GUI (he encontrado varios y se ven demasiado prolijo para lo que debería ser algo realmente básico)?
  • ¿No puedo leer el texto de forma no bloqueante? No me importa la seguridad del hilo en este caso.

--EDIT--
que utiliza Dispatcher, pero tuvo una llamada más detallada a continuación, lo que tenían John:

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

no le importaría incluso más concisa sin embargo. El acceso a una propiedad de texto debe ser completamente básico.

Respuesta

1

No hay ningún "truco rápido" para la lectura de los valores de un objeto de interfaz gráfica de usuario de un hilo diferente que el que lo creó. WPF simplemente no te permitirá hacerlo todo. Windows Forms se quejaba ocasionalmente, pero WPF es mucho más estricto.

Necesita aprender sobre el despachador. Puede parecer detallado, pero realmente no es tan difícil de entender. Pasas un delegado al despachador que apunta a un método que deseas invocar en el hilo de la GUI, y lo hace.

Aquí es un buen ejemplo sencillo:

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

+1

¿Entonces no hay manera de llamar al valor thread-unsalfe? Solo necesito obtenerlo, no configurarlo. –

+0

Realmente no estoy buscando un truco rápido, estoy buscando algo legible. Una mano corta si quieres. –

+0

siento explotar tu burbuja, pero usando el despachador ES abreviatura. :) ¿Quieres escribir tu propio despachador/marshaller? – x0n

5

Oisin es correcto, usted tiene que mirar a Dispatcher. Algo como esto debería funcionar, y no es demasiado detallado:

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

Bueno, es mejor que lo que tenía originalTextBox.Dispatcher.Invoke ( DispatcherPriority.Normal, nueva acción ( delegado() { texto = originalTextBox.Text; })); –

5

Puede:

  • Utilice la Dispatcher para programar un mensaje a ejecutar en el subproceso de interfaz de usuario desde un subproceso en segundo plano. Un DispatcherPriority de Send le proporcionará la respuesta más rápida posible.
  • Utilice DispatcherTimer para ejecutar periódicamente mensajes en el hilo de la interfaz de usuario.
  • Utilice un OneWayToSource de unión para conectar la propiedad Text a una propiedad en su componente de fondo. De esta forma, no tendrá que hacer ningún trabajo para obtener el valor de la propiedad, ya se habrá proporcionado a su componente.
0

Mi solutiosn ... El 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> 

y el Archivo 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; }); 
    } 



} 

el segundo cuadro de texto es para una prueba de tipo mientras que el hilo es runing

1

Otra respuesta es utilizar Jeff Clase SmartDispatcher de Wilcox.

En algún lugar en el evento de carga constructor o hacer una SmartDispatcher.Initialize() (para ajustar el despachador IU)

A continuación, en cualquier lugar que necesita para establecer una propiedad o llamar a un método:

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

El Lo mejor de esto es que no necesita saber si está en el hilo de la interfaz de usuario o no (y es posible que deba hacerlo desde ambos). El SmartDispatcher se encarga del cambio de hilo si es necesario.

Lo anterior es asincrónico, pero si lo necesita sincrónico, simplemente agregue otro método para llamar a Invoke en lugar de BeginInvoke.

1

Acabo de tropezar aquí. Hace un tiempo comencé a construir una clase estática que puedo agregar a mis proyectos para acceder rápidamente a algunas propiedades de control comunes. Está hinchado con el tiempo, pero facilita las cosas a la vez que oculta mucho código de despachador. Crudo pero efectivo. Podría darte algunas ideas. que básicamente puede hacer cosas como esta:

string temp = SafeGuiWpf.GetText(originalTextBox); 

Aquí es lo que el SafeGuiWpf última parecía que, si le resulta útil. (Creo que funciona en la red y hasta 3, pero ha sido un tiempo)

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 

En retrospectiva, podría haber hecho aún más resbaladiza si lo hiciera estos como extensiones de clase.

1

que utilizar el siguiente método de extensión de evitar esto:

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

Y, por supuesto, este método tiene que ser añadido en una clase estática separada.

+1

¡Impresionante! ¡Una solución realmente increíble! Lo he aplicado dentro de mi proyecto WinForm (¡lo hice genérico, aunque)! ¡Funciona genial! –

Cuestiones relacionadas