none
CheckBox dentro de un Datagrid de WPF usando MVVM RRS feed

  • Pregunta

  • Tengo un problema con un Datagrid de WPF, quiero que una de las columnas del Datagrid sea un CheckBox ligado a una propiedad tipo bool llamada seleccionada de mi modelo, y que el usuario pueda seleccionar o des-seleccionar desde allí, y luego ver los cambios que haya hecho, pero no lo hace, explico lo que hice:

    Tengo una clase donde defino todas las propiedades que serán columnas de mi Datagrid, solo muestro el detalle de seleccionada:

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    
    using System.Linq;
    using System.Text;
    using System.ComponentModel;
    
    namespace Modulo_Pagos
    {
        public class CuentasxCobrarCL : INotifyPropertyChanged
        {
            private bool _seleccionada;
            public bool seleccionada
            {
                get
                { return _seleccionada; }
                set
                {
                    _seleccionada = value;
                    OnPropertyChanged("seleccionada");
                }
            }
    
            #region PropertyChanged
            public event PropertyChangedEventHandler PropertyChanged;
    
            private void OnPropertyChanged(string propertyName)
            {
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                }
            }
            #endregion
        }
    }
    
    
    


    Luego en el código de mi .xaml.cs :

    public ObservableCollection<CuentasxCobrarCL> lsCuentasxCobrar;
    
    // creo una instancia de la collection
    lsCuentasxCobrar = new ObservableCollection<CuentasxCobrarCL>();
    // limpio el ItemsSource de mi Datagrid
    grdCuentasxCobrar.ItemsSource = null;
    
    
    // en un ciclo voy llenando mi collection, con selecionada = false
    lsCuentasxCobrar.Add(new CuentasxCobrarCL{...});
    
    // al terminar asigno mi collection al ItemsSource de mi Datagrid
    grdCuentasxCobrar.ItemsSource = lsCuentasxCobrar;


    en el .xaml defino mi Datagrid como sigue:

    <DataGrid x:Name="grdCuentasxCobrar" AutoGenerateColumns="False" VerticalAlignment="Top" Margin="5,5,5,5" Width="870" Height="250"
    ItemsSource="{Binding}" CanUserResizeColumns="True" CanUserReorderColumns="True" HorizontalAlignment="Left" 
    IsReadOnly="True" CanUserSortColumns="True" ScrollViewer.HorizontalScrollBarVisibility="Auto" ScrollViewer.VerticalScrollBarVisibility="Auto" >
        <DataGrid.Columns>
    
    
    <!--COLUMNA SELECCIONAR-->
    <sdk:DataGridTemplateColumn Header="Seleccionar" Width="95" CanUserSort="False"  >
        <sdk:DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <CheckBox Name="chkseleccionada" 
                            IsChecked="{Binding seleccionada, Mode=TwoWay,
                    NotifyOnValidationError=True,
                    ValidatesOnExceptions=True, 
                    ValidatesOnNotifyDataErrors=True, 
                    ValidatesOnDataErrors=True                                                
                    }" 
                            HorizontalAlignment="Center" />
            </DataTemplate>
        </sdk:DataGridTemplateColumn.CellTemplate>
        <sdk:DataGridTemplateColumn.CellEditingTemplate>
            <DataTemplate>
                <CheckBox IsChecked="{Binding seleccionada, Mode=TwoWay,
                    NotifyOnValidationError=True,
                    ValidatesOnExceptions=True, 
                    ValidatesOnNotifyDataErrors=True, 
                    ValidatesOnDataErrors=True                                                
                    }"  />
            </DataTemplate>
        </sdk:DataGridTemplateColumn.CellEditingTemplate>
    </sdk:DataGridTemplateColumn>

    y puedo interactuar correctamente con mi checkbox pero al final, tengo un botón que solo uso para inspeccionar los resultados tanto en la collection como en los items del Datagrid, y en ambos todos los registros tienen false en seleccionada

    Alguna idea?

    Muchas gracias


    cyndyrdz

    jueves, 5 de julio de 2018 18:57

Respuestas

  • no lo puedo creer!

    está bien "obvio" MI ERROR....

    en la declaración de mi Datagrid le puse IsReadOnly=true!!!

    tan obvio que me tardé horas en encontrarlo... el resto de la implementación estaba bien...

    (Partner) muchas gracias por tu apoyo, y voy a revisar despacio tus sugerencias

    Saludos



    cyndyrdz

    viernes, 6 de julio de 2018 16:36

