none
DispatcherTimer desde Thread RRS feed

  • Pregunta

  • Hola buenas... Tengo un thread secundario, que se encarga de recibir información de un servidor. Cuando recibo cierta información tengo que mostrarla en la interface de usuario. Eso ya lo tengo resulto con un listview, gracias a los compañeros de aqui, pero en uno los campos del litview tiene que ser un contador, un cronómerto desde 00:00 hasta que el servidor me diga que pare.

    Yo, un cronometro se hacerlo con dispatchertimer, pero cuando trato de lanzarlo desde el tread no me funciona, nunca llega a la propiedad tick.

    Alguna idea de como podría hacerlo?

    Otra cosa, ese cronómetro tengo que ponerlo en cada línea de información a medida que el servidor me vaya mandando datos. Para esto vale con un único disptachertimer o habría que hacerlo de otra forma? Y para pararlos?

    Mil gracias

    miércoles, 16 de mayo de 2012 13:25

Respuestas

  • Hola Hercross.

    Casi lo solucionas tu todo ;).

    Si, para lo que quieres hacer en el punto 1, lo ideal es que uses un TimeSpan.

    Basandome en el ultimo ejemplo que he puesto, seria modificar en el modelo la propiedad que tiene el tiempo y en lugar de un entero establecer un TimeSpan, algo como esto:

        /// <summary>
        /// Modelo con datos de un usuario
        /// </summary>
        public class UserModel : Base
        {
            private int m_id;
            private string m_name;
            private string m_nick;
            private TimeSpan m_time;
    
            /// <summary>Id del usuario</summary>
            public int Id { get { return m_id; } set { m_id = value; this.NotifyPropertyChanged("Id"); } }
            /// <summary>Nombre del usuario</summary>
            public string Name { get { return m_name; } set { m_name = value; this.NotifyPropertyChanged("Name"); } }
            /// <summary>Nick del usuario</summary>
            public string Nick { get { return m_nick; } set { m_nick = value; this.NotifyPropertyChanged("Nick"); } }
            /// <summary>segundos para el proceso</summary>
            public TimeSpan Time { get { return m_time; } set { m_time = value; this.NotifyPropertyChanged("Time"); } }
        }

    para inicializarlo con tiempo 0 se puede hacer algo asi:

                        models.Add(new UserModel() 
                        {
                            Id = models.Count,
                            Name = string.Format("name_{0}", models.Count),
                            Nick = string.Format("nick_{0}", models.Count),
                            Time  = new TimeSpan()
                        });

    y luego en el proceso para incrementarse se puede hacer de la siguiente forma:

                    // actualizar el tiempo de todos los items del listview
                    foreach (var item in items) item.Time += TimeSpan.FromSeconds(1);
    Como el time espan es un objeto que representa un intervalo de tiempo, este ser de formato 00:00:00 por defecto de modo que ya no hay que hacer nada mas, sera representado como quieres, luego desde el objeto se puede obtener cualquier componente de tiempo.


    Saludos
    David González
    MCP, MCTS
    Visita mi Blog en: http://www.dgzornoza.com/

    • Marcado como respuesta Hercross viernes, 8 de junio de 2012 15:44
    martes, 5 de junio de 2012 19:30

