Foros del Web » Programación para mayores de 30 ;) » .NET »

[SOLUCIONADO] c# y actualizar formulario

Estas en el tema de c# y actualizar formulario en el foro de .NET en Foros del Web. Buenas tardes, he creado una nueva aplicacion wpf, y he puesto una serie de controles que se tienen que ir actualizando... La aplicacion inicia con ...
  #1 (permalink)  
Antiguo 30/06/2013, 15:17
 
Fecha de Ingreso: octubre-2009
Mensajes: 73
Antigüedad: 14 años, 6 meses
Puntos: 0
c# y actualizar formulario

Buenas tardes,
he creado una nueva aplicacion wpf, y he puesto una serie de controles que se tienen que ir actualizando... La aplicacion inicia con la pulsacion de un boton.

Lo que sucede es que no se como hacer un repaint, para ir viendo la evolucion del bucle y el resultado generado.

Alguna idea?

PD: He leido de refresh, invalidate.. pero no me lo acepta.

Muchas gracias
  #2 (permalink)  
Antiguo 01/07/2013, 11:37
(Desactivado)
 
Fecha de Ingreso: abril-2013
Mensajes: 70
Antigüedad: 11 años
Puntos: 5
Respuesta: c# y actualizar formulario

En WPF se usa databinding, y tus ViewModels o Models tienen que implementar INotifyPropertyChanged.

no existe el concepto de "repaint", y el concepto de Invalidate() se usa para cuestiones de layout y rendering, que no tienen nada que ver con lo que estas tratando de hacer.

Esto es muy diferente a los hacks horribles de code behind que se usan en tecnologias dinsaurio inutiles como winforms. Postea un screenshot de lo que necesitas y te puedo decir la forma correcta de hacerlo en WPF.
  #3 (permalink)  
Antiguo 02/07/2013, 19:44
 
Fecha de Ingreso: octubre-2009
Mensajes: 73
Antigüedad: 14 años, 6 meses
Puntos: 0
Respuesta: c# y actualizar formulario

Vaya no se como adjuntar una imagen. Es un formulario simple, que tiene que ir rellenando un par de TextBox a traves de unas iteraciones de un bucle.

El programa da inicio cuando se pulsa un boton, y cada iteracion modifica el valor (el Caption) de los textbox. El tema como sabes, es que a pesar de la iteracion, el texto no se actualiza.
  #4 (permalink)  
Antiguo 03/07/2013, 11:16
(Desactivado)
 
Fecha de Ingreso: abril-2013
Mensajes: 70
Antigüedad: 11 años
Puntos: 5
Respuesta: c# y actualizar formulario

Cita:
Iniciado por jordixip Ver Mensaje
Vaya no se como adjuntar una imagen. Es un formulario simple, que tiene que ir rellenando un par de TextBox a traves de unas iteraciones de un bucle.

El programa da inicio cuando se pulsa un boton, y cada iteracion modifica el valor (el Caption) de los textbox. El tema como sabes, es que a pesar de la iteracion, el texto no se actualiza.
1 - Si no puedes poner una imagen aqui, lo pones en imgur.com y me pasas un link

2 - no existe tal cosa como un "Caption" en WPF.

3 - seguramente estas metiendo un bucle que se come el UI thread y no le das tiempo al UI Thread a que procese los cambios y actualice la UI adecuadamente.
Como te he dicho, en WPF se usa databinding, y tu bucle tendria que estar en el ViewModel y no en el CodeBehind, de manera asincronica e despachando las notificaciones de cambio (NotifyPropertyChanged()) al Dispatcher de la aplicacion.

Postea tu XAML y tu codigo y te puedo mostrar la forma correcta de hacerlo en WPF.
  #5 (permalink)  
Antiguo 04/07/2013, 00:19
 
Fecha de Ingreso: octubre-2009
Mensajes: 73
Antigüedad: 14 años, 6 meses
Puntos: 0
Respuesta: c# y actualizar formulario

Ahi va:

<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="194" Width="404">
<Grid Height="148" Width="388">
<Button Content="Iniciar" Height="33" HorizontalAlignment="Left" Margin="193,72,0,0" Name="button1" VerticalAlignment="Top" Width="60" Click="button1_Click" />
<Label Content="xxxxx" Height="25" HorizontalAlignment="Left" Margin="13,11,0,0" Name="label1" VerticalAlignment="Top" Width="52" />
<Label Height="30" HorizontalAlignment="Left" Margin="71,8,0,0" Name="label2" VerticalAlignment="Top" Width="108" />
<Label Content="xxxxx" Height="27" HorizontalAlignment="Left" Margin="193,13,0,0" Name="label3" VerticalAlignment="Top" Width="110" />
<Label Height="27" HorizontalAlignment="Left" Margin="288,12,0,0" Name="label4" VerticalAlignment="Top" Width="91" />
<Label Height="24" HorizontalAlignment="Left" Margin="28,72,0,0" Name="label5" VerticalAlignment="Top" Width="73" />
</Grid>
</Window>