Todas las respuestas

  • Categóricamente hablando, no logro ver el error que produce su problema, pero sí veo unos cuantos problemas.

    1. Si va a asignar ItemsSource directamente en código C#, no lo asigne en XAML.  Veo que tiene en XAML un binding al data context del padre, pero imagino que dicho data context no tiene nada que ver con la colección que intenta utilizar.
    2. Si es solamente un checkbox lo que necesita, ¿por qué utilizar una columna personalizada?  El DataGrid provee el tipo de columna de checkbox ya lista para usarse.
    3. Su OnPropertyChanged no está implementado correctamente según las recomendaciones de mejores prácticas.  Si usa C# 6 (creo) o superior, puede escribirlo en una línea:  PropertyChanged?.Invoke(new PropertyChangedEventArgs(propertyName));.

    Ahora bien, le cuento que yo hice y publiqué una biblioteca de clases que ayuda mucho a hacer esto que usted está haciendo.  Se llama wj.DataBinding y puede leer cómo usarla para su caso leyendo en el wiki la documentación de la clase Container.  Además, tengo este otro proyecto que tiene ejemplos en Windows Forms y WPF sobre cómo hacer lo que usted está haciendo.


    Jose R. MCP
    My GIT Repositories | Mis Repositorios GIT

    jueves, 5 de julio de 2018 19:18
  • Ah, y la biblioteca wj.DataBinding también tiene la clase ObservableCollectionEx que mejora ObservableCollection en el caso de colecciones grandes.  El proyecto de ejemplo también demuestra esta función.

    Jose R. MCP
    My GIT Repositories | Mis Repositorios GIT

    jueves, 5 de julio de 2018 19:20
  • Muchas gracias José, considero todas tus observaciones y sugerencias, y luego comento cómo me fue. Saludos

    cyndyrdz

    jueves, 5 de julio de 2018 20:28
  • Perfecto.  Buena suerte.  También, si no le resulta muy complicado, note cómo en el ejemplo de WPF no hay código en el code-behind de la ventana WPF.  Eso es ideal cuando uno dice trabajar bajo el modelo MVVM.  Todo el asunto está en el XAML (primordialmente los bindings) y el ViewModel.  Trate de hacerlo así y verá que es mejor.  Por ejemplo, su colección debería ser una propiedad del ViewModel y no declararla en el window.xaml.cs.  Todo dentro del ViewModel.

    Jose R. MCP
    My GIT Repositories | Mis Repositorios GIT

    jueves, 5 de julio de 2018 20:41
  • Ya comencé a revisar tus sugerencias:

    1. Hice el cambio, básicamente quité la asignación del ItemsSource del Codigo en C# y en el XAML en la definición del Datagrid puse:

    ItemsSource="{Binding lsCuentasxCobrar}"

    pero si lo hago así, no me muestra nada en el Datagrid cuando termina de procesar los datos de la collection

    2. No es solo un checkbox para "mostrar" en el Datagrid, debe tener la funcionalidad de que el usuario pueda sobre éste marcar o desmarcar el checkbox, y que se refleje el dato en la colección, si pongo en el .xaml:

    <DataGridCheckBoxColumn Binding="{Binding seleccionada, Mode=TwoWay}" CanUserSort="False" ClipboardContentBinding="{x:Null}"/>

    en esa columna no me deja editar la selección, solo muestra el valor de seleccionada en un checkbox deshabilitado

    3. No sé que me falta, o si alguna versión que estoy usando no es la adecuada, pero si pongo:

    PropertyChanged?.Invoke(new PropertyChangedEventArgs(propertyName));
    en la implentación del Notify, me marca error en la parte de PropertyChanged?.Invoke  me dice que solo se puede usar assignment,call, etc.


    cyndyrdz

    jueves, 5 de julio de 2018 21:18
  • El #1 luce bien.  ¿Dónde asignó el ViewModel?  ¿En el Window.DataContext como el ejemplo que tengo yo en GitHub?

    Para #2, el binding luce bien.  El Mode debería ser opcional puesto que la propiedad seleccionada tiene tanto un get como un set públicos.  Debería entonces ser TwoWay automáticamente.  Pero bueno, eso no es importante.  Probablemente el binding le falla por el problema derivado de #1.

    Para #3, tal vez no tiene la versión de C# necesaria para ese tipo de código.  ¿Qué versión de Visual Studio utiliza?

    En todo caso, la forma correcta de invocar el evento es, en sintaxis "antigua":

    PropertyChangedEventHandler ev = PropertyChanged;
    if (ev != null)
    {
        ev(this, new PropertyChangedEventArgs(propertyName));
    }

    Debe hacerse la copia del delegado del evento para evitar problemas de concurrencia.  Pero si gusta, puede utilizar la clase NotifyPropertyChanged que yo proveo en wj.DataBinding.  Como verá en la página explicativa, usarla es muy fácil, y en una nueva versión mejoraré el caso de propiedades calculadas para que sea aún más sencillo.

    En fin, para ayudarle, cuénteme cómo asigna el ViewModel al XAML como le pregunté en #1, y si no es muy grande, muéstreme el ViewModel también.  Si no, pues la función que llena la lista.


    Jose R. MCP
    My GIT Repositories | Mis Repositorios GIT

    jueves, 5 de julio de 2018 22:15
  • Me tuve qué salir a una junta, llegando continúo, estaba pasando todo al viewmodel... pero: 1 creo que solo puse el ItemsSource como puse, en la definición del datagrid en .xaml, me parece que en datacontext está a nivel usercontrol hacia el mismo uc 3 uso VS 2013 En cuanto pueda mando más datos...

    cyndyrdz

    jueves, 5 de julio de 2018 23:09
  • no lo puedo creer!

    está bien "obvio" MI ERROR....

    en la declaración de mi Datagrid le puse IsReadOnly=true!!!

    tan obvio que me tardé horas en encontrarlo... el resto de la implementación estaba bien...

    (Partner) muchas gracias por tu apoyo, y voy a revisar despacio tus sugerencias

    Saludos



    cyndyrdz

    viernes, 6 de julio de 2018 16:36