Todas las respuestas

  • Hola Hercross.

    ¿donde creas el DispatcherTimer y desde donde lo lanzas, el hilo principal o el hilo secundario creado?.

    ¿como creas el hilo secundario, es un hilo foreground o background?

    En cuanto a lo ultimo, en un principio con un unico timer te vale, no es necesario crear varios, de echo no veo muy elegante crear varios para ello.

    Exactamente los conometros de las lineas ¿se activan todos al tiempo y se van parando conforme se obtienen datos o se van activando uno tras otro segun se vayan obteniendo datos del servidor?.

    En un principio el DispatcherTimer es un timer corriendo en el objeto dispatcher que usa WPF/Silverlight para el hilo de la interface de usuario, no deberia de existir ningun problema, ya que no tienes que insertarlo tu mismo en el dispatcher.


    Saludos
    David González
    MCP, MCTS
    Visita mi Blog en: http://www.dgzornoza.com/

    • Propuesto como respuesta noexisto007 sábado, 26 de mayo de 2012 6:35
    miércoles, 16 de mayo de 2012 18:56
  • Hola David.. Gracias por responder.

    El DispatcherTimer lo lazo desde el hilo pricipal y desde el secundario, he probado desde los 2 y nada, nunca me llega al tick.

    El hilo secundario es un foreground.

    Lo que quiero hacer con los cronometros es que cada vez que recibo info del servidor lo muestro el pantalla, y tengo que mostrar el tiempo que ese proceso lleva activo, para eso el cronometro, y tiene que activarse por cada linea de información que reciba, que puedo recibir ahora una y dentro de unos segundos otra o dos a la vez, es decir, los cronometros tienen que inicializarse con cada linea de entrada. Y tengo que pararlos cuando el servidor me diga que el proceso se acabo, lo tengo que parar para guardar el dato del tiempo que ha tardado.

    Ya te digo que si lo hago en el hilo rpicipal con un boton, por ejmplo, me funciona, lo arranco, lo paro, lo que sea, pero en este caso, no soy capaz de lanzarlo.

    Algún consejo?

    Gracias

    jueves, 17 de mayo de 2012 13:05
  • Buenas... He seguido investigando, ya he conseguido que me vaya al meyodo tick del dispatchertimer, pero no se como añadir el cronometro al listview. He tratado de añadirlo, pero por cada segundo, me añade una fila en el listview, en vez de dejarlo todo en la misma linea y modificar el campo.

    Alguna idea de por donde puedo tirar?

    La cosa es, en el listview con varios campos en sus columnas, en una de ellas tengo que meter el cronometro.

    Gracias

    martes, 22 de mayo de 2012 8:08
  • Hola Hercross.

    He estado un poco liado y no he podido responder antes.

    he realizado un pequeño ejeplillo para ver que problemas podrian surgir, pero la verdad es que funciona como se espera, igual es por la forma en la que lo has implementado, de modo que pongo el ejemplo para que le eches un vistazo y podamos ver cosas a traves de el.

    el codigo xaml:

    	<Window.Resources>
            
    		<local:ViewModel x:Key="ViewModelDataSource" d:IsDataSource="True"/>
        
        </Window.Resources>
        
        <Grid Name="MainGrid" DataContext="{Binding Source={StaticResource ViewModelDataSource}}">
    
            <ListView ItemsSource="{Binding UserModels}">
                <ListView.View>
                    <GridView AllowsColumnReorder="true" ColumnHeaderToolTip="Employee Information">
                        
                        <GridViewColumn DisplayMemberBinding="{Binding Path=Id}" Header="Id" Width="100"/>                    
                        <GridViewColumn DisplayMemberBinding="{Binding Path=Name}" Header="Nombre" Width="100"/>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=Nick}" Header="Nombre" Width="100"/>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=Time}" Header="Nombre" Width="100"/>
                        <GridViewColumn  Header="Nombre" Width="100">
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <Button Tag="{Binding Path=Id}" Content="Comenzar" Click="Button_Click"></Button>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>
                    </GridView>
                </ListView.View>
            </ListView>
        </Grid>

    En el codigo tan solo hay un listview y un recurso para añadir como contexto de datos un viewmodel con el modelo que usare como ejemplo.

    Para hacerlo mas sencillo la columna de tiempo es un int en el cual se mostraran los segundos (por la implementacion puede existir un margen de error de un segundo, pero no queria complicar mas el ejemplo)

    Codigo c#:

     /// <summary>
        /// Lógica de interacción para MainWindow.xaml
        /// </summary>
        public partial class MainWindow : Window
        {
            /// <summary>variable para guardar el DispatcherTimer</summary>
            private System.Windows.Threading.DispatcherTimer m_dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
    
            /// <summary>Variable para guardar un diccionario con las filas que se estan ejecutando (key = id)</summary>
            private Dictionary<int, bool> m_listExecuting;
    
            /// <summary>
            /// Constructor por defecto de la clase
            /// </summary>
            public MainWindow()
            {
                InitializeComponent();
    
                // obtener la lista del recurso de la clase
                System.Collections.ObjectModel.ObservableCollection<UserModel> Testlist = 
                    (this.Resources["ViewModelDataSource"] as ViewModel).UserModels;
                // inicializar una lista por defecto
                Testlist.Add(new UserModel() { Id = 1, Name = "Pepe", Nick = "PepeNick", Time = 0 });
                Testlist.Add(new UserModel() { Id = 2, Name = "Dario", Nick = "DarioNick", Time = 0 });
                Testlist.Add(new UserModel() { Id = 3, Name = "David", Nick = "DavidNick", Time = 0 });
                Testlist.Add(new UserModel() { Id = 4, Name = "Manolo", Nick = "ManoloNick", Time = 0 });
    
                // inicializar lista que mapea las acciones en estado de ejecucion (actualmente ninguna se esta ejecutando)
                m_listExecuting = Testlist.ToDictionary((key) => key.Id, (value) => false);
    
                // inicializar el dispatcher timer
                m_dispatcherTimer.Tick += delegate(object sender, EventArgs e)
                {
                    // obtener todos los items de la lista que estan en ejecucion
                    IEnumerable<int> updateItems = m_listExecuting.Where((item) => item.Value == true).Select((item) => item.Key);
    
                    // actualizar el tiempo de los elementos que estan en ejecucion
                    foreach (var keyItem in updateItems)
                    {
                        (this.Resources["ViewModelDataSource"] as ViewModel).UserModels.First((item) => item.Id == keyItem).Time++;
                    }
    
                    // forzar al ComandManager a lanzar el evento RequerySuggested
                    CommandManager.InvalidateRequerySuggested();
                };
                m_dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
            }
    
            /// <summary>
            /// Evento ocurrido al hacer click en algun boton del listview
            /// </summary>
            /// <param name="sender">objeto remitente</param>
            /// <param name="e">argumentos del evento</param>
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                Button btn = sender as Button;
                // establecer el proceso que se va a ejecutar
                int keyId = (int)btn.Tag;
                m_listExecuting[keyId] = true;
    
                // crear el nuevo hilo donde sera ejecutado algun proceso
                new System.Threading.Thread(delegate(object _keyId)
                    {
                        // si no esta iniciado, se inicia el dispatcher timer
                        if (!m_dispatcherTimer.IsEnabled) m_dispatcherTimer.Start();
    
                        // TODO: aqui va la ejecucion del proceso
                        // como ejemplo el proceso sera una pausa de 10 segundos
                        System.Threading.Thread.Sleep(10 * 1000);
    
                        // establecer el proceso terminado en la lista de procesos
                        m_listExecuting[(int)_keyId] = false;
    
                        // detener el dispatcher si no hay procesos de la lista ejecutandose
                        if (m_listExecuting.All((item) => item.Value == false)) m_dispatcherTimer.Stop();
    
                    }).Start(keyId);
            }
        }
    
        /// <summary>
        /// Clase conteniendo el viewModel de pruebas
        /// </summary>
        public class ViewModel : Base
        {
            /// <summary>
            /// Constructor por defecto de la clase
            /// </summary>
            public ViewModel()
            {
                // crear una lista vacia
                UserModels = new System.Collections.ObjectModel.ObservableCollection<UserModel>();
            }
    
            /// <summary>
            /// Lista con modelos de usuario
            /// </summary>
            public System.Collections.ObjectModel.ObservableCollection<UserModel> UserModels { get; set; }
        }
    
        /// <summary>
        /// Modelo con datos de un usuario
        /// </summary>
        public class UserModel : Base
        {
            private int m_id;
            private string m_name;
            private string m_nick;
            private int m_time;
    
            /// <summary>Id del usuario</summary>
            public int Id { get { return m_id; } set { m_id = value; this.NotifyPropertyChanged("Id"); } }
            /// <summary>Nombre del usuario</summary>
            public string Name { get { return m_name; } set { m_name = value; this.NotifyPropertyChanged("Name"); } }
            /// <summary>Nick del usuario</summary>
            public string Nick { get { return m_nick; } set { m_nick = value; this.NotifyPropertyChanged("Nick"); } }
            /// <summary>segundos para el proceso</summary>
            public int Time { get { return m_time; } set { m_time = value; this.NotifyPropertyChanged("Time"); } }
        }
    
        /// <summary>
        /// Clase para usar como base para implementar notificacion de cambios de propiedades
        /// </summary>
        public class Base : System.ComponentModel.INotifyPropertyChanged
        {
            /// <summary>
            /// Evento para suscribirse al cambio de una propiedad
            /// </summary>
            public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    
            /// <summary>
            /// Funcion  para notificar un cambio de propiedad, de modo que se actualice el binding
            /// </summary>
            /// <param name="propertyName">Nombre de la propiedad a actualizar</param>
            public void NotifyPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
                }
            } 
        }

    El codigo c#, tiene un modelo de ejemplo y un viewmodel a usar para el enlace (ambos heredan de una clase para implementar notificaciones de cambios).

    Lo importante es el constructor, donde se inicializa el evento del dispatcher y un diccionario que he usado para mantener un mapeo de los procesos que se estan ejecutando.

    En el evento click de cada boton es donde se lanza su proceso correspondiente y se inicia/detiene el dispatcher compartido por todos.

    echale un vistazo y comentas si es lo que estas haciendo o donde tienes el problema.


    Saludos
    David González
    MCP, MCTS
    Visita mi Blog en: http://www.dgzornoza.com/

    domingo, 27 de mayo de 2012 16:37
  • Hola David, muchas gracias por responder... He tratado de cargar tu ejemplo pero me salta un error. Me da un error con esta línea

    <local:ViewModel x:Key="ViewModelDataSource" d:IsDataSource="True"/>

    Me dice que local no existe....

    Hay algo mal? o Estoy haciendo algo mal?

    Muchas gracias

    martes, 29 de mayo de 2012 7:04
  • Hola Hercross.

    Si, local es el nombre de espacios que comunmente uso para referenciar al ensamblado de la clase, de modo que tienes que añadir el nombre de espacios en la etiqueta de la ventana:

    xmlns:local="clr-namespace:Tu nombre de espacios"

    En el nombre de espacios debe ir el que pertenece a la clase con el viewmodel.

    por otra parte el nombre de espacios 'd:' lo inserta expression, lo puedes quitar solo es para el diseño visual y se ignora en tiempo de ejecucion.


    Saludos
    David González
    MCP, MCTS
    Visita mi Blog en: http://www.dgzornoza.com/

    martes, 29 de mayo de 2012 11:05
  • Buenas... Creo que me perdí...

    En mi caso, no me hace falta botones ni nada, simplemente es que cuando añada la línea al listview, uno de sus campos tiene que ser un contador, 00:00, y poder parar y guardar el valor del contador, cuando elimine la línea.

    He mirado el ejemplo, pero me pierdo. Algún ejemplo un poco mas sencillo, o alguna referencia a lo que tendría que buscar?

    Muchas gracias

    jueves, 31 de mayo de 2012 12:41
  • Hola Hercross.

    Como no tenia un activador, he puesto los botones en las filas, exactamente no se como tienes el codigo y como enlazas el listview.

    Te he simplificado el codigo lo maximo posible para hacer lo que comentas, he puesto un boton para añadir items al listview y otro para eliminar el ultimo, de modo que cuando se añade uno el contador se va incrementando y cuando se elimina, se muestra un mensaje con el tiempo del elemento eliminado.

    A ver si este te es mas util:

    codigo xaml:

    <Window
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication7"
            xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
            mc:Ignorable="d" 
            x:Class="WpfApplication7.MainWindow"
            Title="MainWindow" Height="350" Width="525">
        
    	<Window.Resources>        
    		<local:ViewModel x:Key="ViewModelDataSource" d:IsDataSource="True"/>    
        </Window.Resources>
        
        <Grid Name="MainGrid" DataContext="{Binding Source={StaticResource ViewModelDataSource}}">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            
            <!-- Lista -->
            <ListView ItemsSource="{Binding UserModels}">
                <ListView.View>
                    <GridView AllowsColumnReorder="true" ColumnHeaderToolTip="Employee Information">                    
                        <GridViewColumn DisplayMemberBinding="{Binding Path=Id}" Header="Id" Width="100"/>                    
                        <GridViewColumn DisplayMemberBinding="{Binding Path=Name}" Header="Nombre" Width="100"/>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=Nick}" Header="Nombre" Width="100"/>
                        <GridViewColumn DisplayMemberBinding="{Binding Path=Time}" Header="Nombre" Width="100"/>
                    </GridView>
                </ListView.View>
            </ListView>
    
            <!-- botones para añadir o eliminar fila -->
            <Button Grid.Row="1" Width="150" HorizontalAlignment="Left" x:Name="btn_add" Content="añadir fila" Click="Button_Click"></Button>
            <Button Grid.Row="1" Width="150" HorizontalAlignment="Right" x:Name="btn_remove" Content="eliminar fila" Click="Button_Click"></Button>
            
        </Grid>
    </Window>
    

    el codigo es casi identico al anterio, pero he eliminado el boton de cada item y en su lugar he puesto 2 botones fuera para añadir y eliminar items respectivamente:

    codigo .cs:

    /// <summary>
    /// Lógica de interacción para MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>variable para guardar el DispatcherTimer</summary>
        private System.Windows.Threading.DispatcherTimer m_dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
    
        /// <summary>
        /// Constructor por defecto de la clase
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
    
            // inicializar el dispatcher timer (donde se incrementara el cronometro de todos los imtems)
            m_dispatcherTimer.Tick += delegate(object sender, EventArgs e)
            {
                // obtener el contexto de datos con los datos del listview
                var items = (this.Resources["ViewModelDataSource"] as ViewModel).UserModels;
    
                // actualizar el tiempo de todos los items del listview
                foreach (var item in items) item.Time++;
    
                // forzar al ComandManager a lanzar el evento RequerySuggested
                CommandManager.InvalidateRequerySuggested();
            };
    
            // establecer el cambio de tiempo cada 1 segundo para el timer
            m_dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
    
            // iniciar el timer (lo ideal es inicializarlo cuando haga falta)
            m_dispatcherTimer.Start();
        }
    
        /// <summary>
        /// Evento ocurrido al hacer click en algun boton para añadir o quitar un elemento en el listview
        /// </summary>
        /// <param name="sender">objeto remitente</param>
        /// <param name="e">argumentos del evento</param>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            // obtener el boton que se ha pulsado
            Button btn = sender as Button;
            // obtener el array de modelos que componen el listview desde el contexto de datos
            var models = (this.Resources["ViewModelDataSource"] as ViewModel).UserModels;
    
            switch (btn.Name)
            {
                // añadir un item al listview
                case "btn_add":
                    // crear el modelo que sera añadido a la lista del contexto de datos y enlazado automaticamente al listview
                    models.Add(new UserModel() 
                    {
                        Id = models.Count,
                        Name = string.Format("name_{0}", models.Count),
                        Nick = string.Format("nick_{0}", models.Count),
                        Time  = 0
                    });
                    break;
    
                // eliminar un item al listview
                case "btn_remove":
                    // obtener el item a eliminar (el ultimo)
                    var item = models.Last();
                    // NOTA: aqui se obtiene el tiempo que ha tardado hasta que se ha eliminado el item
                    // (como ejemplo se muestra un mensaje con el tiempo)
                    MessageBox.Show(string.Format("El tiempo del elemento eliminado es: {0}", item.Time));
                        
                    // eliminar el item
                    models.Remove(item);
    
                    break;
            }
        }
    }
    
    /// <summary>
    /// Clase conteniendo el viewModel de pruebas
    /// </summary>
    public class ViewModel : Base
    {
        /// <summary>
        /// Constructor por defecto de la clase
        /// </summary>
        public ViewModel()
        {
            // crear una lista vacia
            UserModels = new System.Collections.ObjectModel.ObservableCollection<UserModel>();
        }
    
        /// <summary>
        /// Lista con modelos de usuario
        /// </summary>
        public System.Collections.ObjectModel.ObservableCollection<UserModel> UserModels { get; set; }
    }
    
    /// <summary>
    /// Modelo con datos de un usuario
    /// </summary>
    public class UserModel : Base
    {
        private int m_id;
        private string m_name;
        private string m_nick;
        private int m_time;
    
        /// <summary>Id del usuario</summary>
        public int Id { get { return m_id; } set { m_id = value; this.NotifyPropertyChanged("Id"); } }
        /// <summary>Nombre del usuario</summary>
        public string Name { get { return m_name; } set { m_name = value; this.NotifyPropertyChanged("Name"); } }
        /// <summary>Nick del usuario</summary>
        public string Nick { get { return m_nick; } set { m_nick = value; this.NotifyPropertyChanged("Nick"); } }
        /// <summary>segundos para el proceso</summary>
        public int Time { get { return m_time; } set { m_time = value; this.NotifyPropertyChanged("Time"); } }
    }
    
    /// <summary>
    /// Clase para usar como base para implementar notificacion de cambios de propiedades
    /// </summary>
    public class Base : System.ComponentModel.INotifyPropertyChanged
    {
        /// <summary>
        /// Evento para suscribirse al cambio de una propiedad
        /// </summary>
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    
        /// <summary>
        /// Funcion  para notificar un cambio de propiedad, de modo que se actualice el binding
        /// </summary>
        /// <param name="propertyName">Nombre de la propiedad a actualizar</param>
        public void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
            }
        } 
    }

    El codigo c# lo he simplificado lo maximo posible, he mantenido el modelo y viewmodel sin modificarlo ya que nos sirve como ejemplo de contexto de datos,

    Si no entiendes algo comentalo y lo hablamos


    Saludos
    David González
    MCP, MCTS
    Visita mi Blog en: http://www.dgzornoza.com/

    jueves, 31 de mayo de 2012 19:30
  • Hola buenas..

    Llamame inútil, pero no consigo cargar tu ejemplo, me salta un error:

    "la etiqueta "ViewModel" no existe en el espacio de nombres XML 'clr-namespace:WpfApplication1'. Linea 12, posicion 10."

    "No se encontró el tipo 'local:ViewModel'. Compruebe que no falta una referencia a un ensamblado y que se han generado todos los ensamblados a los que se hace referencia."

    Gracias mil.

    viernes, 1 de junio de 2012 17:19
  • Hola Hercross.

    No te preocupes todos hemos tenido problemas de referencias que cuestan encontrar.

    en el xaml existe en la ventana el nombre de espacios que referencia el tag <local:

    xmlns:local="clr-namespace:WpfApplication7"

    En el ejemplo que he puesto yo, el nombre de espacios es WpfApplication7, tu deberas cambiarlo al nombre de espacios en el que resida la clase 'ViewModel', mira el nombre de espacios donde has puesto la clase 'ViewModel' e insertalo en la etiqueta:

    xmlns:local="clr-namespace:TuNombreDeEspacios"

    Con esto deberia de funcionar, si no te funciona, te pongo el ejemplo en el skydrive y le pegas un vistazo.


    Saludos
    David González
    MCP, MCTS
    Visita mi Blog en: http://www.dgzornoza.com/

    viernes, 1 de junio de 2012 20:56
  • Hola David, muchas gracias..

    Ya conseguí que me funcionara tu ejemplo, creo que puede valerme para lo que yo necesito. Voy a ver si puedo adaptarlo para mi aplicación.

    Muchas gracias por todo

    domingo, 3 de junio de 2012 14:57
  • Hola David, muy buenas...

    Estoy tratando de adaptar tu ejemplo para mi código, y me encuentro con un problema.

    Cada vez que intento ejecutarlo, me salta este mensaje:

    El código de usuario no controló NotSupportedException

    Este tipo de CollectionView no admite cambios en el SourceCollection de un subproceso distinto del subproceso Dispatcher.

    La cosa es que yo para lanzarlo, lo llamo así:

    presentación2(1, 2, 3); //1, 2, 3 son los parámetros que le paso.

    El problema, yo creo, es que yo llamo al método presentacion2, desde un thread diferente al principal, no?

    Se podría corregir de alguna forma?

    Mil gracias

    lunes, 4 de junio de 2012 17:37
  • Hola buenas, vale ya está arreglado, era porque no estaba usando delegados, y yo lo lanzaba desde un thread. Eso está, pero ahroa me surgen un par de dudas:

    1.- Para añadir una fila lo hago de la siguiente manera:

    var models = (this.Resources["ViewModelDataSource"] as ViewModel).UserModels;

    listView1.Dispatcher.BeginInvoke(new Action(delegate()
                    {
                        models.Add(new UserModel()
                        {
                            Direccion = p_Direccion,
                            CallerID = p_CallerID,
                            Time = 0,
                            UniqueID = p_UniqueID
                        });
                    }));

    De esta forma voy añadiendo fila cargando la información que me va pasando el servidor.

    El Time, se va incrementando, pero quería hacerlo en formato 00:00, de tal forma que cuando los segundos sean 60 empiece de 0 otra vez, y los minutos crezcan.

    Esto lo suelo hacer de la siguiente manera:

    i += 1;
    int Horas = i / 3600;
    int Minutos = i / 60;
    int Segundos = i % 60;

    tiempo = Horas.ToString("00") + ":" + Minutos.ToString("00") + ":" + Segundos.ToString("00");

    Pero esto no tengo muy claro como adaptarlo para poder meter el 00:00:00 dentro de Time, y que vaya subiendo el tiempo como quiero. Alguna idea de como hacerlo?

    2.- A la hora de eliminar las filas, lo hago de la siguiente forma:

    var models = (this.Resources["ViewModelDataSource"] as ViewModel).UserModels;

                listView1.Dispatcher.BeginInvoke(new Action(delegate()
                    {
                        models.Remove(models.FirstOrDefault((item) => item.UniqueID == p_UniqueID));

                    }));

    Con esto, lo que hago, es eliminar la fila la cual el campo UniqueID sea igual al p_UniqueID, que es el dato que me da mi servidor.

    Hasta quí todo bine, elimino la fila que necesito. Pero necesito coger el tiempo de esa fila, para guardarlo, y es esto lo que tampoco tengo muy claro.

    Como puedo coger el tiempo que tengo a la hora de eliminar la fila?

    Muchas gracias

    martes, 5 de junio de 2012 16:55
  • Vale, el punto numero 2 ya está resuelto. Lo estaba haciendo mal, al final lo he hecho así:

    var models = (this.Resources["ViewModelDataSource"] as ViewModel).UserModels;

    var itemTime2 = models.FirstOrDefault((item) => item.UniqueID == p_UniqueID);

    MessageBox.Show("El tiempo es " + itemTime2.Time);

    listView1.Dispatcher.BeginInvoke(new Action(delegate()
    {
            models.Remove(itemTime2);
    }));

    El punto 1 todavía no he sido capaz de sacarlo, alguna idea?

    Mil gracias

    martes, 5 de junio de 2012 17:20
  • Hola Hercross.

    Casi lo solucionas tu todo ;).

    Si, para lo que quieres hacer en el punto 1, lo ideal es que uses un TimeSpan.

    Basandome en el ultimo ejemplo que he puesto, seria modificar en el modelo la propiedad que tiene el tiempo y en lugar de un entero establecer un TimeSpan, algo como esto:

        /// <summary>
        /// Modelo con datos de un usuario
        /// </summary>
        public class UserModel : Base
        {
            private int m_id;
            private string m_name;
            private string m_nick;
            private TimeSpan m_time;
    
            /// <summary>Id del usuario</summary>
            public int Id { get { return m_id; } set { m_id = value; this.NotifyPropertyChanged("Id"); } }
            /// <summary>Nombre del usuario</summary>
            public string Name { get { return m_name; } set { m_name = value; this.NotifyPropertyChanged("Name"); } }
            /// <summary>Nick del usuario</summary>
            public string Nick { get { return m_nick; } set { m_nick = value; this.NotifyPropertyChanged("Nick"); } }
            /// <summary>segundos para el proceso</summary>
            public TimeSpan Time { get { return m_time; } set { m_time = value; this.NotifyPropertyChanged("Time"); } }
        }

    para inicializarlo con tiempo 0 se puede hacer algo asi:

                        models.Add(new UserModel() 
                        {
                            Id = models.Count,
                            Name = string.Format("name_{0}", models.Count),
                            Nick = string.Format("nick_{0}", models.Count),
                            Time  = new TimeSpan()
                        });

    y luego en el proceso para incrementarse se puede hacer de la siguiente forma:

                    // actualizar el tiempo de todos los items del listview
                    foreach (var item in items) item.Time += TimeSpan.FromSeconds(1);
    Como el time espan es un objeto que representa un intervalo de tiempo, este ser de formato 00:00:00 por defecto de modo que ya no hay que hacer nada mas, sera representado como quieres, luego desde el objeto se puede obtener cualquier componente de tiempo.


    Saludos
    David González
    MCP, MCTS
    Visita mi Blog en: http://www.dgzornoza.com/

    • Marcado como respuesta Hercross viernes, 8 de junio de 2012 15:44
    martes, 5 de junio de 2012 19:30
  • Perfecto, como siempre.

    Muchas gracias David

    viernes, 8 de junio de 2012 15:44
  • Buenas...estoy usandolo, pero tengo un problema, a la hora de añadir o elinimar el ietm no tengo problema, es a la hora de modificar un campo de ese item cundo tengo el problama.

    Al tratar de modificar un campo me salta este error:

    El subproceso q realiza la llamada no puede obtener acceso a este objeto porque e propietario es otro subproceso.

    Intentando depurar algo, veo que itemtime esta vacio....

    Esto lo tengo dentro de un metodo, el cual lo llamo desde un thread, me imagino que ahi esta el problema. Cmo deberia de hacer para poder llamar a ese metodo desde un thread?

    Muchas gracias

    viernes, 25 de enero de 2013 19:08
  • Hola Hercross.

    uff, hace tiempo de este post, ¿pedes poner algo de codigo para ver cual puede ser la causa?

    ¿estas usando el dispatcher para la modificacion tambien ¿no?


    Saludos
    David González
    MCP, MCTS
    Visita mi Blog en: http://www.dgzornoza.com/

    lunes, 28 de enero de 2013 7:43
  • Hola David, muchas gracias por responder....

    Ha sido fallo mio, la excepción la daba porque, en la comparación,  item.Uniqueid estaba vacío.... pero ya esta arreglado.

    Pero ya aprovechando... el TimeSpan hay forma de personalizarlo? Al usarlo me sale 00:00:00 y no me gusta como queda, se podría quitar el 00 de las horas hasta que realmente fuera 01?

    Muchas gracias

    martes, 29 de enero de 2013 10:57
  • Hola Hercross.

    Si claro, puedes formatear la salida como quieras, en concreto nunca he quitado la hora hasta que sea 1, ya que puede dar luegar a confusion entre h:m o m:s si se quita el primer grupo.

    En cualquier caso te dejo la pagina donde te puedes configurar la hora como quieras mediante el formateo de la cadena.

    http://msdn.microsoft.com/es-es/library/8kb3ddd4(v=vs.100).aspx

    En cuanto a la presentacion, no indicas como lo haces, si lo haces mediante xaml, en este post explico algo:

    http://social.msdn.microsoft.com/Forums/es-ES/wpfes/thread/351391a8-a312-4fc0-a71e-290c28054bf3


    Saludos
    David González
    MCP, MCTS
    Visita mi Blog en: http://www.dgzornoza.com/

    martes, 29 de enero de 2013 11:30