none
Definición de una Clase y una Clase Collection en Silverlight RRS feed

  • Pregunta

  • Soy nuevo en todo esto y estoy tratando de implementar una clase, y luego una colección de ésta, mi clase es:

    public class ProgramaMTTO : INotifyPropertyChanged
    {
        private int diferencia;
    
        public int Prioridad { get; set; }
        public string Unidad { get; set; }
        public DateTime FechaUltMtto { get; set; }
        public int IDTipoMantenimientoAnt { get; set; }
        public string ClaveMttoAnt { get; set; }
        public int HorometroAnt { get; set; }
        public int Horometro { get; set; }
        public int Diferencia
        {
            get { return diferencia; }
            set
            {
                diferencia = Horometro - HorometroAnt;
            }
        }
        public int IDTipoMantenimiento { get; set; }
        public string ClaveMtto { get; set; }
        public DateTime FechaMtto { get; set; }
        public string HoraMtto { get; set; }
        public float TiempoEstandarHrs { get; set; }
        public int Mecanico { get; set; }
        public int Electrico { get; set; }
        public int Operador { get; set; }
        public int JefeInmediato { get; set; }
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

    Y la colección de mi clase la definí siguiendo un ejemplo de un libro de Rodrigo Díaz Concha:

    public class ProgramaMTTOs : Collection<ProgramaMTTO>, INotifyPropertyChanged
    {
        public ProgramaMTTOs()
        {
            Add(new ProgramaMTTO()
            {
                Prioridad = 1,
                Unidad = "F-03",
                FechaUltMtto = new DateTime(2012, 12, 27),
                IDTipoMantenimientoAnt = 1,
                ClaveMttoAnt = "MP1 (250)",
                HorometroAnt = 6028,
                Horometro = 6294
            });
            Add(new ProgramaMTTO()
            {
                Prioridad = 2,
                Unidad = "F-02",
                FechaUltMtto = new DateTime(2012, 12, 26),
                IDTipoMantenimientoAnt = 2,
                ClaveMttoAnt = "MP2 (500)",
                HorometroAnt = 4122,
                Horometro = 4385
            });
            Add(new ProgramaMTTO()
            {
                Prioridad = 3,
                Unidad = "C-01",
                FechaUltMtto = new DateTime(2012, 12, 15),
                IDTipoMantenimientoAnt = 5,
                ClaveMttoAnt = "MP5 (3000)",
                HorometroAnt = 9521,
                Horometro = 9780
            });
        }
    
        public event NotifyCollectionChangedEventHandler CollectionChanged;
    
        void OnCollectionChanged(NotifyCollectionChangedAction action, int index, ProgramaMTTO item)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, item, index));
            }
        }
    
        void OnCollectionChanged(NotifyCollectionChangedAction action, int index)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, null, index));
            }
        }
    
        void OnCollectionChanged(NotifyCollectionChangedAction action, ProgramaMTTO newitem, ProgramaMTTO olditem, int index)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, newitem, olditem, index));
            }
        }
    
        protected override void InsertItem(int index, ProgramaMTTO item)
        {
            base.InsertItem(index, item);
            OnCollectionChanged(NotifyCollectionChangedAction.Add, index, item);
        }
    
        protected override void RemoveItem(int index)
        {
            base.RemoveItem(index);
    
            OnCollectionChanged(NotifyCollectionChangedAction.Remove, index);
        }
    
        protected override void SetItem(int index, ProgramaMTTO item)
        {
            ProgramaMTTO olditem = this[index];
    
            base.SetItem(index, item);
    
            OnCollectionChanged(NotifyCollectionChangedAction.Replace, item, olditem, index);
        }
    }
    

    Pero cuando compilo me marca: 'MTTO.PruebaDataGrid.ProgramaMTTOs' no implementa el miembro de interfaz 'System.ComponentModel.INotifyPropertyChanged.PropertyChanged'

    No tengo ni idea, pero presiento que es algo relativamente sencillo si alguien me puede ayudar le agradeceré muchísimo. Saludos

    lunes, 26 de enero de 2015 21:30