Y por otro lado:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Net.NetworkInformation;

namespace WpfApplication3
{
/// <summary>
/// Lógica de interacción para MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private void button1_Click(object sender, RoutedEventArgs e)
{

int numero_escaneos = 20;
int contador_escaneos = 0;



while (contador_escaneos < numero_escaneos)
{

label4.Content = contador_escaneos;


contador_escaneos++;




}



}
}
}

Gracias por tu ayuda.
  #6 (permalink)  
Antiguo 04/07/2013, 09:19
(Desactivado)
 
Fecha de Ingreso: abril-2013
Mensajes: 70
Antigüedad: 11 años
Puntos: 5
Respuesta: c# y actualizar formulario

Ok mira, en primer lugar debes mejorar tu XAML:

Código XML:
Ver original
  1. <Window x:Class="WpfApplication9.MainWindow"
  2.        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.        Title="MainWindow" Height="194" Width="404">
  5.     <Grid>
  6.         <Grid.ColumnDefinitions>
  7.             <ColumnDefinition/>
  8.             <ColumnDefinition/>
  9.         </Grid.ColumnDefinitions>
  10.        
  11.         <Grid.RowDefinitions>
  12.             <RowDefinition Height="Auto"/>
  13.             <RowDefinition Height="Auto"/>
  14.             <RowDefinition Height="Auto"/>
  15.         </Grid.RowDefinitions>
  16.  
  17.         <!-- Numero Escaneos -->
  18.         <Label Content="Numero Escaneos:"/>
  19.         <TextBlock Text="{Binding Numero_Escaneos}" Grid.Column="1"/>
  20.  
  21.         <!-- Contador Escaneos -->
  22.         <Label Content="Contador Escaneos:" Grid.Row="1"/>
  23.         <TextBlock Text="{Binding Contador_Escaneos}" Grid.Column="1" Grid.Row="1"/>
  24.        
  25.         <Button Content="Iniciar" Grid.Row="2" Grid.ColumnSpan="2"
  26.                Click="button1_Click" />
  27.  
  28.     </Grid>
  29. </Window>

El asunto es que seguramente utilizaste el designer de Visual Studio para genera el XAML. Esto no te ayuda, debes aprender a leer y escribir XAML como si fuera castellano. Es escencial para poder usar WPF.

En segundo lugar, debes aprender a utilizar un patrón de diseño que se llama MVVM:
http://en.wikipedia.org/wiki/Model_View_ViewModel

Este tutorial te puede ayudar tambien
http://fernandomachadopiriz.com/2010...on-foundation/

Este patron especifica que la logica de la aplicacion debe estar ESTRICTAMENTE separada de la capa de presentacion (UI), por lo tanto no debes poner la logica en el code behind, si no en un ViewModel Correspondiente:

Código C#:
Ver original
  1. public class MainWindowViewModel:PropertyChangedBase
  2.     {
  3.         public int Numero_Escaneos { get; set; }
  4.  
  5.         private int _contadorEscaneos;
  6.         public int Contador_Escaneos
  7.         {
  8.             get { return _contadorEscaneos; }
  9.             set
  10.             {
  11.                 _contadorEscaneos = value;
  12.                 OnPropertyChanged("Contador_Escaneos");
  13.             }
  14.         }
  15.  
  16.         public void Start()
  17.         {
  18.             Task.Factory.StartNew(() =>
  19.             {
  20.                 while (Contador_Escaneos < Numero_Escaneos)
  21.                 {
  22.                     Contador_Escaneos++;
  23.                     Thread.Sleep(100);
  24.                 }
  25.             });
  26.            
  27.         }
  28.     }

El ViewModel expone simplemente propiedades y metodos que la View (o sea la Window en este caso) ejecuta, y se enlaza a los datos de estas properties.