Respuestas

  • Finalmente luego de revisar sus propuestas y leer el capítulo completo de mi libro, quedó así:

    using System.ComponentModel;
    using System.Collections.ObjectModel;
    
    namespace MTTO
    {
        public class ProgramaMTTO : INotifyPropertyChanged
        {
            private int _prioridad;
            public int Prioridad 
            {
                get
                { return _prioridad; }
                set
                {
                    _prioridad = value;
                    OnPropertyChanged("Prioridad");
                }
            }
            private string _unidad;
            public string Unidad
            {
                get
                { return _unidad; }
                set
                {
                    _unidad = value;
                    OnPropertyChanged("Unidad");
                }
            }
            private DateTime _fechaultmtto;
            public DateTime FechaUltMtto
            {
                get
                { return _fechaultmtto; }
                set
                {
                    _fechaultmtto = value;
                    OnPropertyChanged("FechaUltMtto");
                }
            }
            private int _horometroant;
            public int HorometroAnt
            {
                get
                { return _horometroant; }
                set
                {
                    _horometroant = value;
                    OnPropertyChanged("HorometroAnt");
                    OnPropertyChanged("Diferencia");
                }
            }
            private int _horometro;
            public int Horometro
            {
                get
                { return _horometro; }
                set
                {
                    _horometro = value;
                    OnPropertyChanged("Horometro");
                    OnPropertyChanged("Diferencia");
                }
            }
            public int Diferencia
            {
                get
                { return _horometro - _horometroant; }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            private void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    
        public class ProgramaMttos : ObservableCollection<ProgramaMTTO>
        {
            public ProgramaMttos()
            {
                Add(
                    new ProgramaMTTO()
                    {
                        Prioridad = 1,
                        Unidad = "C-01",
                        FechaUltMtto = new DateTime(2014, 11, 4, 9, 39, 07),
                        HorometroAnt = 5289,
                        Horometro = 6520
                    });
                Add(
                    new ProgramaMTTO()
                    {
                        Prioridad = 2,
                        Unidad = "B-01",
                        FechaUltMtto = new DateTime(2014, 12, 14, 19, 39, 07),
                        HorometroAnt = 3560,
                        Horometro = 4300
                    });
            }
        }
    
        public partial class PruebaDataGrid : Page
        {
    
            public PruebaDataGrid()
            {
                InitializeComponent();
            }
    
            // Se ejecuta cuando el usuario navega a esta página.
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
            }
    
        }
    }

    Y mi xaml de ejemplo:

    <navigation:Page 
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
               xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
               xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
               xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
               xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" 
        xmlns:local="clr-namespace:MTTO" 
        xmlns:Web="clr-namespace:MTTO.Web" 
        xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
        x:Class="MTTO.PruebaDataGrid"
               mc:Ignorable="d"
               d:DesignWidth="640" d:DesignHeight="480"
               Title="PruebaDataGrid Page">
        <navigation:Page.Resources>
            <local:ProgramaMttos x:Key="programamtto" />
        </navigation:Page.Resources>
        <Grid x:Name="LayoutRoot" Background="DarkSeaGreen" DataContext="{Binding Source={StaticResource programamtto}}" >
    
            <sdk:DataGrid x:Name="dgProgramaMTTO" AutoGenerateColumns="True" ItemsSource="{Binding}"
                          Margin="104,127,10,153" RowDetailsVisibilityMode="VisibleWhenSelected">
            </sdk:DataGrid>
            
            <StackPanel Orientation="Vertical" DataContext="{Binding SelectedItem, ElementName=dgProgramaMTTO}" Margin="15,5,50,358">
                <TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="{Binding HorometroAnt, Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>
                <TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="{Binding Horometro, Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>
                <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Diferencia, Mode=TwoWay}" VerticalAlignment="Top"/>
            </StackPanel>
    
        </Grid>
    </navigation:Page>
    

    Así, me muestra tanto en el DataGrid, como en los TextBox los datos de la colección de prueba, y si cambio alguno de los datos en Horometro , HorometroAnt, me refleja en todos lados el cambio de Diferencia

    Gracias a ambos

    • Marcado como respuesta Rick Sanz miércoles, 28 de enero de 2015 16:41
    miércoles, 28 de enero de 2015 16:38