En WPF, los Bindings (o sea los lugares en XAML donde las properties tienen un "{Binding}" se resuelven contra el objeto designado como DataContext del elemento raíz aplicable.

Por ejemplo en este caso, lo que hacemos es designar a nuestro ViewModel como DataContext de la Window en el Constructor en code behind:

Código C#:
Ver original
  1. public partial class MainWindow : Window
  2.     {
  3.         public MainWindowViewModel ViewModel { get; set; }
  4.  
  5.         public MainWindow()
  6.         {
  7.             InitializeComponent();
  8.  
  9.             DataContext = ViewModel = new MainWindowViewModel();
  10.         }
  11.  
  12.         ...Continua abajo

Esto hace, como te decia que todos los "{Bindings}" se vayan a buscar a la instancia de MainWindowViewModel que acabamos de crear.

Luego, el mismo concepto se aplica al boton:

En lugar de que el Click handler ejecute la logica, la delega al ViewModel y este realiza las tareas de logica que correspondan y actualiza sus propiedades adecuadamente:



Código C#:
Ver original
  1. ... Contiunacion del Code Behind
  2.  
  3.         private void button1_Click(object sender, RoutedEventArgs e)
  4.         {
  5.             ViewModel.Numero_Escaneos = 20;
  6.             ViewModel.Contador_Escaneos = 0;
  7.             ViewModel.Start();
  8.         }

Como ves, el click handler en realidad no hace nada, si no que simplemente ejecuta un metodo en el ViewModel, y el ViewModel es el que realmente tiene la logica de procesamiento.

Ahora bien, otro concepto importante es el tema de que si ejecutas tareas que llevan mucho tiempo en el UI thread, este se cuelga ejecutando tus tareas y no das lugar a que se refresque la pantalla.

Por lo tanto, tu codigo se debe ejecutar en un Thread aparte, y esto lo hace el ViewModel mediante la TPL:
http://msdn.microsoft.com/en-us/library/dd460717.aspx


Código C#:
Ver original
  1. (Codigo del ViewModel que esta mas arriba)
  2.          
  3.             Task.Factory.StartNew(() =>
  4.             {
  5.                 while (Contador_Escaneos < Numero_Escaneos)
  6.                 {
  7.                     Contador_Escaneos++;
  8.                     Thread.Sleep(100);
  9.                 }
  10.             });

Lo que hace esto es ejecutar una nueva tarea (o sea, en un thread separado), que realiza la iteración y actualiza el valor de la propiedad Contador_Escaneos, luego espera 100 milisegundos antes de la siguiente iteracion.

Como esto se realiza dentro del Task, no cuelga el UI Thread, y éste puede seguir respondiendo a peticiones del usuario (UI) o actualizar los datos de UI de acuerdo con los Bindings.

Ahora bien, como te dije el ViewModel para poder notificar sus cambios a la View tiene que implementar la interfaz System.ComponentModel.INotifyPropertyChanged
http://msdn.microsoft.com/en-us/libr...tychanged.aspx

Por lo tanto lo que yo hago es derivar todos mis ViewModels de una clase base que va mas o menos asi:

Código C#:
Ver original
  1. public class PropertyChangedBase:INotifyPropertyChanged
  2.     {
  3.         public event PropertyChangedEventHandler PropertyChanged;
  4.  
  5.         protected virtual void OnPropertyChanged(string propertyName)
  6.         {
  7.             Application.Current.Dispatcher.BeginInvoke((Action) (() =>
  8.             {
  9.                 PropertyChangedEventHandler handler = PropertyChanged;
  10.                 if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
  11.             }));
  12.         }
  13.      }

Si te fijas, lo que hace esto es despachar las notificaciones de cambio al Dispatcher de la aplicacion (o sea, el UI Thread). Esto es debido a que SOLO el UI Thread puede manipular cuestiones de UI, por lo tanto es necesario esto ya que en el momento que se ejecuta el OnPropertyChanged() (en el setter de la propiedad) el Thread actual es el Thread de la Task y no el UI Thread.

Finalmente, si observas te daras cuenta que el ViewModel no "conoce" a la View, es decir no tiene ni necesita ninguna referencia a ningun elemento visual, sino que simplemente expone sus properties y metodos y opera con sus propios datos y logica. Esto es algo muy bueno ya que la separacion de responsabilidades te ayuda a mantener el codigo muchisimo mas reutilizable, limpio, escalable, y feliz.

Por ejemplo, si se te ocurre cambiar totalmente el MainWindow, simplemente lo cambias en XAML y ya, no necesitas tocar ni 1 linea de codigo y tu logica seguira funcionando perfectamente (siempre considerando que la View consuma los datos del ViewModel a traves de Bindings).

Tambien podrias agarrar el codigo del ViewModel exactamente como esta y utilizarlo para otra tecnologia que no sea WPF, y la logica deberia seguir funcionando perfectamente, ya que como te dije no depende de ningun componente visual.

Última edición por HighCore; 04/07/2013 a las 09:37
  #7 (permalink)  
Antiguo 05/07/2013, 16:09
 
Fecha de Ingreso: octubre-2009
Mensajes: 73
Antigüedad: 14 años, 6 meses
Puntos: 0
Respuesta: c# y actualizar formulario

Genial, voy a estudiar todo lo que me has pasado y me has resultado de mucha ayuda.

Etiquetas: aplicacion, formulario
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 06:13.