Todas las respuestas

  • Simplemente tienes que quitar el "INotifyPropertyChanged" de la declaración de la colección. No quieres que la colección notifique los cambios de propiedades, sino los cambios de contenido de la colección. En su lugar tendrás que poner "INotifyCollectionChanged", si no me equivoco.
    martes, 27 de enero de 2015 8:49
  • Adicionalmente a lo que le comenta Alberto, veo que su clase ProgramaMTTO no implementa correctamente INotifyPropertyChanged. El código de las propiedades debe hacer el llamado al evento cuando los valores cambian.  De otra forma nunca notificará nada.

    Le dejo una implementación base que puede usar para cualquier clase que necesite implementar INotifyPropertyChanged:

    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    
    namespace <espacio de nombres aquí>
    {
        public class NotifyPropertyChangedBase : INotifyPropertyChanged
        {
            #region INotifyPropertyChanged
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            protected void RaisePropertyChanged(string propertyName)
            {
                PropertyChangedEventHandler ev = PropertyChanged;
                if (ev != null)
                {
                    ev(this, new PropertyChangedEventArgs(propertyName));
                }
            }
    
            protected bool SavePropertyAndNotify<TProp>(ref TProp storage, TProp newValue,
                [CallerMemberName] string propertyName = null)
            {
                if (Object.Equals(storage, newValue)) return false;
                storage = newValue;
                RaisePropertyChanged(propertyName);
                return true;
            }
            #endregion
        }
    }
    

    Esa sería la clase base.  Entonces usted la usaría algo así:

    public class ProgramaMTTO : NotifyPropertyChangedBase
    {
        //Ejemplifico unas pocas propiedades.  Haga usted el resto.
        //Nótese que no puede usarse la nomenclatura corta de propiedades.
        private int m_prioridad;
        public int Prioridad
        {
            get
            {
                return m_prioridad;
            }
            set
            {
                SavePropertyAndNotify(ref m_prioridad, value);
            }
        }
        private int m_horometroAnt;
        public int HorometroAnt
        {
            get
            {
                return m_horometroAnt;
            }
            set
            {
                //Este es un caso particular porque un cambio en esta
                //propiedad también representa un cambio en la propiedad Diferencia.
                bool changed = SavePropertyAndNotify(ref m_horometroAnt, value);
                if (changed) RaisePropertyChanged("Diferencia");
            }
        }
        private int m_horometro;
        public int Horometro
        {
            get
            {
                return m_horometro;
            }
            set
            {
                //Mismo caso que la propiedad anterior.
                bool changed = SavePropertyAndNotify(ref m_horometro, value);
                if (changed) RaisePropertyChanged("Diferencia");
            }
        }
        //Su implementación de esta propiedad era incorrecta.
        public int Diferencia
        {
            get
            {
                return Horometro - HorometroAnt;
            }
        }
        //ETC.  Las demás propiedades de una forma similar.
    }

    La función SavePropertyAndNotify() se encarga de mucho:  Compara el valor actual con el anterior.  Si es igual, el evento PropertyChanged no es invocado; si es distinto entonces almacena el nuevo valor e invoca el evento PropertyChanged.  Además de eso devuelve un valor Booleano que indica si hubo un cambio de valor.  Ese valor de retorno es útil en propiedades cuyo valor afecta el valor de otras propiedades como lo es en el caso de las propiedades HorometroAnt y Horometro, cuyos valores afectan la propiedad Diferencia.  Por lo tanto un cambio en uno de estos valores debe, también, invocar PropertyChanged para la propiedad Diferencia.


    Jose R. MCP
    Code Samples

    martes, 27 de enero de 2015 19:23
  • Muchas gracias a Alberto y José, voy a tomar sus sugerencias y a probarlo, saludos!
    miércoles, 28 de enero de 2015 14:53
  • Finalmente luego de revisar sus propuestas y leer el capítulo completo de mi libro, quedó así:

    using System.ComponentModel;
    using System.Collections.ObjectModel;
    
    namespace MTTO
    {
        public class ProgramaMTTO : INotifyPropertyChanged
        {
            private int _prioridad;
            public int Prioridad 
            {
                get
                { return _prioridad; }
                set
                {
                    _prioridad = value;
                    OnPropertyChanged("Prioridad");
                }
            }
            private string _unidad;
            public string Unidad
            {
                get
                { return _unidad; }
                set
                {
                    _unidad = value;
                    OnPropertyChanged("Unidad");
                }
            }
            private DateTime _fechaultmtto;
            public DateTime FechaUltMtto
            {
                get
                { return _fechaultmtto; }
                set
                {
                    _fechaultmtto = value;
                    OnPropertyChanged("FechaUltMtto");
                }
            }
            private int _horometroant;
            public int HorometroAnt
            {
                get
                { return _horometroant; }
                set
                {
                    _horometroant = value;
                    OnPropertyChanged("HorometroAnt");
                    OnPropertyChanged("Diferencia");
                }
            }
            private int _horometro;
            public int Horometro
            {
                get
                { return _horometro; }
                set
                {
                    _horometro = value;
                    OnPropertyChanged("Horometro");
                    OnPropertyChanged("Diferencia");
                }
            }
            public int Diferencia
            {
                get
                { return _horometro - _horometroant; }
            }
    
            public event PropertyChangedEventHandler PropertyChanged;
    
            private void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
        }
    
        public class ProgramaMttos : ObservableCollection<ProgramaMTTO>
        {
            public ProgramaMttos()
            {
                Add(
                    new ProgramaMTTO()
                    {
                        Prioridad = 1,
                        Unidad = "C-01",
                        FechaUltMtto = new DateTime(2014, 11, 4, 9, 39, 07),
                        HorometroAnt = 5289,
                        Horometro = 6520
                    });
                Add(
                    new ProgramaMTTO()
                    {
                        Prioridad = 2,
                        Unidad = "B-01",
                        FechaUltMtto = new DateTime(2014, 12, 14, 19, 39, 07),
                        HorometroAnt = 3560,
                        Horometro = 4300
                    });
            }
        }
    
        public partial class PruebaDataGrid : Page
        {
    
            public PruebaDataGrid()
            {
                InitializeComponent();
            }
    
            // Se ejecuta cuando el usuario navega a esta página.
            protected override void OnNavigatedTo(NavigationEventArgs e)
            {
            }
    
        }
    }

    Y mi xaml de ejemplo:

    <navigation:Page 
               xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
               xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
               xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
               xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
               xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
               xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices" 
        xmlns:local="clr-namespace:MTTO" 
        xmlns:Web="clr-namespace:MTTO.Web" 
        xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" 
        x:Class="MTTO.PruebaDataGrid"
               mc:Ignorable="d"
               d:DesignWidth="640" d:DesignHeight="480"
               Title="PruebaDataGrid Page">
        <navigation:Page.Resources>
            <local:ProgramaMttos x:Key="programamtto" />
        </navigation:Page.Resources>
        <Grid x:Name="LayoutRoot" Background="DarkSeaGreen" DataContext="{Binding Source={StaticResource programamtto}}" >
    
            <sdk:DataGrid x:Name="dgProgramaMTTO" AutoGenerateColumns="True" ItemsSource="{Binding}"
                          Margin="104,127,10,153" RowDetailsVisibilityMode="VisibleWhenSelected">
            </sdk:DataGrid>
            
            <StackPanel Orientation="Vertical" DataContext="{Binding SelectedItem, ElementName=dgProgramaMTTO}" Margin="15,5,50,358">
                <TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="{Binding HorometroAnt, Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>
                <TextBox HorizontalAlignment="Left" Height="23" TextWrapping="Wrap" Text="{Binding Horometro, Mode=TwoWay}" VerticalAlignment="Top" Width="120"/>
                <TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="{Binding Diferencia, Mode=TwoWay}" VerticalAlignment="Top"/>
            </StackPanel>
    
        </Grid>
    </navigation:Page>
    

    Así, me muestra tanto en el DataGrid, como en los TextBox los datos de la colección de prueba, y si cambio alguno de los datos en Horometro , HorometroAnt, me refleja en todos lados el cambio de Diferencia

    Gracias a ambos

    • Marcado como respuesta Rick Sanz miércoles, 28 de enero de 2015 16:41
    miércoles, 28 de enero de 2015 16:38
  • Su libro parece estar desactualizado. Los eventos deben duplicarse para evitar problemas de concurrencia en ambientes multi hilo. Refiérase a mi implementación, la función RaisePropertyChanged para que vea la forma correcta de invocar eventos.

    Jose R. MCP
    Code Samples

    miércoles, 28 de enero de 2015 17:01
  • José, intenté implementar su sugerencia, pero me marca que no existe:

    protected bool SavePropertyAndNotify<TProp>(ref TProp storage, TProp newValue,
                [CallerMemberName] string propertyName = null)
    investigando encuentro que aplica para .NET Framework 4.5, y como no me lo encuentra, supongo que estoy usando 4.0, traté de cambiar mi proyecto a 4.5 pero no encontré cómo....

    miércoles, 28 de enero de 2015 17:33
  • ¿Qué versión de Visual Studio usa? Si no es 2012 o 2013 no tiene disponible la 4.5. Sin embargo, si es Silverlight eso del todo no funciona. Este blog dice que "declarando" la clase ya funciona.  No sé.  Nunca lo he probado.  Supongo que tal vez sí sirve, pero no sé. 

    En todo caso lo que hace ese atributo es evitarle a uno tener que escribir el nombre de la propiedad. Puede usar mi implementación cambiando la declaración a:

    protected bool SavePropertyAndNotify<TProp>(ref TProp storage, TProp newValue, string propertyName)


    Lo que le obligaría a cambiar las llamadas a algo como:

        private int m_prioridad;
        public int Prioridad
        {
            get
            {
                return m_prioridad;
            }
            set
            {
                SavePropertyAndNotify(ref m_prioridad, value, "Prioridad");
            }
        }

    Lo cual es molesto, pero bueno, si es Silverlight y lo que dice el blog que le mencioné no es cierto, pues no hay de otra:  Es pasar el nombre de la propiedad como string o usar un lambda.

    Pero bueno, lo que le decía antes es que uno hace un duplicado del evento.  Yo me refería al método RaisePropertyChanged, que es muy similar a su OnPropertyChanged, excepto claro por el detallito que quiero hacerle notar:  Yo hago una copia del evento en la variable ev y no uso PropertyChanged directamente.


    Jose R. MCP
    Code Samples

    miércoles, 28 de enero de 2015 18:08
  • Estoy usando .NET 4.0, Silverlight 5, Visual Studio 2012, y el libro que estoy usando de guía es:

    Aplicaciones de negocio con Microsoft Silverlight 5, de Rodrigo Díaz Concha (2013) y en una parte dice:

    "La clase ObservableCollection<T> es una clase genérica, especialmente pensada para colecciones enlazables. ...al usarla, nos ahorraremos todo el código ...escrito para notificar que la colección ha cambiado, ya que esta clase lo hará automáticamente por nosotros, debido a que en ella ya está implementada la interfaz INotifyCollectionChanged (y de hecho, también INotifyPropertyChanged)"

    miércoles, 28 de enero de 2015 20:08
  • Estoy usando .NET 4.0, Silverlight 5, Visual Studio 2012, y el libro que estoy usando de guía es:

    Aplicaciones de negocio con Microsoft Silverlight 5, de Rodrigo Díaz Concha (2013) y en una parte dice:

    "La clase ObservableCollection<T> es una clase genérica, especialmente pensada para colecciones enlazables. ...al usarla, nos ahorraremos todo el código ...escrito para notificar que la colección ha cambiado, ya que esta clase lo hará automáticamente por nosotros, debido a que en ella ya está implementada la interfaz INotifyCollectionChanged (y de hecho, también INotifyPropertyChanged)"

    Ok. Perfecto. ¿Pero por qué menciona esto? No lo entiendo. ¿O será que usted entiende por "todo el código" como todo el código en todas las clases relacionadas a la colección? Porque eso no es cierto. Es "todo el código" que tendría que escribir para crear una colección con notificaciones en cuanto al cambio en el contenido de la colección. La colección no cubre las notificaciones de cambio de propiedades de lo objetos contenidos. Los objetos contenidos deben implementar INotifyPropertyChanged.

    Jose R. MCP
    Code Samples

    viernes, 30 de enero de 2015 1:23
  • Exacto José, así como lo explicas es como lo entendí, y es lo que viene en el texto, solo que abrevié lo que transcribí, saludos y gracias
    viernes, 30 de enero de 2015 14:25