none
Login con procedimiento almacenado RRS feed

  • Pregunta

  • Hola a todos.

    Tengo una consulta, estoy haciendo un login con procedimientos almacenados y windows forms, hasta el momento puedo ingresar correctamente, la ayuda que me gustaría que me colaboraran, es que al seleccionar el tipo de usuario desde un combobox, puede ser administrador u otro, pueda poder ingresar, el combobox lo estoy llenando con otro procedimiento almacenado.

    Como verán abajo es mi procedimiento sp_login donde idRoles es una llave foránea de otra tabla llamada tb_rol, donde solo tienen campos id y rol.

    Tengo este DataTable donde llamo al procedimiento

    public DataTable n_Login(L_Login obj)
            {
                SqlConnection cn = Conexion.ObtenerConexion();
                SqlCommand cmd = new SqlCommand("sp_ConsultaLogin", cn);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@logueo", obj.logueo);
                cmd.Parameters.AddWithValue("@contrasena", obj.contrasena);
                cmd.Parameters.AddWithValue("@idRol", obj.idLRoles); -----> sin esta línea me ingresa todo bien
                SqlDataAdapter da = new SqlDataAdapter(cmd);
                DataTable dtable1 = new DataTable();
                da.Fill(dtable1);
                return dtable1;
            }

    En el formulario en el botón ingresar

    private void btn_Ingresar_Click(object sender, System.EventArgs e)
            {
                DataTable dt = new DataTable();
                objl.logueo = txt_Usuario.Text;
                objl.contrasena = txt_Pass.Text;
                dt = objn.logueo(objl);
                if (string.IsNullOrEmpty(txt_Usuario.Text) || string.IsNullOrEmpty(txt_Pass.Text))
                {
                    MessageBox.Show("Debe Completar los campos necesarios");
                }
                else
                {
                    //if(cbo_Rol.Text == objl.idLRoles.ToString()) ----->No se si esta bien colocar esa condicional donde mi combobox es igual a lo que estoy llenando de mi base de datos, esta parte es donde me gustaría que me colaboren como poder ingresar al seleccionar el tipo de rol o tipo de usuario.
                    //{
                        if (dt.Rows.Count > 0)
                        {
                            objl.logueo = dt.Rows[0][1].ToString();
                            objl.contrasena = dt.Rows[0][2].ToString();
                            //objl.idLRoles = int.Parse(dt.Rows[0][3].ToString());
                            //MessageBox.Show("Bienvenido " + obje.logueo);
                            this.Hide();
                            frm_Principal pri = new frm_Principal();
                            pri.Show();
                            pri.lbl_NombreLogin.Text = "Bienvenido " + objl.logueo;
                        }
                        else
                        {
                            MessageBox.Show("Usuario Incorrecto");
                        }
                    //}
                    //MessageBox.Show("Seleccione un Rol");
                }
            }

    • Cambiado EricRR lunes, 5 de julio de 2021 14:08
    lunes, 28 de junio de 2021 7:15

Respuestas

  • Perdón! Faltaba algo ... lo que hacía antes era asignar el id del Rol seleccionado del cbo_Rol a la propiedad idRoles del objeto L_Login objl, pero no ponía la condición de que coincida el Rol para que se muestre el Form de bienvenida. Ahora lo arreglé. Vi que el DataTable en su 3ra columna trae el Rol como string (del sp_login), no el id del Rol, así que puse un if que pida que el SelectedItem (fijate como obtengo de él el Rol como string) sea igual al Rol que viene en la 3ra columna del dt, con lo cual ahí si los 3 datos coincidieron, y entonces se oculta el frm_login (yo lo cerraría con Close()) y se abre el frm_Principal, dejando el else (no coincide el Rol) para que lo llenes con lo que habría que hacer cuando no coincide el Rol seleccionado con el de la tabla tb_login.

    Espero que ahora si ya esté listo.

    Saludos

    • Marcado como respuesta KaLuKaLuSoft miércoles, 30 de junio de 2021 2:59
    martes, 29 de junio de 2021 22:01
  • Hola KaLu

    Como el Timer no va muy bien con el MessageBox, hice otro pequeño Form, frm_Esperar, cuya función es esperar 10 (te va avisando cuantos faltan en un Label) segundos, y al transcurrir estos, habilita un Button que cierra este Form, volviendo al frm_Login. Para esto el Interval del timer1 es 1000 (1 segundo) y en su controlador del Evento Tick actualiza el Label, y cuando llega a 0 habilita el Button que cierra el frm_Esperar y regresa al frm_Login. También se puede hacer que al llegar a 0 se cierre automáticamente, eso hacelo como más te guste.

    El código del frm_Login, hice algunos cambios, puse la variable (de clase) intentos, que al fallar 3 veces muestra el frm_Esperar (y asigna a intentos 0 de nuevo), no pude cambiar Hide() por Close() para cerrar frm_Login cuando se abre frm_Principal porque pusiste a frm_Login como Form de inicio (creo que sería mejor poner a frm_Principal de inicio y abrir frm_Login y que este último no se cierre hasta tener éxito, y recién ahí cerrarse y que se ejecute frm_Principal), moví el código:

    dt = objn.n_Login(objl);

    a la parte en que no se ingresó vacío en los TextBox de usuario o contraseña, saqué la instanciación de dt que no era necesaria, y saqué las lineas comentadas. Fijate bien todos los cambios y podes probar el programa para ver que funciona. Todo esto no considera si entre los 3 intentos fallidos se cierra y vuelve a abrir la aplicación (que se podría hacer pero es un poco más complicado).

    Te paso el código de frm_Login primero

    using System.Windows.Forms;
    using Capa_Negocio;
    using System.Data;
    using Capa_Logica;
    
    namespace Ventas
    {
        public partial class frm_Login : Form
        {
            private L_Login objl = new L_Login();
            private N_Login objn = new N_Login();
            private int intentos;
    
            public frm_Login()
            {
                InitializeComponent();
            }
            private void frm_Login_Load(object sender, System.EventArgs e)
            {
                cbo_Rol.DataSource = N_Login.ObtenerRoles();
                cbo_Rol.DisplayMember = "rol";
                cbo_Rol.ValueMember = "id";
            }
    
            private void btn_Ingresar_Click(object sender, System.EventArgs e)
            {
                DataTable dt;
                if (string.IsNullOrEmpty(txt_Usuario.Text) || string.IsNullOrEmpty(txt_Pass.Text))
                {
                    MessageBox.Show("Debe Completar los campos necesarios");
                }
                else
                {
                    objl.logueo = txt_Usuario.Text;
                    objl.contrasena = txt_Pass.Text;
                    dt = objn.n_Login(objl);
                    string Rol = ((L_Login)cbo_Rol.SelectedItem).rol;
                    if (dt.Rows.Count > 0)
                    {
                        objl.logueo = dt.Rows[0][0].ToString();
                        objl.contrasena = dt.Rows[0][1].ToString();
                        if (Rol == dt.Rows[0][2].ToString())
                        {
                            this.Hide();
                            frm_Principal pri = new frm_Principal();
                            pri.Show();
                            pri.lbl_NombreLogin.Text = "Bienvenido " + objl.logueo + " / " + Rol;
                        }
                        else 
                        {
                            intentos++;
                            MessageBox.Show("El Rol seleccionado es incorrecto", "Error");
                            if (intentos == 3)
                            {
                                frm_Esperar esp = new frm_Esperar();
                                esp.ShowDialog();
                                intentos = 0;
                            }
                        }
                    }
                    else
                    {
                        intentos++;
                        MessageBox.Show("Usuario o Contraseña Incorrecto");
                        if (intentos == 3) 
                        {
                            frm_Esperar esp = new frm_Esperar();
                            esp.ShowDialog();
                            intentos = 0;
                        }
                    }
                }
            }
        }
    }

    y segundo, el de frm_Esperar. En este, en diseño, tendrías que agregar un Timer (Interval a 1000, Enabled a false), un Button (Text a "Volver al Login", Enabled a false), y un Label (Text a "Segundos restantes antes de desbloquear: 10").

    using System;
    using System.Windows.Forms;
    
    namespace Ventas
    {
        public partial class frm_Esperar : Form
        {
            private int segundos = 9;
            public frm_Esperar()
            {
                InitializeComponent();
            }
    
            private void timer1_Tick(object sender, EventArgs e)
            {
                label1.Text = "Segundos restantes antes de desbloquear: " + segundos;
                if (segundos == 0) 
                {
                    label1.Text = "Ya se puede volver al Login.";
                    timer1.Stop();
                    button1.Enabled = true;
                }
                segundos--;
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                Close();
            }
    
            private void frm_Esperar_Load(object sender, EventArgs e)
            {
                timer1.Start();
            }
        }
    }

    Me parece que sería mejor poner a frm_Principal como Form de inicio, así se puede cerrar frm_Login cuando este ya se realizó, aunque por ahora no habría problemas con esto, pero es lo más normal, no se después si amplias el programa si habrá problemas. 

    Espero que te sea útil.

    Saludos

    Pablo

    EDITO: Me olvidé de decirte que a frm_Esperar tenes que poner su propiedad ControlBox a false para que no se pueda cerrar con la cruz de arriba a la derecha.



    sábado, 3 de julio de 2021 22:18
  • Hola Kalu

    No me molesta para nada que me pregunten todo lo que necesiten, es un gusto ayudar a compañeros/as de otros países, además es lo que me gusta, la enseñanza y la programación. También trato de no sólo pasar código sino que se entienda lo que hace, para que los programadores más jóvenes aprendan, como vos decís que queres hacer, que es lo importante.

    Creo que antes el DataGridView estaba, pero como estaba vacío y no se llenaba en tiempo de ejecución, creí que no estaba. Algo faltaba antes, parte del programa y parte de la BD, ahora está todo, excepto el SP sp_ListarUsuario que no estaba y lo agregué yo. 

    Se podría separar el nombre completo sólo si cada parte fuese una sola palabra, pero por ejemplo si tenes:

    Juana María González de los Pilates Canteros

    es muy fácil unir las palabras, pero ¿cómo sabe el sistema cuántas palabras tiene cada parte? En este caso sabemos que es 2 - 4 - 1 ... ¿pero si fuese:

    Enrique Solanas Ramírez De Los Palotes?

    sería 1 - 1 - 4 ... o ni yo lo se, podría ser también 1 - 2 - 3

    Esto se soluciona muy fácilmente, incluyendo en la consulta tanto el nombre completo como las 3 partes, y si queres que en el DataGridView se vea sólo el nombre completo, haces invisibles las columnas que tendrían las 3 partes. Luego, en el evento CellContentDoubleClick tomas las 3 partes por separado (del DataTable que ponemos a nivel de clase para tenerlo a mano) para llenar los TextBox. Te paso como quedó el archivo frm_Usuario.cs, del cual eliminé el método comentado del CellContentClick, porque puse el nuevo (y para que el archivo no sea tan largo) y porque me parece más usual que para ir a Actualizar se tenga que dar doble click que uno solo, igual si queres cambiarlo, hace como prefieras. 

    Una cosa que me parece ineficiente es que estén separados Insertar y Actualizar, creo que podrían utilizar el mismo TabPage para hacer su cometido, sólo difiriendo en el comando que ejecutan. Además, en tu Button Actualizar, haces referencia a los TextBox de Insertar y no a los de Actualizar, que también se complicaría de ponerles nombres diferentes, por ahora los del tabpage2 están con números.

    using System;
    using System.Data;
    using System.Windows.Forms;
    using Capa_Negocio;
    using Capa_Datos;
    using Capa_Logica;
    
    namespace Ventas
    {
        public partial class frm_Usuario : Form
        {
            private DataTable tabla;
            private N_InsertarUsuario objI = new N_InsertarUsuario();
            private N_ActualizarUsuario objA = new N_ActualizarUsuario();
            private Conexion cn = new Conexion();
            private L_Usuario objL = new L_Usuario();
    
            public frm_Usuario()
            {
                InitializeComponent();
            }
            
            private void btn_Guardar_Click(object sender, EventArgs e)
            {
                objL.nombre = txt_Nombre.Text;
                objL.apellidopaterno = txt_ApellidoPaterno.Text;
                objL.apellidomaterno = txt_ApellidoMaterno.Text;
                objL.correo = txt_Correo.Text;
                objL.telefono = Convert.ToInt32(txt_Telefono.Text);
                objL.celular = Convert.ToInt32(txt_Celular.Text);
                objL.comentario = txt_Comentario.Text;
                objI.InsertarUsuarios(objL);
                MessageBox.Show("Ingresado Correctamente");
            }
            private void btn_Actualizar_Click(object sender, EventArgs e)
            {
                objL.nombre = txt_Nombre.Text;
                objL.apellidopaterno = txt_ApellidoPaterno.Text;
                objL.apellidomaterno = txt_ApellidoMaterno.Text;
                objL.correo = txt_Correo.Text;
                objL.telefono = Convert.ToInt32(txt_Telefono.Text);
                objL.celular = Convert.ToInt32(txt_Celular.Text);
                objL.comentario = txt_Comentario.Text;
                objA.ActualizarUsuario(objL);
                MessageBox.Show("Actualizado Correctamente");
                
            }
    
            private void ListarUsuarios()
            {
                N_ListarUsuario listar = new N_ListarUsuario();
                tabla = listar.ListarProductos();
                dataGridView1.DataSource = tabla; 
                dataGridView1.Columns[1].Visible = false;
                dataGridView1.Columns[2].Visible = false;
                dataGridView1.Columns[3].Visible = false;
            }
            private void frm_Usuario_Load(object sender, EventArgs e)
            {
                ListarUsuarios();
            }
            private void txt_filtrar_TextChanged(object sender, EventArgs e)
            {
                //if(cb_ListarUsuario.Text == "Nombre")
                //{
                //    SqlDataAdapter sda = new SqlDataAdapter("select nombre + ' ' + apellidopaterno + ' ' + apellidomaterno AS [Nombre] from tb_usuario WHERE Nombre LIKE '" + txt_filtrar.Text + "%'", cn.ToString());
                //    DataTable data = new DataTable();
                //    sda.Fill(data);
                //    dataGridView1.DataSource = data;
                //}
            }
    
            private void dataGridView1_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
            {
                tabControl1.SelectedTab = tabPage2;
                textBox2.Text = tabla.Rows[e.RowIndex].Field<string>(1);
                textBox1.Text = tabla.Rows[e.RowIndex].Field<string>(2);
                textBox7.Text = tabla.Rows[e.RowIndex].Field<string>(3);
                textBox5.Text = tabla.Rows[e.RowIndex].Field<int>(4).ToString();
                textBox4.Text = tabla.Rows[e.RowIndex].Field<int>(5).ToString();
                textBox6.Text = tabla.Rows[e.RowIndex].Field<string>(6);
                textBox3.Text = tabla.Rows[e.RowIndex].Field<string>(7);
            }
        }
    }
    

    Y así quedaría el SP para que traiga tanto el nombre completo como las 3 partes:

    ALTER PROC [dbo].[sp_ListarUsuario]
    AS 
    SELECT nombre + ' ' + apellidopaterno + ' ' + apellidomaterno AS [Nombre Completo], 
        nombre AS Nombre, apellidopaterno AS [Apellido Paterno], apellidomaterno AS [Apellido Materno], 
        telefono AS Telefono, celular AS Celular, correo AS Correo, comentarios AS Comentarios
    FROM tb_usuario
    GO
    Algunas otras cositas que te señalo, el frm_Usuario aparece varias veces si hago click en el Menu, a los objetos que declaras en la clase está bueno ponerles private (aunque por omisión lo serán), no sé por qué 'ListarProductos()' en lugar de 'ListarUsuarios()', los números de teléfono lo usual es hacerlos como string por si comienzan con 0 o si llevan algún guión, y los MessageBox que dicen "Actualizado e Insertado correctamente" creo que deberían estar en los métodos que hacen eso, si es que de verdad tuvieron éxito, o que devuelvan algo como true para que sea muestre el mensaje, porque si no siempre va a verse ese mensaje, sea cierto o no. Y te digo de nuevo, creo que sería mucho más eficiente, prolijo, y cómodo, usar el mismo TabPage para Insertar y Actualizar, también te evitará problemas con los nombres de los TextBox. 


    Saludos

    • Marcado como respuesta KaLuKaLuSoft miércoles, 14 de julio de 2021 6:08
    martes, 13 de julio de 2021 19:31
  • Hola

    No es necesario poner el método ListarUsuarios() luego de id = ... , porque ya están cargados los datos, en todo caso después de hacer el Update si, para que se reflejen los cambios.

    El código que te pasé me equivoqué, y creo que quizás te podrías haber dado cuenta en qué, pero bueno, aquí está:

    var data = (byte[])tabla.Rows[e.RowIndex][5];  // o tabla.Rows[e.RowIndex].Field<Byte[]>(5)

    var stream = new MemoryStream(data);

    pictureBox1.Image = Image.FromStream(stream);

    Saludos

    • Marcado como respuesta KaLuKaLuSoft viernes, 3 de septiembre de 2021 1:54
    viernes, 27 de agosto de 2021 13:09

Todas las respuestas

  • Hola KaLu

    No pusiste que error se produce, el mensaje del mismo. Por lo pronto, veo algunas cosas raras, como 

     objl.logueo = dt.Rows[0][1].ToString();

    que debería comenzar en 0 asi

     objl.logueo = dt.Rows[0][0].ToString();

    y que es 'objn' en

    dt = objn.logueo(objl);

    Fijate por favor de ordenar el código y explicar mejor cual es el error y su mensaje y en que linea se genera. Creo que podría ser en 

    //objl.idLRoles = int.Parse(dt.Rows[0][3].ToString());

    y que sería IndexOutOfRangeException, pero confirmamelo.

    Podes debuggear para encontrar el error.

    Saludos

    Pablo


    lunes, 28 de junio de 2021 15:07
  • Acá

     cmd.Parameters.AddWithValue("@idRol", obj.idLRoles); -----> sin esta línea me ingresa todo bien

    si usas el SP que mostras el código entonces no está declarado el parámetro idRol, pero quizás no es el mismo porque en el código C# lo llamas 'sp_ConsultaLogin', no 'sp_login' ...  me parece que deberías usar mejores nombres en tus variables y objetos en general para que no haya tanta confusión.

    lunes, 28 de junio de 2021 15:17
  • gracias por la observación, sp_ConsultaLogin y sp_Login tienen los mismos parámetros

    objl.logueo = dt.Rows[0][1].ToString(); ---->[0]-- es la columna [1]--es la fila, la fila [0][0]---seria el id y el id no lo necesito, de todos modos intenté como mencionaste igual me sale el mensaje que seleccione un rol

    dt = objn.logueo(objl); --- objb.logueo esta llamando a un método que tengo en mi clase en la capa negocio,pero lo cambio con n_Login, porque logueo lo tenía en otra capa y lo coloque en mi capa negocio, como verás en la imagen igual no me deja, me sigue saliendo que seleccione un rol

    Alguna otra observación me avisa 

    Saludos.

    lunes, 28 de junio de 2021 19:09
  • Ahora se cual es el problema. Primero, te pregunto, ¿el rol no debería estar en la tabla? o sea, ¿saber directamente cuál es el rol de cada usuario, consultando la tabla por nombre y contraseña, sin necesidad del ComboBox de rol? ¿y que sólo haya que especificar el rol del usuario al registrarse, o que se asigne de otra manera?

    De todas formas, la causa por la que te muestra el cartel "Seleccione un Rol" es que 

    if (cbo_Rol.Text == objl.idRoles.ToString())

    resulta false, por eso ejecuta lo que hay en el else

    Acerca de este if, me parece que la condición para que te de true es que SelectedIndex no sea -1, caso contrario sería que no hay Item (o índice) seleccionado, con lo cual ahí si debería mandarte a seleccionar un Rol. En cambio, si SelectedIndex del ComboBox no es -1, para acceder al Rol seleccionado simplemente

    cbo_Rol.SelectedItem.ToString()

    o si queres buscarlo por su índice que no se como preferiras

    cboRol.Items[cboRol.SelectedIndex].ToString()

    No se de que tipo es idRoles, me parece que int, de ser así iba a ser muy difícil que alguna vez sea true

    if (cbo_Rol.Text == objl.idRoles.ToString())

    porque se supone que los Items del cbo_Rol son roles, no números, por eso se ejecuta el else que te manda a seleccionar un Rol, equivocadamente.

    Como cosa aparte de este asunto del Rol, me parece que el primer else (el más externo) que le sigue al primer if del código que me pasaste en la imagen, es innecesario, o sea que se podría suprimir y dejar su código interno justo detrás del cuerpo del primer if. Sin embargo, no produce errores de ninguna clase, lo cual es algo raro. 

    En resúmen, creo que hay que cambiar

    if (cbo_Rol.Text == objl.idRoles.ToString())

    por 

    if (cbo_Rol.SelectedIndex != -1)

    y acceder al Rol del ComboBox así

    cbo_Rol.SelectedItem.ToString()

    Ojalá te sea útil.

    Saludos

    Pablo


    lunes, 28 de junio de 2021 20:32
  • El rol lo tengo en otra tabla como veras en la siguiente imagen

    Como son tablas relacionadas, el idRoles de tb_login esta tomando el valor id de tb_roles, hice lo que me comentaste y me sale de esta manera, el cbo_Rol.SelectedItem.ToString() lo coloque en un messagebox para ver si me muestra el nombre del item seleccionado, luego de aceptar me abre el otro formulario.

    De todos modos te dejo el link en github para puedas ver el código con la base de datos más detenidamente.

    archivos

    En resumen lo que estoy intentando hacer es al ingresar el usuario, contraseña y al seleccionar el tipo de rol correcto esto me abra otro formulario si el rol no coincide no me abra nada o muestre algún mensaje. Como dije antes el combobox estoy agarrando los datos desde la base de datos no estoy colocandolo manualmente

    Saludos

    martes, 29 de junio de 2021 3:56
  • Hola

    Me marca lleno de errores por la relación de los proyectos. Yo no hago los programas con proyectos distintos, por eso no se como hacerlo funcionar. ¿Me podrías explicar? 

    Y también, ¿me podrías explicar que es lo que habría que hacer luego de seleccionar el Rol? Decías que hay que ver si coincide, ¿con qué? y ¿qué hacer después?

    EDITO: ya pude resolver los errores y compila. Ahora la BD ¿cómo la hago? ¿con el script? ¿Viene con los datos ya?

    EDITO:

    Te cuento que hay un error en la definición de sp_ActualizarLogin, donde dice idRol debe decir idRoles, que es el nombre que le pusiste al campo de tb_login, aunque me parece que si le ponías idRol era mejor, porque no creo que haya usuarios con más de un Rol ni que un campo pueda tener más de un valor.

    alter proc [dbo].[sp_ActualizarLogin]
    @id int,
    @logueo varchar(50),
    @contrasena varchar(50),
    @idRol int
    as
    begin
    update tb_login set logueo=@logueo, contrasena=@contrasena, idRol=@idRol
    where id=@id
    end
    GO

    EDITO:

    Acá hay un error en tiempo de ejecución (que creo que lo vi antes viendo el código que mostraste), que dice que le pasaste demasiados argumentos al SP, y comenté esa linea. No se cómo pudiste llegar al Form login con este error en el medio, pero bueno ... sigo viendo tu programa. 

    martes, 29 de junio de 2021 19:12
  • No entiendo que ocurrió que no me dejaba seguir escribiendo antes ... acá está la captura del error en tiempo de ejecución, y comente la linea que supongo causaba el error

    martes, 29 de junio de 2021 20:24
  • Acá hay otro error, aunque el primer campo que es el id no lo uses, no lo cargas en tu DataTable, o sea que no está en Rows[0][0] el id, y los demás campos en 1, 2, y 3, sino que están en ese dt sólo los últimos 3 campos en 0, 1, y 2. 

    martes, 29 de junio de 2021 20:34
  • Acá, al intentar convertir a int un string que contiene un Rol, arroja la excepción de formato. Voy a ver si lo puedo obtener con cbo_Rol.SelectedValue al idRoles. Sigo viendo tu programa. Creo que si seguimos así en un rato se soluciona el problema.

    martes, 29 de junio de 2021 20:43
  • Fijate aquí, ya con idRoles asignado con cbo_Rol.SelectedValue que, efectivamente le trae el id seleccionado, y el asunto del cbo_Rol.SelectedItem el problema era que no era un string puro, sino un objeto de la clase L_Login, así como ves en la imagen pude extraer (aunque en el código no es necesario) el valor de la propiedad rol del mismo que efectivamente trae "administrador" o lo que sea. 

    martes, 29 de junio de 2021 20:56
  • Finalmente, comento las lineas que no son necesarias. El SelectedItem de cbo_Rol nunca será null porque está enlazado a datos, lo del string del Rol y el MessageBox no hace falta, y el else que te manda a seleccionar un Rol no hace falta porque siempre habrá uno seleccionado. Y agrego en el mensaje de error de logueo que es usuario O CONTRASEÑA incorrecto. 

    martes, 29 de junio de 2021 21:10
  • Y ahora si, ocurre como vos decías, que al seleccionar el usuario, contraseña, y Rol correctos, se abre un Form de bienvenida. 

    Espero que te sea de ayuda. Cualquier otra cosa que necesites avisame.

    Saludos

    Pablo

    martes, 29 de junio de 2021 21:15
  • Hola, gracias por el tiempo que te tomas al colaborarme, al comentar las líneas que está en la imagen, si pruebas por ejemplo entrar con el usuario bibliotecario con su contraseña pero seleccionas administrador igual te entra, solo debería entrar cuando se selecciona el rol bibliotecario, así respectivamente con el de admin u otros que llegue a agregar.

    admi = rol administrador

    bibliotecario = rol bibliotecario

    martes, 29 de junio de 2021 21:53
  • Perdón! Faltaba algo ... lo que hacía antes era asignar el id del Rol seleccionado del cbo_Rol a la propiedad idRoles del objeto L_Login objl, pero no ponía la condición de que coincida el Rol para que se muestre el Form de bienvenida. Ahora lo arreglé. Vi que el DataTable en su 3ra columna trae el Rol como string (del sp_login), no el id del Rol, así que puse un if que pida que el SelectedItem (fijate como obtengo de él el Rol como string) sea igual al Rol que viene en la 3ra columna del dt, con lo cual ahí si los 3 datos coincidieron, y entonces se oculta el frm_login (yo lo cerraría con Close()) y se abre el frm_Principal, dejando el else (no coincide el Rol) para que lo llenes con lo que habría que hacer cuando no coincide el Rol seleccionado con el de la tabla tb_login.

    Espero que ahora si ya esté listo.

    Saludos

    • Marcado como respuesta KaLuKaLuSoft miércoles, 30 de junio de 2021 2:59
    martes, 29 de junio de 2021 22:01
  • Me aparece ese error y copie sino me equivoco tal como esta en la imagen

    • Marcado como respuesta KaLuKaLuSoft miércoles, 30 de junio de 2021 2:58
    • Desmarcado como respuesta KaLuKaLuSoft miércoles, 30 de junio de 2021 2:59
    martes, 29 de junio de 2021 23:03
  • Fijate en mi primer mensaje con imagen. Ahí me lo marcó a mi en ejecución, que estás pasándole al SP un argumento de más. Sólo debe recibir usuario y contraseña y le pasas también el Rol. En la imagen comenté la linea que provoca el error. Es en el archivo N_Login.cs.

    Cuando arregles eso va a funcionar correctamente (si tenes el archivo frm_Login.cs como en mi última imagen). Y podes comentar o borrar las lineas innecesarias.

    Saludos

    miércoles, 30 de junio de 2021 0:08
  • Muchas gracias, hice una modificación al procedimiento en el sql, quedo de esta manera 

    Al final se logró el objetivo 

    Una consulta más, para hacer que un usuario al intentar 3 veces se bloquee por 2 minutos por ejemplo o que un administrador lo desbloquee desde una configuración, que tipo de datos me recomiendas que utilizar para guardar ese valor en la base de datos? un bit? para 0 este habilitado y 1 bloqueado? u otro? en el procedimiento tendría que colocar alguna condicional para lograr eso?

    miércoles, 30 de junio de 2021 2:58
  • Hola

    Podes ponerle al objeto objl una variable intentos de tipo int, y al loguear si se coloca un nombre de usuario correcto y la contraseña y/o el Rol incorrecto, ir aumentando esa variable, y cuando llega a 3, haces un pequeño Form que avise del hecho, y que no se pueda salir del mismo por 2 minutos con un Timer, para que no se pueda salir controlas el Evento FormClosing y le asignas true a la propiedad Cancel del parámetro e (si no recuerdo mal) hasta que pasen los 2 minutos. Para esto, tendrás que tener un método o algo que verifique que el nombre de usuario es correcto aunque la contraseña sea incorrecta, porque justamente eso sería que un usuario falle el intento; si se ingresa un usuario incorrecto, ¿Qué se debería hacer? ¿También bloquear por 2 minutos? 

    Si queres hacer que el admin desbloquee al usuario bloqueado, creo que habría que hacer algo un poco más grande, primero entrar como admin (y no fallar en el logueo, ¿o si no, qué hacer?), y segundo tendría que haber un nuevo Form con el/los usuario(s) bloqueado(s) y que el admin los desbloquee con un Button, o quizás simplemente algo, un Button o un MessageBox que le pregunte si quiere desbloquear a todos los bloqueados, al iniciar sesión o loguearse. 

    Fijate cual de las 2 preferis, y si podes ir haciéndolo; si te trabas, ahí veo yo de hacerlo, lo que si esto no es taaan sencillo como lo anterior, pero no importa, lo hacemos.

    miércoles, 30 de junio de 2021 17:10
  • Hola, estaba intentando de los 3 intentos y lo estaba haciendo de esta manera.

    De 2min lo baje a 10 segundos, me quede que al hacer 3 intentos se bloquea el botón de ingresar y a los 10 segundos se desbloquea, esta no es la manera que me gustaría que quedara, espero este bien mi procedimiento o si tuvieras alguno mejor para ir aprendiendo más.

    Una consulta, mientras lo hacía se me vino a la mente, se podría hacer o utilizar mi procedimiento almacenado agregando un tipo de dato bit, para que cuando se bloque en el último intento, al dar clic en el último messagebox este haga un update y que en la base de datos cambie el bit a inactivo? por defecto podría estar activo = 0, inactivo = 1, luego de los 10 segundos se mande otro update ya con el bit como activo?

    Si se llegase a poder en vez de bloquear el boton ingresar, cómo podría validar que el usuario está bloqueado, puede que un usuario no espere los 10 segundos y al volver intentar no lo logre.

    Saludos


    • Editado KaLuKaLuSoft sábado, 3 de julio de 2021 6:28 Actualización
    sábado, 3 de julio de 2021 6:16
  • Hola KaLu

    Como el Timer no va muy bien con el MessageBox, hice otro pequeño Form, frm_Esperar, cuya función es esperar 10 (te va avisando cuantos faltan en un Label) segundos, y al transcurrir estos, habilita un Button que cierra este Form, volviendo al frm_Login. Para esto el Interval del timer1 es 1000 (1 segundo) y en su controlador del Evento Tick actualiza el Label, y cuando llega a 0 habilita el Button que cierra el frm_Esperar y regresa al frm_Login. También se puede hacer que al llegar a 0 se cierre automáticamente, eso hacelo como más te guste.

    El código del frm_Login, hice algunos cambios, puse la variable (de clase) intentos, que al fallar 3 veces muestra el frm_Esperar (y asigna a intentos 0 de nuevo), no pude cambiar Hide() por Close() para cerrar frm_Login cuando se abre frm_Principal porque pusiste a frm_Login como Form de inicio (creo que sería mejor poner a frm_Principal de inicio y abrir frm_Login y que este último no se cierre hasta tener éxito, y recién ahí cerrarse y que se ejecute frm_Principal), moví el código:

    dt = objn.n_Login(objl);

    a la parte en que no se ingresó vacío en los TextBox de usuario o contraseña, saqué la instanciación de dt que no era necesaria, y saqué las lineas comentadas. Fijate bien todos los cambios y podes probar el programa para ver que funciona. Todo esto no considera si entre los 3 intentos fallidos se cierra y vuelve a abrir la aplicación (que se podría hacer pero es un poco más complicado).

    Te paso el código de frm_Login primero

    using System.Windows.Forms;
    using Capa_Negocio;
    using System.Data;
    using Capa_Logica;
    
    namespace Ventas
    {
        public partial class frm_Login : Form
        {
            private L_Login objl = new L_Login();
            private N_Login objn = new N_Login();
            private int intentos;
    
            public frm_Login()
            {
                InitializeComponent();
            }
            private void frm_Login_Load(object sender, System.EventArgs e)
            {
                cbo_Rol.DataSource = N_Login.ObtenerRoles();
                cbo_Rol.DisplayMember = "rol";
                cbo_Rol.ValueMember = "id";
            }
    
            private void btn_Ingresar_Click(object sender, System.EventArgs e)
            {
                DataTable dt;
                if (string.IsNullOrEmpty(txt_Usuario.Text) || string.IsNullOrEmpty(txt_Pass.Text))
                {
                    MessageBox.Show("Debe Completar los campos necesarios");
                }
                else
                {
                    objl.logueo = txt_Usuario.Text;
                    objl.contrasena = txt_Pass.Text;
                    dt = objn.n_Login(objl);
                    string Rol = ((L_Login)cbo_Rol.SelectedItem).rol;
                    if (dt.Rows.Count > 0)
                    {
                        objl.logueo = dt.Rows[0][0].ToString();
                        objl.contrasena = dt.Rows[0][1].ToString();
                        if (Rol == dt.Rows[0][2].ToString())
                        {
                            this.Hide();
                            frm_Principal pri = new frm_Principal();
                            pri.Show();
                            pri.lbl_NombreLogin.Text = "Bienvenido " + objl.logueo + " / " + Rol;
                        }
                        else 
                        {
                            intentos++;
                            MessageBox.Show("El Rol seleccionado es incorrecto", "Error");
                            if (intentos == 3)
                            {
                                frm_Esperar esp = new frm_Esperar();
                                esp.ShowDialog();
                                intentos = 0;
                            }
                        }
                    }
                    else
                    {
                        intentos++;
                        MessageBox.Show("Usuario o Contraseña Incorrecto");
                        if (intentos == 3) 
                        {
                            frm_Esperar esp = new frm_Esperar();
                            esp.ShowDialog();
                            intentos = 0;
                        }
                    }
                }
            }
        }
    }

    y segundo, el de frm_Esperar. En este, en diseño, tendrías que agregar un Timer (Interval a 1000, Enabled a false), un Button (Text a "Volver al Login", Enabled a false), y un Label (Text a "Segundos restantes antes de desbloquear: 10").

    using System;
    using System.Windows.Forms;
    
    namespace Ventas
    {
        public partial class frm_Esperar : Form
        {
            private int segundos = 9;
            public frm_Esperar()
            {
                InitializeComponent();
            }
    
            private void timer1_Tick(object sender, EventArgs e)
            {
                label1.Text = "Segundos restantes antes de desbloquear: " + segundos;
                if (segundos == 0) 
                {
                    label1.Text = "Ya se puede volver al Login.";
                    timer1.Stop();
                    button1.Enabled = true;
                }
                segundos--;
            }
    
            private void button1_Click(object sender, EventArgs e)
            {
                Close();
            }
    
            private void frm_Esperar_Load(object sender, EventArgs e)
            {
                timer1.Start();
            }
        }
    }

    Me parece que sería mejor poner a frm_Principal como Form de inicio, así se puede cerrar frm_Login cuando este ya se realizó, aunque por ahora no habría problemas con esto, pero es lo más normal, no se después si amplias el programa si habrá problemas. 

    Espero que te sea útil.

    Saludos

    Pablo

    EDITO: Me olvidé de decirte que a frm_Esperar tenes que poner su propiedad ControlBox a false para que no se pueda cerrar con la cruz de arriba a la derecha.



    sábado, 3 de julio de 2021 22:18
  • Hola Tigre Pablito

    La parte de login, por el momento lo deje a un lado luego continúo, para avanzar estoy en la parte de usuarios.

    En la imagen tengo un tabcontrol

    Insertar = tabpage1

    Actualizar = tabpage2

    Eliminar = tabpage3

    Buscar = tabpage4

    Mi pregunta es la siguiente: Cómo puedo pasar los datos del datagridview que está en BUSCAR = tabpage4 a ACTUALIZAR = tabpage2 y rellenarlos en los diferentes textbox para hacer la actualización respectiva.

    Te paso de nuevo el repositorio en github todo actualizado.

    Actualizado

    Saludos

    lunes, 12 de julio de 2021 8:18
  • Hola KaLu

    No hay ningún DataGridView en el tabpage4 (¿Me habrás pasado una versión anterior del código?). Lo que podes hacer es cargar en un DataTable los datos de los usuarios y luego asignar este al DataSource de la grilla. Para cargar los datos necesitas un SqlCommand con una consulta SQL o un SP, y un SqlDataReader, que lo cargas al DataTable con el método Load() del 2do. Luego, al hacer doble click (por ejemplo) en una fila, te puede dirigir al tabpage2 para actualizar y cargas en los TextBox así, en el método del evento DoubleClick del DataGridView:

    textBoxNombre.Text = table.Rows[e.RowIndex]["Nombre"].ToString();

    o lo que es lo mismo

    textBoxNombre.Text = table.Rows[e.RowIndex].Field<string>(e.ColumnIndex);

    Hay 2 cosas que te sugiero cambiar, una es que por cada click al menú usuario aparece un nuevo Form, y tendría que haber uno solo. Y que el código de los tabpage Insertar y Actualizar tendría que ser el mismo, sólo que uno al terminar hace un Insert y el otro hace un Update, pero salvo eso se comportan casi igual. 

    Avisame si de todo esto hay algo que no sabes hacer y lo vemos.

    Saludos


    martes, 13 de julio de 2021 11:50
  • Hola Tigre Pablito

    Acabo de leer lo que respondiste, se me hace extraño que no veas un DataGridView en el tabpage4, acabo de verificar en el GitHub y aparece el DataGridView.

    Acabo de verificar que algo se me olvidó decirte.

    Tengo esta tabla con estos campos.

    Este es mi procedimiento almacenado el cual listo los usuarios en el DataGridView, como verás los campos nombre,apellidopaterno,apellidomaterno los estoy concatenando o uniendo en una sola columna llamada Nombre Completo.

    Como veras en buscar o tabpage4, estoy uniendo esos campos en una sola fila, se podría separar estos campos al  dar doble clic y se rellenen a sus respectivos textbox el nombre,el apellidopaterno y el apellidomaterno? Sino se puede lo haría de la forma normal con los campos separados, para luego actualizar y volverlos a unir como estan en la imagen.

    Disculpa que te moleste tanto, en el internet hay bastantes videos de pasar datos de un DataGridView a otro formulario y rellenar los campos, pero con tabpage casi nada o los que encontre no cumplen ni un poco en lo que estoy intentando hacer, me gusta estar en constante aprendizaje.

    Saludos


    martes, 13 de julio de 2021 16:02
  • Hola Kalu

    No me molesta para nada que me pregunten todo lo que necesiten, es un gusto ayudar a compañeros/as de otros países, además es lo que me gusta, la enseñanza y la programación. También trato de no sólo pasar código sino que se entienda lo que hace, para que los programadores más jóvenes aprendan, como vos decís que queres hacer, que es lo importante.

    Creo que antes el DataGridView estaba, pero como estaba vacío y no se llenaba en tiempo de ejecución, creí que no estaba. Algo faltaba antes, parte del programa y parte de la BD, ahora está todo, excepto el SP sp_ListarUsuario que no estaba y lo agregué yo. 

    Se podría separar el nombre completo sólo si cada parte fuese una sola palabra, pero por ejemplo si tenes:

    Juana María González de los Pilates Canteros

    es muy fácil unir las palabras, pero ¿cómo sabe el sistema cuántas palabras tiene cada parte? En este caso sabemos que es 2 - 4 - 1 ... ¿pero si fuese:

    Enrique Solanas Ramírez De Los Palotes?

    sería 1 - 1 - 4 ... o ni yo lo se, podría ser también 1 - 2 - 3

    Esto se soluciona muy fácilmente, incluyendo en la consulta tanto el nombre completo como las 3 partes, y si queres que en el DataGridView se vea sólo el nombre completo, haces invisibles las columnas que tendrían las 3 partes. Luego, en el evento CellContentDoubleClick tomas las 3 partes por separado (del DataTable que ponemos a nivel de clase para tenerlo a mano) para llenar los TextBox. Te paso como quedó el archivo frm_Usuario.cs, del cual eliminé el método comentado del CellContentClick, porque puse el nuevo (y para que el archivo no sea tan largo) y porque me parece más usual que para ir a Actualizar se tenga que dar doble click que uno solo, igual si queres cambiarlo, hace como prefieras. 

    Una cosa que me parece ineficiente es que estén separados Insertar y Actualizar, creo que podrían utilizar el mismo TabPage para hacer su cometido, sólo difiriendo en el comando que ejecutan. Además, en tu Button Actualizar, haces referencia a los TextBox de Insertar y no a los de Actualizar, que también se complicaría de ponerles nombres diferentes, por ahora los del tabpage2 están con números.

    using System;
    using System.Data;
    using System.Windows.Forms;
    using Capa_Negocio;
    using Capa_Datos;
    using Capa_Logica;
    
    namespace Ventas
    {
        public partial class frm_Usuario : Form
        {
            private DataTable tabla;
            private N_InsertarUsuario objI = new N_InsertarUsuario();
            private N_ActualizarUsuario objA = new N_ActualizarUsuario();
            private Conexion cn = new Conexion();
            private L_Usuario objL = new L_Usuario();
    
            public frm_Usuario()
            {
                InitializeComponent();
            }
            
            private void btn_Guardar_Click(object sender, EventArgs e)
            {
                objL.nombre = txt_Nombre.Text;
                objL.apellidopaterno = txt_ApellidoPaterno.Text;
                objL.apellidomaterno = txt_ApellidoMaterno.Text;
                objL.correo = txt_Correo.Text;
                objL.telefono = Convert.ToInt32(txt_Telefono.Text);
                objL.celular = Convert.ToInt32(txt_Celular.Text);
                objL.comentario = txt_Comentario.Text;
                objI.InsertarUsuarios(objL);
                MessageBox.Show("Ingresado Correctamente");
            }
            private void btn_Actualizar_Click(object sender, EventArgs e)
            {
                objL.nombre = txt_Nombre.Text;
                objL.apellidopaterno = txt_ApellidoPaterno.Text;
                objL.apellidomaterno = txt_ApellidoMaterno.Text;
                objL.correo = txt_Correo.Text;
                objL.telefono = Convert.ToInt32(txt_Telefono.Text);
                objL.celular = Convert.ToInt32(txt_Celular.Text);
                objL.comentario = txt_Comentario.Text;
                objA.ActualizarUsuario(objL);
                MessageBox.Show("Actualizado Correctamente");
                
            }
    
            private void ListarUsuarios()
            {
                N_ListarUsuario listar = new N_ListarUsuario();
                tabla = listar.ListarProductos();
                dataGridView1.DataSource = tabla; 
                dataGridView1.Columns[1].Visible = false;
                dataGridView1.Columns[2].Visible = false;
                dataGridView1.Columns[3].Visible = false;
            }
            private void frm_Usuario_Load(object sender, EventArgs e)
            {
                ListarUsuarios();
            }
            private void txt_filtrar_TextChanged(object sender, EventArgs e)
            {
                //if(cb_ListarUsuario.Text == "Nombre")
                //{
                //    SqlDataAdapter sda = new SqlDataAdapter("select nombre + ' ' + apellidopaterno + ' ' + apellidomaterno AS [Nombre] from tb_usuario WHERE Nombre LIKE '" + txt_filtrar.Text + "%'", cn.ToString());
                //    DataTable data = new DataTable();
                //    sda.Fill(data);
                //    dataGridView1.DataSource = data;
                //}
            }
    
            private void dataGridView1_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
            {
                tabControl1.SelectedTab = tabPage2;
                textBox2.Text = tabla.Rows[e.RowIndex].Field<string>(1);
                textBox1.Text = tabla.Rows[e.RowIndex].Field<string>(2);
                textBox7.Text = tabla.Rows[e.RowIndex].Field<string>(3);
                textBox5.Text = tabla.Rows[e.RowIndex].Field<int>(4).ToString();
                textBox4.Text = tabla.Rows[e.RowIndex].Field<int>(5).ToString();
                textBox6.Text = tabla.Rows[e.RowIndex].Field<string>(6);
                textBox3.Text = tabla.Rows[e.RowIndex].Field<string>(7);
            }
        }
    }
    

    Y así quedaría el SP para que traiga tanto el nombre completo como las 3 partes:

    ALTER PROC [dbo].[sp_ListarUsuario]
    AS 
    SELECT nombre + ' ' + apellidopaterno + ' ' + apellidomaterno AS [Nombre Completo], 
        nombre AS Nombre, apellidopaterno AS [Apellido Paterno], apellidomaterno AS [Apellido Materno], 
        telefono AS Telefono, celular AS Celular, correo AS Correo, comentarios AS Comentarios
    FROM tb_usuario
    GO
    Algunas otras cositas que te señalo, el frm_Usuario aparece varias veces si hago click en el Menu, a los objetos que declaras en la clase está bueno ponerles private (aunque por omisión lo serán), no sé por qué 'ListarProductos()' en lugar de 'ListarUsuarios()', los números de teléfono lo usual es hacerlos como string por si comienzan con 0 o si llevan algún guión, y los MessageBox que dicen "Actualizado e Insertado correctamente" creo que deberían estar en los métodos que hacen eso, si es que de verdad tuvieron éxito, o que devuelvan algo como true para que sea muestre el mensaje, porque si no siempre va a verse ese mensaje, sea cierto o no. Y te digo de nuevo, creo que sería mucho más eficiente, prolijo, y cómodo, usar el mismo TabPage para Insertar y Actualizar, también te evitará problemas con los nombres de los TextBox. 


    Saludos

    • Marcado como respuesta KaLuKaLuSoft miércoles, 14 de julio de 2021 6:08
    martes, 13 de julio de 2021 19:31
  • Hola

    Estoy intentando actualizar, estoy teniendo este problemita, en qué estaría haciendo mal?

    Error 

    Procedimiento

    El botón actualizar

    miércoles, 14 de julio de 2021 6:39
  • Hola

    Ese error es por no pasarle los parámetros, como dice el cartel. A veces los carteles o mensajes de error te explican bien cual es el error, hay que saber interpretarlos. 

    Había algunos errores, como que sp_ActualizarUsuario asignaba apellidomaterno al apellidopaterno, y algunos TextBox cambiados o descolocados en el código. 

    Modifiqué la acción de Actualizar: cuando haces doble click en el DataGridView, te lleva a la tabPage1 y completa los TextBox y el Button dice "Actualizar", y hay un Button "Cancelar"; si clickeas el 2do, se vacían los TextBox y el 1er Button dice "Ingresar", y está así habilitada la opción de Insertar. La única forma de que se habilite la opción de Actualizar es viniendo del DataGridView haciendo doble click en alguna fila, en este caso, si cambias los datos y clickeas en Actualizar, hace el Update y muestra el MessageBox "Actualizado Correctamente", y al aceptar luego vacía los TextBox y vuelve el Text del Button a "Ingresar", habilitando otra vez esta acción; también, si al venir desde el DataGridView para actualizar queremos cancelar, clickeas "Cancelar" y se vacían los TextBox y se vuelve a habilitar la acción de Insertar. Por esto, es que ya la TabPage Actualizar no es necesaria. También agregué los parámetros del SP para actualizar. Lo probé todo, Actualizar e Insertar y funciona bien ... pero estuve como 10 minutos buscando como loco por qué no actualizaba bien el apellido paterno hasta que se me encendió la lamparita y me di cuenta que la única opción que quedaba era que haya un error en el SP. 

    Otro pequeño cambio fue comparar el retorno de ExecuteNonQuery() con 1 (que es lo que debería devolver si se inserta o se actualiza 1 solo registro) y que retornen true o false los métodos InsertarUsuario() y ActualizarUsuario() y en caso de éxito (es muy raro que falle) muestra el MessageBox, y en caso de falla, muestra un MessageBox que lo avisa y al aceptar, luego no vacía los TextBox para dar la posibilidad de intentar de nuevo la acción de Actualizar o Insertar. 

    Otro detalle es que puse una variable de clase, int id, para que al ir del DataGridView a Actualizar, como no hay un TextBox donde se ponga el id, lo tengamos a mano para pasarselo a objL para que éste lo tenga para cuando haya que pasarle los parámetros al SP. Me estaba olvidando, agregué el id a la consulta sp_ListarUsuario, para tenerlo para poder actualizar, y cambié los indices de las columnas invisibles a 0, 2, 3, y 4 que son el id y las 3 partes del nombre, o sea tendrías que arreglar eso, y lo del apellidopaterno en el Update.

    Otra novedad es en el tabPage4 un Button "Refrescar" para actualizar la lista luego de Insertar o Actualizar registros de Usuario.

    Te paso los archivos que modifiqué, 1ro frm_Usuario.cs

    using System;
    using System.Data;
    using System.Windows.Forms;
    using Capa_Negocio;
    using Capa_Datos;
    using Capa_Logica;
    
    namespace Ventas
    {
        public partial class frm_Usuario : Form
        {
            private DataTable tabla;
            private int id;
            private N_InsertarUsuario objI = new N_InsertarUsuario();
            private N_ActualizarUsuario objA = new N_ActualizarUsuario();
            private Conexion cn = new Conexion();
            private L_Usuario objL = new L_Usuario();
    
            public frm_Usuario()
            {
                InitializeComponent();
            }
            
            private void btn_Guardar_Click(object sender, EventArgs e)
            {
                objL.id = id;
                objL.nombre = txt_Nombre.Text;
                objL.apellidopaterno = txt_ApellidoPaterno.Text;
                objL.apellidomaterno = txt_ApellidoMaterno.Text;
                objL.correo = txt_Correo.Text;
                objL.telefono = Convert.ToInt32(txt_Telefono.Text);
                objL.celular = Convert.ToInt32(txt_Celular.Text);
                objL.comentario = txt_Comentario.Text;
                if (btn_Guardar.Text == "Ingresar")
                {
                    if (objI.InsertarUsuarios(objL) == true)
                    {
                        MessageBox.Show("Guardado Correctamente");
                        VaciarTextBoxes();
                    }
                    else
                        MessageBox.Show("Se produjo un error: No guardado");
                }
                else if (btn_Guardar.Text == "Actualizar")
                {
                    if (objA.ActualizarUsuario(objL) == true)
                    {
                        MessageBox.Show("Actualizado Correctamente");
                        VaciarTextBoxes();
                        btn_Guardar.Text = "Ingresar";
                    }
                    else
                        MessageBox.Show("Se produjo un error: No actualizado");
                }
            }
    
            private void ListarUsuarios()
            {
                N_ListarUsuario listar = new N_ListarUsuario();
                tabla = listar.ListarProductos();
                dataGridView1.DataSource = tabla;
                dataGridView1.Columns[0].Visible = false;
                dataGridView1.Columns[2].Visible = false;
                dataGridView1.Columns[3].Visible = false;
                dataGridView1.Columns[4].Visible = false;
            }
    
            private void frm_Usuario_Load(object sender, EventArgs e)
            {
                ListarUsuarios();
            }
    
            private void txt_filtrar_TextChanged(object sender, EventArgs e)
            {
                //if(cb_ListarUsuario.Text == "Nombre")
                //{
                //    SqlDataAdapter sda = new SqlDataAdapter("select nombre + ' ' + apellidopaterno + ' ' + apellidomaterno AS [Nombre] from tb_usuario WHERE Nombre LIKE '" + txt_filtrar.Text + "%'", cn.ToString());
                //    DataTable data = new DataTable();
                //    sda.Fill(data);
                //    dataGridView1.DataSource = data;
                //}
            }
    
            private void dataGridView1_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
            {
                tabControl1.SelectedTab = tabPage1;
                btn_Guardar.Text = "Actualizar";
                txt_Nombre.Text = tabla.Rows[e.RowIndex].Field<string>(2);
                txt_ApellidoPaterno.Text = tabla.Rows[e.RowIndex].Field<string>(3);
                txt_ApellidoMaterno.Text = tabla.Rows[e.RowIndex].Field<string>(4);
                txt_Telefono.Text = tabla.Rows[e.RowIndex].Field<int>(5).ToString();
                txt_Celular.Text = tabla.Rows[e.RowIndex].Field<int>(6).ToString();
                txt_Correo.Text = tabla.Rows[e.RowIndex].Field<string>(7);
                txt_Comentario.Text = tabla.Rows[e.RowIndex].Field<string>(8);
                id = tabla.Rows[e.RowIndex].Field<int>(0);
            }
    
            private void btn_Cancelar_Click(object sender, EventArgs e)
            {
                VaciarTextBoxes();
                btn_Guardar.Text = "Ingresar";
            }
    
            private void VaciarTextBoxes() 
            {
                txt_ApellidoPaterno.Text = "";
                txt_Nombre.Text = "";
                txt_ApellidoMaterno.Text = "";
                txt_Telefono.Text = "";
                txt_Celular.Text = "";
                txt_Correo.Text = "";
                txt_Comentario.Text = "";
            }
    
            private void btn_Refrescar_Click(object sender, EventArgs e)
            {
                ListarUsuarios();
            }
        }
    }
    

    2do, N_ActualizarUsuario.cs

    using System.Data.SqlClient;
    using Capa_Datos;
    using Capa_Logica;
    using System.Data;
    
    namespace Capa_Negocio
    {
        public class N_ActualizarUsuario
        {
            public bool ActualizarUsuario(L_Usuario objl)
            {
                SqlConnection cn = Conexion.ObtenerConexion();
                SqlCommand cmd = new SqlCommand("sp_ActualizarUsuario", cn);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@id", objl.id);
                cmd.Parameters.AddWithValue("@nombre", objl.nombre);
                cmd.Parameters.AddWithValue("@apellidopaterno", objl.apellidopaterno);
                cmd.Parameters.AddWithValue("@apellidomaterno", objl.apellidomaterno);
                cmd.Parameters.AddWithValue("@telefono", objl.telefono);
                cmd.Parameters.AddWithValue("@celular", objl.celular);
                cmd.Parameters.AddWithValue("@correo", objl.correo);
                cmd.Parameters.AddWithValue("@comentarios", objl.comentario);
                if (cmd.ExecuteNonQuery() == 1)
                {
                    cn.Close();
                    return true;
                }
                else
                {
                    cn.Close();
                    return false;
                }
                
            }
        }
    }
    

    y 3ro, N_InsertarUsuario.cs

    using System.Data;
    using System.Data.SqlClient;
    using Capa_Datos;
    using Capa_Logica;
    
    namespace Capa_Negocio
    {
        public class N_InsertarUsuario
        {
            public bool InsertarUsuarios(L_Usuario objl)
            {
                SqlConnection cn = Conexion.ObtenerConexion();
                SqlCommand cmd = new SqlCommand("sp_InsertarUsuario", cn);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@nombre", objl.nombre);
                cmd.Parameters.AddWithValue("@apellidopaterno", objl.apellidopaterno);
                cmd.Parameters.AddWithValue("@apellidomaterno", objl.apellidomaterno);
                cmd.Parameters.AddWithValue("@correo", objl.correo);
                cmd.Parameters.AddWithValue("@telefono", objl.telefono);
                cmd.Parameters.AddWithValue("@celular", objl.celular);
                cmd.Parameters.AddWithValue("@comentario", objl.comentario);
                if (cmd.ExecuteNonQuery() == 1)
                {
                    cn.Close();
                    return true;
                }
                else
                {
                    cn.Close();
                    return false;
                }
                //cmd.Parameters.Clear(); no es necesario porque cmd es variable local que desaparece al salir del método
            }
        }
    }
    

    Por último, te paso un link a mi Dropbox donde está en zip todo el proyecto, así vas analizando y modificando el tuyo original (y vas aprendiendo con la memoria motriz), porque además hice otros pocos cambios a los nombres de los TextBox que estaban al revés, tabPage1 tenía el nombre lbl_comentario, y puse SelectionMode de la grilla a FullRowSelect que es mejor.

    Prueba-master.zip

    Entonces podes eliminar el tabPage2, y el 3 también, porque para eliminar podes poner otro Button en el tabPage4 y que elimine el Usuario que está en la fila seleccionada, podes practicar con eso, que muestre un MessageBox o algo similar que diga el nombre del Usuario y si confirmas la eliminación.

    Saludos


    miércoles, 14 de julio de 2021 20:33
  • Hola Tigre Pablito

    Descargue desde tu dropbox el archivo, lo hice correr y me salió este error

    Por lo que veo el dato 2 sería el nombre, el 3 el appellidopaterno y el 4 apellidomaterno, pero el 4 lo esta tomando como si fuera un entero.

    Sobre los tabPage 2 y 3, los tengo individualmente para reforzar, porque luego ire metiendo insertar,actualizar y eliminar en un solo procedimiento.

    Si pudieses colaborarme, cree una nueva pregunta aca está el link, sino te da podrías entrar a mi perfil y luego a mi actividad, es sobre PILA

    Saludos.

    jueves, 15 de julio de 2021 7:10
  • Hola Kalu

    Porque no cambiaste el SP sp_ListarUsuario para que traiga tambien el id, por eso cree que el índice 4 es el teléfono. Cambia el SP así:

    USE [DB_Prueba]
    GO
    /****** Object:  StoredProcedure [dbo].[sp_ListarUsuario]    Script Date: 15/7/2021 17:38:34 ******/
    SET ANSI_NULLS ON
    GO
    SET QUOTED_IDENTIFIER ON
    GO
    ALTER PROC [dbo].[sp_ListarUsuario]
    AS 
    SELECT id AS Id, nombre + ' ' + apellidopaterno + ' ' + apellidomaterno AS [Nombre Completo], 
        nombre AS Nombre, apellidopaterno AS [Apellido Paterno], apellidomaterno AS [Apellido Materno], 
        telefono AS Telefono, celular AS Celular, correo AS Correo, comentarios AS Comentarios
    FROM tb_usuario
    

    y también cambiale a sp_ActualizarUsuario que a apellidopaterno le asigne @apellidopaterno y no @apellidomaterno que vi que estaba mal.

    Ahora te va a funcionar bien. Fijate en especial como funciona lo de Insertar / Actualizar. Lee bien mi última respuesta si no lo hiciste por favor.

    Saludos


    jueves, 15 de julio de 2021 20:43
  • Hola Tigre Pablito

    Volví de nuevo con esta práctica personal.

    1ro.- Copie el código a mi archivo original me sale este error en el boton para ingresar. (Lo estoy haciendo en un solo tabpage)

    2do.- Modifique el sp_ListarUsuario para abrir el que me pasaste. Agrega todo bien, pero al modificar no me está guardando lo del txt_ApellidoPaterno, como verás a continuació

    ANTES

    DESPUES

    sp_ListarUsuario

    Saludos

    Actualización: El punto 2, logré corregirlo, el problema era en mi sp_ActualizarUsuario donde tenía el apellido materno de esta manera

    @apellidomaterno=@apellidomaterno

    Tuve que corregirlo: apellidomaterno=@apellidomaterno

    Queda pendiente el punto 1 porque me sale ese error y en el tuyo que me pasaste no sale ningún error

    Actualización 2: El problema era que mi N_InsertarUsuario, no lo había copiado
    • Editado KaLuKaLuSoft sábado, 24 de julio de 2021 5:16 Actualizacion Punto 1 y Punto 2
    sábado, 24 de julio de 2021 4:01
  • Hola

    ¿Entonces vas bien por ahora? Te recomiendo leer bien y tratar de entender el código, así te queda grabado y aprendes para la próxima y para otras cosas. (Cualquier duda podes preguntarme). Ya viste que sólo es necesario un TabPage para Insertar/Actualizar y otro para Visualizar los datos, que no hace falta ni para Actualizar separado ni para eliminar. Para esto último, podes poner un Button en el TabPage del DataGridView y que elimine la fila seleccionada, previo mostrar los datos y pedir confirmación con un MessageBox.

    domingo, 25 de julio de 2021 13:50
  • Hola

    Si por el momento estoy bien, los errores que tengo trato de preguntarte, si lo arreglo lo actualizo, así también queda como documentado.

    Quería tenerlo por separado para tenerlo como guía para un futuro pero no hay problema estoy comentando en el código.

    Por ahorita tengo estos problemas:

    Punto 1: Estuve viendo las propiedades del datagridview, no logro eliminar esa fila del circulo grande y del circulo pequeño tengo el problema que cuando le doy clic los datos se me mueven por ejemplo:

    -En la imagen sale 111 2222 333, si le doy clic en el nombre se me mueve y si se queda en aaa hola3 Prueba2 en la posición de arriba por ejemplo, al dar doble clic como para actualizar datos, se me rellenan solo los datos    111 2222 333 en vez de aaa hola3 Prueba2, ese problemita tengo, si se puede bloquear para que no se mueva los datos de todas las filas.

    Punto 2: Tengo el botón eliminar con un mensaje de SI/NO para eliminar, cuando le doy en NO se me elimina el dato

    Punto 3: Tengo este problema al eliminar, en el Punto 2 solo me elimina los datos que aún no están relacionados.

    -Quisiera eliminar los datos del usuario que ya están relacionados, de la tabla usuario tengo ese error que sale abajo.....creo que es eliminar en cascada algo así, el mismo problema llegaría a tener con la table roles


    Saludos

    domingo, 25 de julio de 2021 20:44
  • Hola

    Para el Punto 1: Modifiqué el código del método ListarUsuarios() para que ponga no visible los row headers (encabezadores de fila) y la propiedad de ordenación de las cabeceras de columna a NotSortable. Con esto ya está el Punto 1.

            private void ListarUsuarios()
            {
                N_ListarUsuario listar = new N_ListarUsuario();
                tabla = listar.ListarProductos();
                dataGridView1.DataSource = tabla;
    
                foreach (DataGridViewColumn columna in dataGridView1.Columns)
                    columna.SortMode = DataGridViewColumnSortMode.NotSortable;
                dataGridView1.RowHeadersVisible = false;
    
                dataGridView1.Columns[0].Visible = false;
                dataGridView1.Columns[2].Visible = false;
                dataGridView1.Columns[3].Visible = false;
                dataGridView1.Columns[4].Visible = false;
            }

    Punto 2: Si vos comenzas un método invocando al método EliminarUsuario() (aunque sea en un if que le pregunta si devuelve true, que LO HARÁ si tiene éxito, que lo normal es que lo tenga), te va a eliminar el registro, por más que después le preguntes o le pidas confirmación. Me tomo el atrevimiento de dejarte como ejercicio (recordá, lo importante, y lo que vos queres, es aprender) editar ese método del evento Click del Button Eliminar, pero primero tenes que preguntarle con MessageBox si quiere eliminar el usuario, le podes pasar nombre o algún dato en el MessageBox para que se sepa qué cosa se va a eliminar, y si te responde que sí, recién ahí invocas al método EliminarUsuario(), y, opcionalmente comprobas si retorna true o no, que es muy raro que falle pero lo podes verificar. 

    Punto 3: Acá te explica como eliminar en cascada. Tene cuidado, que si eliminas una fila de una tabla padre o detalle, todas las filas hijas de una o más tablas se eliminarán. Pero quizás esto es lo que queres.

    Eliminar en cascada en T SQL

    Saludos

    domingo, 25 de julio de 2021 22:10
  • Hola

    Punto 1: Tengo un error, sin eso cumple con lo del ordenamiento. (SOLUCIONADO).

    dataGridView1.RowHeadersVisible = false;

    Los encabezados de fila no se pueden hacer invisibles porque el control DataGridView incluye los encabezados de fila en su modo ajuste automático

    Punto 2: Lo solucioné de esta manera

    Punto 3: Ya estaba viendo ese link, no le entiendo muy bien pero sigo analizando-

    Punto 4: En vista de que se bloqueo lo del punto 1, al hacer doble clic en los encabezados me sale este error.

    Me recomiendas evitar ese error en un try catch?

    Saludos

    domingo, 25 de julio de 2021 23:48
  • Hola

    Punto 1:

    Los encabezados de fila no se pueden hacer invisibles porque el control DataGridView incluye los encabezados de fila en su modo ajuste automático

    Entonces yo le sacaría el modo ajuste automático. ¿Es necesario?

    Punto 2:

    ¿Vaciar TextBoxes de dónde? Y si no lo eliminas, no necesitas volver a listar los usuarios porque no hubo cambios en la BD. Pero bien, salvo estos comentarios.

    Punto 3:

    Cualquier cosa preguntame, pero creo que lo vas a entender bien vos leyendo.

    Punto 4:

    Poner un try catch no serviría de nada porque anularía que te pase los datos al Form de TabPage1, o sea quizás se suprime el error (se atrapa, se dice, la Exception), pero no sirve para nada el código. Esto ocurre (que e.RowIndex sea -1) porque no hay ninguna fila seleccionada, si no está o hay problemas con los row headers, hacele el doble click en la fila y funcionará. 

    lunes, 26 de julio de 2021 0:41
  • Hola

    Punto 1: Ni se que propiedad o como hice eso de automático, estaba probando varias propiedades del datagridview que algunas tal vez no las cambie y no vi algún gran cambio.

    Punto 2: Cierto quitaré esa linea.

    Punto 3: Gracias cualquier cosa te consulto, solo necesito al eliminar datos de usuario se elimine en la otra tabla y creo que es eso de cascada

    Punto 4: Preguntaba mas porque vi como usuario, debe haber usuarios que puede que hagan doble clic en los encabezados o por error llega a dar.

    lunes, 26 de julio de 2021 1:03
  • Punto 4:

    Podes hacer algo como:

    if (e.RowIndex == -1)
    {
        MessageBox.Show("Emboque mejor el mouse en la fila");
        return;
    }
    // acá sigue el método
    // ....

    ¡Qué raro que te haya saltado error al intentar hacer no visible los row headers! Yo lo hice que tengo tu applicación y salió todo bien, se muestra el DataGridView sin los row headers y sin ningún problema.

    lunes, 26 de julio de 2021 1:33
  • Hola

    Punto 3: No se si una buena práctica, en las propiedades de la relación de las tablas que son afectadas, cambie la propiedad REGLA DE ELIMINACION de SIN ACCION a CASCADA, con eso logré eliminar en ambas tablas.

    Es recomendable hacerlo de esta manera, igual para la actualización?

    lunes, 26 de julio de 2021 2:47
  • La actualización en cascada no se si haría alguna diferencia en tu caso, por como son las tablas. Pero no conozco con gran detalle el asunto de la actualización. Mañana lo estudio un poco y te contesto mejor. Acá en Argentina son las 0:21 a.m.

    Saludos

    lunes, 26 de julio de 2021 3:22
  • Recién termino de entender qué es el Update con Cascade, consiste en, por ejemplo, si le cambias el id en tb_usuario se cambia automáticamente al mismo valor la FK idUsuario en tb_login, conservando el vínculo.

    Aunque dudo que vayas a hacer ese cambio en algún momento en tu aplicación, porque es muy raro que un id se modifique, si lo que va a ocurrir te parece bien, entonces ponele Cascade al Update.

    Yo te decía que en tu caso creía que era indistinto, porque no imaginé que fuera a poder cambiarse un id de una tabla. 

    lunes, 26 de julio de 2021 22:33
  • Interesante, algo nuevo se aprendió.

    Lo dudo utilizar el update en cascade, entonces crees que con el delete en cascade sea recomendable utilizarlo? o hacerlo de otra manera para controlar la eliminiación, porque por lo que vi con delete cascade como está en la imagen veo que lo elimina automaticamente de la otra tabla.

    lunes, 26 de julio de 2021 23:06
  • Con lo grande que es C#, nunca paramos de aprender.

    Utilizar o no el Delete Cascade depende de tu gusto o preferencia, si lo usas y eliminas una fila detalle o padre, se eliminará la fila de la FK o hija, si esto es lo que queres, usalo, si no, no. Yo nunca lo usé y he programado aplicaciones con bases de datos. En tu caso, si lo usas, si eliminas una fila de tb_usuario se eliminará la fila de tb_login que id == idUsuario. Está en vos.

    lunes, 26 de julio de 2021 23:34
  • Hola

    Disculpa, esto no es sobre lo que te llegue a preguntar desde comienzo.

    Tengo este problema, no se si estoy llamándolo bien o hice mi método bien.

    Estoy haciendo un árbol binario, el quiero quiero hacer el preorden y me muestre en un textbox.

    Te dejo el link por si acaso.

    Boton...

    Clase

    Saludos

    viernes, 30 de julio de 2021 7:10
  • Hola

    Nunca use árbol binario, igual intenté ver el github para intentar razonarlo, pero me sale 404, parece que está mal el link.

    viernes, 30 de julio de 2021 21:43
  • Hola acabo de ver que está en privado, veré como colocarlo en público
    viernes, 30 de julio de 2021 21:44
  • Aquí te lo dejo: https://drive.google.com/file/d/1hYAPlyVJEGBhIeGcIEa_4KhsrSAcbTU0/view?usp=sharing

    Ya está en publico.... link

    • Editado KaLuKaLuSoft viernes, 30 de julio de 2021 21:51 Actualización Link
    viernes, 30 de julio de 2021 21:47
  • ¿Me explicas cómo debería funcionar esto y qué resultados esperas obtener? 
    viernes, 30 de julio de 2021 23:27
  • Por ejemplo así tiene que hacer el pre orden, logré hacer el insertar, buscar y eliminar, quisiera hacer el pre orden, in orden y post orden, estos 3 ya tengo los métodos pero me falta es el evento del botón nose si estoy llamando bien por ejemplo al de pre orden.

    viernes, 30 de julio de 2021 23:36
  • Hola KaLu

    ¿Esto es una tarea académica? Si lo es, lo único que puedo hacer es intentar explicarte cómo hacer el código, y prometo que haré lo mejor que pueda para que lo resuelvas.

    sábado, 31 de julio de 2021 13:21
  • Hola Tigre Pablito

    Esto no es una tarea académica, la materia que estaba llevando estructura de datos se terminó este miércoles que pasó, lamentablemente nos enseñaron en puro java, estoy tratando de pasarlo a c#.

    Saludos

    sábado, 31 de julio de 2021 14:45
  • Hola

    Modifiqué el método Insertar() porque estaba un poco confuso, y recursivo, la verdad no se si tendría algún error aunque a simple vista funcionaba bien para crear el árbol, pero no asignaba a los nodos el padre, lo cual me di cuenta cuando fui a escribir el método preOrden(), que lo necesita para recorrer el árbol. Muy usualmente a mi me gusta más lo iterativo que lo recursivo. De hecho, el método preOrden() que hice es iterativo, de hecho no pude hacerlo recursivo, quizás si intentara más podría, no se.

            public void Insertar(int dato)
            {
                Nodo_Arbol nodo = Raiz;
                Nodo_Arbol padre = null;
                // Si no hay Raiz, la asignamos y terminamos el método con return
                if (Raiz == null) 
                {
                    Raiz = new Nodo_Arbol(dato, null, null, null);
                    return; 
                }
                // Recorremos el árbol en el orden en que iría el nuevo dato hasta encontrar el nodo terminal
                // donde iría el nuevo dato, guardando siempre una referencia al nodo que será el padre. En tu
                // método Insertar que estoy reemplazando, no les asignabas el Padre a los nodos, por lo que
                // hasta que me di cuenta de eso y lo corregí, no me funcionaba el método preOrden()
                while (nodo != null) 
                {
                    padre = nodo;
                    if (nodo.info > dato)
                        nodo = nodo.Izquierdo;
                    else if (nodo.info < dato)
                        nodo = nodo.Derecho;
                }
                // Al llegar al nodo terminal, el padre del nodo a insertar, asignamos el nuevo nodo, sin
                // olvidarnos de asignar el Padre, que será necesario en el código del método preOrden
                if (padre.info > dato)
                    padre.Izquierdo = new Nodo_Arbol(dato, null, null, padre);
                else if (padre.info < dato)
                    padre.Derecho = new Nodo_Arbol(dato, null, null, padre);
                else 
                    MessageBox.Show("No se puede ingresar un dato repetido.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

    El método preOrden simplemente agrega a un List<int> (que luego lo va a retornar para mostrar los valores de los nodos en el TextBox), primero la Raiz, y luego asigna a una variable de tipo Nodo_Arbol la raíz para recorrer en esa variable todos los nodos, primero de la izquierda, y luego de la derecha, en orden ascendente, en el método PreOrden(), que recibe como parámetro el nodo y el List<int> datos en el que ira colocando los datos de los nodos. En éste, primero agrega a datos el nodo mismo inicial, y luego recorre todo el sub-árbol en un while infinito verificando si el nodo izquierdo es null, si no, si el nodo derecho es null, recorriendo todas las ramas, si ambos son null regresa al nodo padre y vuelve a verificar si hay algún nodo sea en la derecha o la izquierda, en la comprobación también verifica que el dato (nodo.info) no esté ya en la colección datos, para no repetir. Cuando ya no hay más ramas que recorrer y vuelve a través de la propiedad Padre y llega a la raíz, se termina el método PreOrden().

            public List<int> preOrden()
            {
                Nodo_Arbol nodo = Raiz;
                List<int> datos = new List<int>();
                if (Raiz != null)
                {
                    datos.Add(Raiz.info);
                    nodo = Raiz;
                    PreOrden(nodo.Izquierdo, datos);
                    nodo = Raiz;
                    PreOrden(nodo.Derecho, datos);
                }
                return datos;
            }
            public void PreOrden(Nodo_Arbol nodo, List<int> datos)
            {
                if (nodo != null)
                    datos.Add(nodo.info);
                else
                    return;
                while (true)
                {
                    if (nodo.Izquierdo != null && !datos.Contains(nodo.Izquierdo.info))
                    {
                        datos.Add(nodo.Izquierdo.info);
                        nodo = nodo.Izquierdo;
                    }
                    else if (nodo.Derecho != null && !datos.Contains(nodo.Derecho.info))
                    {
                        datos.Add(nodo.Derecho.info);
                        nodo = nodo.Derecho;
                    }
                    else if (nodo != Raiz)
                    {
                        nodo = nodo.Padre;
                        if (nodo == Raiz)
                            return;
                    }
                    else
                    {
                        return;
                    }
                }
            }

    No se lo que es in orden, y post orden supongo que es al revés de pre orden, así que le cambias < por >, > por <, y Derecho por Izquierdo. Por favor intenta entenderlo, que no sea sólo un Copy/Paste, que se que aunque seas un/a programador/a más joven, tenes capacidad para razonar, y cualquier duda con respecto al funcionamiento, me la planteas y yo te respondo.

    Saludos


    sábado, 31 de julio de 2021 23:32
  • Hola Tigre Pablito

    Estoy modificando la parte visual, ya no estoy haciendo con mdi los formularios, me limitaba unas cosas, ahorita estoy pasándolo con panel, en el cual en un panel lateral están los botones que llaman a un formulario y este se muestre en otro panel,

    Estoy teniendo un problemita, me cree otro formulario para hacer una prueba, por ejemplo tengo ese formulario prueba de tamaño por defecto maximizado, al correr el programa lo voy achicando y agrandando con el mouse en los bordes, quiero cambiar el color del boton cuando llego a width <= 500, pero no me sale, creo que estoy haciendo algo mal?

    La idea hacerlo tipo responsivo, si el formulario es menor a 500, modifico las posiciones de los botones, textbox o lo que tenga y cuando sea mayor y entra todo como cuando está maximizado dejarlo tal como si estuviera maximizado, sino usar otro if para que el width sea en otra posicion y modificar las posiciones.

    Espero haber explicado bien.

    No me dejó colocar ya más imagenes para que puedas ver como se queda cuando hago el formulario más pequeño.

    Se puede limitar width y el height para cuando llegue al limite de tamaño permitido el usuario no pueda hacerlo más pequeño?

    lunes, 9 de agosto de 2021 18:41
  • Hola Kalu

    Debe haber un evento que se genera cuando redimensionas el Form, se debería llamar Resized o FormResized, nunca lo usé porque yo los hago que no se puedan cambiar de tamaño, para esto pongo el borde (creo que es BorderStyle) del Form a Fixed3D o FixedDialog. En ese evento podes poner que si Width es menor que 500 entonces que se asigne a Width 500 y manipular todo lo que quieras. Yo lo que haría es al Form no redimensionable así no tengo problemas.

    lunes, 9 de agosto de 2021 20:34
  • Si queres podes hacer preguntas nuevas, así no se mezclan tanto los temas. Yo las veo las preguntas, las que se responder las respondo y las que no, no. Puede darse, y seguro que se dará, que preguntas que yo no se, otros si las sepan, de hecho hay unos cuantos en el Foro que tienen muchos más conocimientos que yo; otras cosas no se si tienen, como por ejemplo tomarse un rato para escribir ejemplos de código para un programador más joven.
    lunes, 9 de agosto de 2021 20:42
  • Hola Tigre Pablito.

    Lo que había preguntado ya encontré otra manera de hacerlo, te pregunté ya que a ti te e llegado a entender bien o tal vez era algo facíl lo que pregunté que te dejaste entender.

    Estoy practicando ahorita con insertar imagenes a sql server con tipo de dato image y datatime, logré insertar todo bien.

    Donde batallé o complicado fue que al dar clic en alguna celda en un datagridview se me muestre la imagen guardada en un picturebox, vi varios tutoriales, los probaba pero no me funcionaban, hasta que encontré uno y le entendí para modificarlo un poco me funcionó.

    Ahorita tengo un problemita que estoy que no encuentro el error, es para actualizar, tengo que mencionar que todos los métodos de insertar, actualizar y eliminar los estoy utilizando igual como me enseñaste al comienzo solo modifique, cambie unos datos y agregue solo el tipo de datos image y un datetime en el sql server.

    Mi problema es: Que al actualizar me salta al mensaje ----- Se produjo un error: No actualizado

    Para ver porque no me actualiza lo hice correr el programa paso a paso, me di cuenta que no me está tomando el id, el id me lo coloca en 0, pero si me está tomando los demás datos, como nombre, dirección,telefono y fechacumple, qué crees que hice mal o qué copié mal?

    En resumen, no me actualiza porque no me está tomando el número de id de la base de datos.

    Te muestro todo:

    public class cls_Actualizar
        {
            public bool ActualizarUsuario(cls_LogicaInsertar objl, PictureBox imagen)
            {
                SqlConnection cn = cls_Conexion.ObtenerConexion();
                SqlCommand cmd = new SqlCommand("actualizardatos", cn);
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@id", objl.id);
                cmd.Parameters.AddWithValue("@nombre", objl.nombre);
                cmd.Parameters.AddWithValue("@direccion", objl.direccion);
                cmd.Parameters.AddWithValue("@telefono", objl.telefono);
                cmd.Parameters.AddWithValue("@fechaCumple", objl.fechaCumple);
                MemoryStream ms = new MemoryStream();
                imagen.Image.Save(ms, ImageFormat.Bmp);
                cmd.Parameters.AddWithValue("@imagen", ms.GetBuffer());
                if (cmd.ExecuteNonQuery() == 1)
                {
                    cn.Close();
                    return true;
                }
                else
                {
                    cn.Close();
                    return false;
                }
            }
        }

    ===============================

     public class cls_LogicaInsertar //El nombre lo ignoras lo utilizo hasta para actualizar y eliminar
        {
            public int id { get; set; }
            public string nombre { get; set; }
            public string direccion { get; set; }
            public int telefono { get; set; }
            public DateTime fechaCumple { get; set; }
            public byte[] imagen { get; set; }
        }

    =============================

    private void btn_Actualizar_Click(object sender, EventArgs e)
            {
                try
                {
                    objL.id = id;
                    objL.nombre = textBox1.Text;
                    objL.direccion = textBox2.Text;
                    objL.telefono = Convert.ToInt32(textBox3.Text);
                    objL.fechaCumple = Convert.ToDateTime(dateTimePicker1.Text);
                    if (objA.ActualizarUsuario(objL, pictureBox1) == true)
                    {
                        MessageBox.Show("Actualizado Correctamente");
                        //VaciarTextBoxes();
                        //ListarUsuarios();
                    }
                    else
                    {
                        MessageBox.Show("Se produjo un error: No actualizado");
                        //VaciarTextBoxes();
                        //ListarUsuarios();
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("No hay datos para actualzar", "Atención!!!", MessageBoxButtons.OK, MessageBoxIcon.Information);
                }
            }

    ======Procedimiento almacenado en sql server

    ALTER proc [dbo].[actualizardatos]
    @id int,
    @nombre varchar(50),
    @direccion varchar(50),
    @telefono int,
    @fechaCumple date,
    @imagen image
    as
    begin
    update tb_imagen set nombre=@nombre,direccion=@direccion,telefono=@telefono,fechaCumple=@fechaCumple,imagen=@imagen
    where id = @id
    end

    Saludos


    viernes, 20 de agosto de 2021 5:59
  • Hola Kalu

    En btn_Actualizar_Click() 

     objL.id = id;

    esa variable id ¿de dónde viene?

    Me parece que al cargar el formulario para actualizar cuando llenas los TextBox con los datos (un registro) que traes con el SELECT (o como se llame el SP), deberías guardar el id del registro en esa variable id, asi luego la tenes, y no queda en 0 por omisión, que es lo que creo que ocurrió. Obviamente que en el SELECT tenes que traer el id junto con los otros datos, para poder pasarlo. 

    Además, me imagino, cuando hiciste el "paso a paso", seguramente nunca te encontraste con una asignación de esa variable id que le asignas a objL.id, por lo cual es obvio que va a tener su valor por omisión 0.

    viernes, 20 de agosto de 2021 16:00
  • Hola.

    objLid = id;

    private int id; de ahí viene la variable id, tal como me ayudaste anteriormente así lo tengo y me funciona en el anterior práctica, pero en esta no me está funcionando.

    viernes, 20 de agosto de 2021 16:05
  • Si, pero en la declaración de la propiedad (public int id { get; set; }) no se le asigna un valor. Ese es el problema, además la variable id (que no se de donde sale, pero podría estar bien si se le asigna lo que corresponde) que le asignas a objL.id no es la misma que el miembro (o propiedad de la clase cls_LogicaInsertar) que es justamente objL.id, y aunque lo fuese, le estarías asignando su propio valor, que es 0 por omisión, pero no lo es. Lo que hiciste en el programa anterior fue, al seleccionar un registro u objeto individual desde un DataGridView, llevar, junto con todos los datos para modificar en un formulario, el id en una variable, que lo obtenes de el propio objeto que seleccionaste y vas a modificar. En este caso actual, no se de donde seleccionas el objeto a modificar, pero seguro lo que te falta es, cuando llenas los TextBox con los datos actuales del objeto, mandar en una variable (porque no va el id a un TextBox para modificarse) el id, y ese id lo usas para pasarselo, en definitiva, al SP, pasando por el objeto objl y su propiedad id. Para esto, al seleccionar los datos de la BD, tenes que incluir el campo id, para tenerlo a mano.
    viernes, 20 de agosto de 2021 17:06
  • hola

    Todos los datos los estoy guardando en el datagridview, para actualizar hago clic en la grilla para que se rellenen los textbox,el datepicker y el picturebox.

    Estoy tratando de entender y viendo cual es el fallo-

    Estoy haciendo lo mismo que en el anterior programa excepto esta parte:

    PROGRAMA ANTERIOR:

    private void dataGridView1_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
            {
                tabControl1.SelectedTab = tabPage1;
                btn_Guardar.Text = "Actualizar";
                txt_Nombre.Text = tabla.Rows[e.RowIndex].Field<string>(2);
                txt_ApellidoPaterno.Text = tabla.Rows[e.RowIndex].Field<string>(3);
                txt_ApellidoMaterno.Text = tabla.Rows[e.RowIndex].Field<string>(4);
                txt_Telefono.Text = tabla.Rows[e.RowIndex].Field<int>(5).ToString();
                txt_Celular.Text = tabla.Rows[e.RowIndex].Field<int>(6).ToString();
                txt_Correo.Text = tabla.Rows[e.RowIndex].Field<string>(7);
                txt_Comentario.Text = tabla.Rows[e.RowIndex].Field<string>(8);
                id = tabla.Rows[e.RowIndex].Field<int>(0);
            }

    NUEVO PROGRAMA

    private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
            {
                try
                {
                    textBox1.Text = dataGridView1.CurrentRow.Cells[1].Value.ToString();
                    textBox2.Text = dataGridView1.CurrentRow.Cells[2].Value.ToString();
                    textBox3.Text = dataGridView1.CurrentRow.Cells[3].Value.ToString();
                    //Muesta la fecha guardada en la base de datos en un dateTimePicker 
                    dateTimePicker1.Value = (DateTime)dataGridView1.CurrentRow.Cells[4].Value;
                    //Muesta la imagen guardada en la base de datos en un PicktureBox 
                    byte[] image = Encoding.ASCII.GetBytes(dataGridView1.CurrentRow.Cells[5].Value.ToString());
                    if (image == null)
                    {
                        pictureBox1.Image = null;
                    }
                    else
                    {
                        var data = (Byte[])(dataGridView1.CurrentRow.Cells[5].Value);
                        var stream = new MemoryStream(data);
                        pictureBox1.Image = Image.FromStream(stream);
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }

    Otra pregunta, porque al hacer copiar y pegar, en este nuevo programa tengo ese error del id? solo agregue unos tipos de datos en el sql y lo demas en el código es igual, quite los datos que no necesito del anterior programa y solo deje los que utilizo y agrege el datetime con el picturebox.

    De todos modos te dejo el link de ambos: Programa Anterior y Programa Actual

    sábado, 21 de agosto de 2021 18:42
  • Hola

    Fijate la última linea del código que pasaste del programa anterior y decime en donde está esa linea en el código del nuevo programa. Te adelanto, por las dudas, la variable id debe ser miembro de clase, para que persista a través de los métodos. Otra cosa, me parecería mejor que obtengas los datos del DataSource (un DataTable o un List<T>), no directamente del DataGridView. Lo del id, como te venía diciendo, es lo que podes ver en el código anterior y no en el actual. Yo se que soy latoso pero hay que leer bien para darse cuenta de las cosas. O sea si me lees lo que escribo lo vas a resolver, además en este caso es casi nada el código que falta. Quiero creer que el campo id está en el SELECT.

    sábado, 21 de agosto de 2021 20:01
  • hola

    Ya logre solucionarlo, me había faltado el ListarUsuario del anterior programa copiar. Me quedó de esta manera.

    private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
            {
                if(e.RowIndex == -1)
                {
                    MessageBox.Show("Emboque mejor el mouse en la fila");
                    return;
                }
                else
                {
                    textBox1.Text = tabla.Rows[e.RowIndex].Field<string>(1);
                    textBox2.Text = tabla.Rows[e.RowIndex].Field<string>(2);
                    textBox3.Text = tabla.Rows[e.RowIndex].Field<int>(3).ToString();
                    dateTimePicker1.Value = tabla.Rows[e.RowIndex].Field<DateTime>(4);
                    byte[] image = Encoding.ASCII.GetBytes(dataGridView1.CurrentRow.Cells[5].Value.ToString());

                    if (image == null)
                    {
                        pictureBox1.Image = null;
                    }
                    else
                    {
                        var data = (Byte[])(dataGridView1.CurrentRow.Cells[5].Value);
                        var stream = new MemoryStream(data);
                        pictureBox1.Image = Image.FromStream(stream);
                    }
                    id = tabla.Rows[e.RowIndex].Field<int>(0);
                    ListarUsuarios();
                }
            }

    Una pregunta en esta parte, cómo puedo hacerlo para tenerlo con tabla.rows en vez de dagaGridView.CurrentRow.....? con ese código paso la imagen guardada como image a un picturebox

    byte[] image = Encoding.ASCII.GetBytes(dataGridView1.CurrentRow.Cells[5].Value.ToString());

                    if (image == null)
                    {
                        pictureBox1.Image = null;
                    }
                    else
                    {
                        var data = (Byte[])(dataGridView1.CurrentRow.Cells[5].Value);
                        var stream = new MemoryStream(data);
                        pictureBox1.Image = Image.FromStream(stream);
                    }

    martes, 24 de agosto de 2021 20:33
  • Hola

    La linea de ListarUsuarios no es necesaria, porque se supone que ya están cargados los registros. Lo único que hacía falta era la linea donde le asignas a la variable id, para tenerla a mano en el update.

    byte[] image = Encoding.ASCII.GetBytes(dataGridView1.CurrentRow.Cells[5].Value.ToString());

    Esta linea está mal. Aunque te de distinto de null. No hay que convertir a string el valor byte[] de la imagen y el método GetBytes() tampoco es para eso.

    Lo correcto sería:

    var data = (byte[])tabla[e.RowIndex][5];

    var stream = MemoryStream(data);

    pictureBox1.Image = Image.FromStream(stream);

    // si es null también lo coloca en el PictureBox directamente

    Saludos

    miércoles, 25 de agosto de 2021 0:32
  • Hola

    Esta parte era la que me faltaba para el id.

               

    private void ListarUsuarios()
            {
                cls_listar listar = new cls_listar();
                 tabla = listar.ListarUsuarios();
                dataGridView1.DataSource = tabla;
            }

    Sobre lo otro espero haberlo colocado en el lugar correcto pero aún así me salta un par de errores

    private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
            {
                if(e.RowIndex == -1)
                {
                    MessageBox.Show("Emboque mejor el mouse en la fila");
                    return;
                }
                else
                {
                    textBox1.Text = tabla.Rows[e.RowIndex].Field<string>(1);
                    textBox2.Text = tabla.Rows[e.RowIndex].Field<string>(2);
                    textBox3.Text = tabla.Rows[e.RowIndex].Field<int>(3).ToString();
                    dateTimePicker1.Value = tabla.Rows[e.RowIndex].Field<DateTime>(4);
                    //byte[] image = Encoding.ASCII.GetBytes(dataGridView1.CurrentRow.Cells[5].Value.ToString());

                    //if (image == null)
                    //{
                    //    pictureBox1.Image = null;
                    //}
                    //else
                    //{
                        var data = (byte[])tabla[e.RowIndex][5];
                        var stream = MemoryStream(data);
                        pictureBox1.Image = Image.FromStream(stream);
                        //var data = (Byte[])(dataGridView1.CurrentRow.Cells[5].Value);
                        //var stream = new MemoryStream(data);
                        //pictureBox1.Image = Image.FromStream(stream);
                    //}
                    id = tabla.Rows[e.RowIndex].Field<int>(0);
                    ListarUsuarios();
                }
            }

    Error 1: (byte[])tabla[e.RowIndex] -----> No se puede aplicar la indización con [] a una expresión tipo DataTable

    Error 2: MemoryStream -----> No se puede usar como método el miembro MemoryStream no invocable.

    Saludos

    jueves, 26 de agosto de 2021 5:08
  • Hola

    No es necesario poner el método ListarUsuarios() luego de id = ... , porque ya están cargados los datos, en todo caso después de hacer el Update si, para que se reflejen los cambios.

    El código que te pasé me equivoqué, y creo que quizás te podrías haber dado cuenta en qué, pero bueno, aquí está:

    var data = (byte[])tabla.Rows[e.RowIndex][5];  // o tabla.Rows[e.RowIndex].Field<Byte[]>(5)

    var stream = new MemoryStream(data);

    pictureBox1.Image = Image.FromStream(stream);

    Saludos

    • Marcado como respuesta KaLuKaLuSoft viernes, 3 de septiembre de 2021 1:54
    viernes, 27 de agosto de 2021 13:09
  • Hola de nuevo, tengo una consulta.

    Tengo 3 formularios:

    1er formulario principal que contiene 2 panel, un panel es para los menus y submenu, y el otro panel es contenedor, donde al hacer clic en un submenu se me muestra el formulario en el panelContenedor.

    2do recibe los datos de un datagridview, por ejemplo idProveedor y razon_social

    3er se encuentra el datagridview donde al hacer clic en una grilla o doble click se rellene los textbox en el formulario 2.

    Estoy haciendolo de esta manera manera.

                private void dataListado_CellContentClick(object sender, DataGridViewCellEventArgs e)
            {
                FrmIngreso form = new FrmIngreso();
                form.txtIdproveedor.Text = this.dataListado.Rows[e.RowIndex].Cells["idproveedor"].Value.ToString();
                form.txtProveedor.Text = this.dataListado.Rows[e.RowIndex].Cells["razon_social"].Value.ToString();
            }

    Cúal sería mi error al mandar los datos al otro formulario? 

    Editado: El código de arriba me agarra los datos en los textbox, pero no me los muestra en el otro formulario.

    Intenté enviar desde el formulario 3 al formulario 2 y no da, pensé que era problema en el formulario 1 ya que utilizo un panelContenedor donde se me abren todos los formularios es parecido a un MDIParent

    Saludos


    • Editado KaLuKaLuSoft viernes, 3 de septiembre de 2021 2:35
    viernes, 3 de septiembre de 2021 2:05
  • Hola

    Te falta el ShowDialog() en el código. 

    Para que un Form hijo pueda acceder a miembros públicos de un Form padre, segui los siguientes pasos:

    1) Declarar miembros de clase públicos en el padre, en tu caso pueden ser 2 string para los 2 datos. 

    2) Al invocar ShowDialog() del hijo en el padre, pasarle this como referencia al padre

    form.ShowDialog(this);

    3) En donde se necesite, en el hijo:

    FormPadre owner = (FormPadre)Owner; // Owner es el padre de este Form

    // y por owner se accede a los datos públicos, por ejemplo:

    string str = owner.RazonSocial;

    owner.idProveedor = 5;

    Saludos

    Pablo

    EDITO: en tu caso el 2do Form es hijo del 3ro
    viernes, 3 de septiembre de 2021 3:07
  • Hola

    En el punto 3, te refieres hacerlo de esta manera?

    try
                {
                    FrmIngreso form = new FrmIngreso();
                    foreach (Form frm in Application.OpenForms)
                    {
                        if (frm.Name == "FrmIngreso")
                        {
                            form = (FrmIngreso)frm;
                            form.txtIdproveedor.Text = this.dataListado.CurrentRow.Cells["idproveedor"].ToString();

                            form.txtProveedor.Text = this.dataListado.CurrentRow.Cells["razon_social"].ToString();
                            this.Close();
                            break;
                        }
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }


    • Editado KaLuKaLuSoft viernes, 3 de septiembre de 2021 3:19
    viernes, 3 de septiembre de 2021 3:18
  • ehh a ver mejor te digo con nombre de los formularios así no entiendo bien,

    de FrmVistaProveedor_Ingreso mando datos a frmIngreso, entonces frmIngreso es hijo de FrmVistaProveedor_Ingreso?

    tiene que ser al revés?

    viernes, 3 de septiembre de 2021 3:25
  • Lo solucione, me costó entenderte esta vez pero por lo que vi me atrae los datos así:

    private void dataListado_CellClick(object sender, DataGridViewCellEventArgs e)
            {
                FrmIngreso form = new FrmIngreso();
                foreach(Form frm in Application.OpenForms)
                {
                    if(frm.Name == "FrmIngreso")
                    {
                        form = (FrmIngreso)frm;
                        form.txtIdproveedor.Text = this.dataListado.Rows[e.RowIndex].Cells["idproveedor"].Value.ToString();
                        form.txtProveedor.Text = this.dataListado.Rows[e.RowIndex].Cells["razon_social"].Value.ToString();
                        this.Close();
                        break;
                    }
                }
            }

    viernes, 3 de septiembre de 2021 3:47
  • Hola

    Otra consulta, cómo podría pasar una fecha de una grilla a un datepicker con esa forma...

    form.dtFecha_Vencimiento.Value = this.dataListado.Rows[e.RowIndex].Cells["fecha_vencimiento"].Value.ToString();

    Lo de negrita me sale error

    viernes, 3 de septiembre de 2021 6:23
  • Hola KaLu

    Disculpa, pero entendiste mal lo de pasar datos del Form padre al hijo. Los controles tienen que ser private, ponerlos public es muy mala práctica. Lo que yo te decía, que te lo comento de nuevo, es:

    1) Agregar (en tu caso) 2 variables string, public (por ejemplo idProv, y prov), al Form padre (FrmVistaProveedor_Ingreso), y al seleccionar la fila (el evento, que yo lo haría como DoubleClick para diferenciarlo de un simple Click, pero eso es como vos prefieras) guardar los valores en esos string (miembros de clase y public { get; set; }), luego

    2) Al mostrar el Form hijo (FrmIngreso), pasarle this a ShowDialog(). this es una autoreferencia al Form padre, que es quien crea y muestra al Form hijo.

    FrmIngreso form = new FrmIngreso();

    form.ShowDialog(this);  // le pasas this que es referencia del padre que luego lo contendrá Owner del hijo

    3) En cualquier método de FrmIngreso en que haya que acceder a los datos public (en este caso, idProv y prov) de FrmVistaProveedor_Ingreso, creamos una referencia al padre, a través de la propiedad Owner del hijo, casteando esta al tipo del padre.

    FrmVistaProveedor_Ingreso padre = (FrmVistaProveedor_Ingreso)Owner;

    y así leemos (o escribimos) en los miembros public idProv y prov del Form padre:

    txtIdproveedor.Text = padre.idProv;

    txtProveedor.Text = padre.prov;

    De la misma forma podes crear datos public en cualquier Form padre y leerlos en el Form hijo. Pero no hay que poner public a los controles ni crear nuevas instancias de los formularios. 

    La última pregunta, la verdad lo usé una vez y no recuerdo bien, pero creo que debería ser:

    dtFecha_Vencimiento.Value = padre.fecha;

    Que justamente, fecha, debería ser otro dato public de FrmVistaProveedor_Ingreso, igual que idProv y prov, pero de tipo DateTime. 

    En el método del evento del DataGridView, entonces, deberías agregar estas 3 lineas:

    idProv = dataListado.Rows[e.RowIndex].Cells["idproveedor"].Value.ToString();

    prov = dataListado.Rows[e.RowIndex].Cells["razon_social"].Value.ToString();

    fecha = (DateTime)dataListado.Rows[e.RowIndex].Cells["fecha_vencimiento"].Value;  // ToString() en este caso no va porque es una fecha, entonces lo casteas a DateTime para que te lo tome el DateTimePicker

    viernes, 3 de septiembre de 2021 17:37
  • Hola Tigre Pablito

    Estaba viendo que lo que estaba haciendo programación en capas, porque todas las capas las mencionaba en capa presentación, al parecer esta mal, vi en internet uno en capas diferente en la capa negocio, ese mismo que vi tiene muchas líneas de código, estoy intentando hacer ese mismo con menos líneas de código como me enseñaste al comienzo.

    CAPA DATOS

    public class DSucursal
        {
            public int id { get; set; }
            public string Sucursal { get; set; }
            public string Ubicacion { get; set; }
            public int TelefonoSucursal { get; set; }
            public bool EstadoSucursal { get; set; }

            public DSucursal()
            {

            }
            public DSucursal(string Sucursal, string Ubicacion, int TelefonoSucursal, bool EstadoSucursal)
            {
                this.Sucursal = Sucursal;
                this.Ubicacion = Ubicacion;
                this.TelefonoSucursal = TelefonoSucursal;
                this.EstadoSucursal = EstadoSucursal;
            }
            SqlConnection SqlCon = new SqlConnection();
            public bool InsertarSucursal(DSucursal objd)
            {
                SqlCon.ConnectionString = DConexion.Cn;
                SqlCon.Open();
                SqlCommand cmd = new SqlCommand();
                cmd.Connection = SqlCon;
                cmd.CommandText = "sp_insertarsucursal";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@Sucursal", objd.Sucursal);
                cmd.Parameters.AddWithValue("@Ubicacion", objd.Ubicacion);
                cmd.Parameters.AddWithValue("@TelefonoSucursal", objd.TelefonoSucursal);
                cmd.Parameters.AddWithValue("@EstadoSucursal", objd.EstadoSucursal);
                if (cmd.ExecuteNonQuery() == 1)
                {
                    if (SqlCon.State == ConnectionState.Open) SqlCon.Close();
                    return true;
                }
                else
                {
                    if (SqlCon.State == ConnectionState.Open) SqlCon.Close();
                    return false;
                }
            }
            public DataTable MostrarSucursal()
            {           
                try
                {
                    //SqlCon.ConnectionString = DConexion.Cn;         
                    SqlCommand cmd = new SqlCommand("sp_listarsucursal", SqlCon);
                    SqlDataReader LeerFilas;
                    DataTable Tabla = new DataTable();
                    cmd.CommandType = CommandType.StoredProcedure;
                    LeerFilas = cmd.ExecuteReader();
                    Tabla.Load(LeerFilas);
                    LeerFilas.Close();
                    return Tabla;
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    if (SqlCon.State == ConnectionState.Open) SqlCon.Close();
                }  
            }
        }

    CAPA NEGOCIO

    public static bool InsertarSucursal(string Sucursal, string Ubicacion, int TelefonoSucursal, bool EstadoSucursal)
            {
                DSucursal objd = new DSucursal();
                objd.Sucursal = Sucursal;
                objd.Ubicacion = Ubicacion;
                objd.TelefonoSucursal = TelefonoSucursal;
                objd.EstadoSucursal = EstadoSucursal;
                return objd.InsertarSucursal(objd);
            }

    public static DataTable Mostrar()
            {
                return new DSucursal().MostrarSucursal();
            }

    CAPA NEGOCIO

    private int id;
            private DataTable tabla;
             private void Mostrar()
             {
                tabla = NSucursal.Mostrar();
                this.dataGridView1.DataSource = tabla;
             }

    private void dataGridView1_CellContentDoubleClick(object sender, DataGridViewCellEventArgs e)
            {
                if (e.RowIndex == -1)
                {
                    MessageBox.Show("Emboque mejor el mouse en la fila");
                    return;
                }
                else
                {
                    id = tabla.Rows[e.RowIndex].Field<int>(0);
                    textBox1.Text = tabla.Rows[e.RowIndex].Field<string>(1);
                    textBox2.Text = tabla.Rows[e.RowIndex].Field<string>(2);
                    textBox3.Text = tabla.Rows[e.RowIndex].Field<int>(3).ToString();
                    if (checkBox1.Checked == true)
                    {
                        checkBox1.Checked = tabla.Rows[e.RowIndex].Field<bool>(4);
                    }
                    else
                    {
                        checkBox1.Checked = tabla.Rows[e.RowIndex].Field<bool>(4);
                    }
                    Mostrar();
                }
            }

    El problema que tengo de nuevo me sale tabla fue null se que ya tuve este problema antes lo corregí, pero ahora por más que busco, lo nuevo es em capa negocio:

    public static DataTable Mostrar()
            {
                return new DSucursal().MostrarSucursal();
            }

    y el mostrar en capa presentación es algo diferente al que estaba haciendo antes, que es este.

    private void ListarUsuarios()
            {
                N_ListarUsuario listar = new N_ListarUsuario();
                tabla = listar.ListarUsuarios();
                dg_usuarios.DataSource = tabla;
            }

    Revise varias veces y no le encuentro la solución, por vuelvo por tu ayuda.

    Saludos

    martes, 14 de septiembre de 2021 5:12
  • Hola Kalu

    Probá poner return Tabla; al final del método MostrarSucursal(). Si no, pasame el proyecto, así lo veo bien y trato de encontrar el error.

    Hay 2 métodos con el mismo nombre, Mostrar(), supongo que están en diferentes clases, no? 

    Saludos

    martes, 14 de septiembre de 2021 10:52
  • Hola te paso el proyecto y el original los métodos MOSTRAR() uno está en NSucursal que es en la CAPA NEGOCIO y el otro MOSTRAR() está en CAPA PRESENTACION dentro del formulario PSucursal.

    En el original no está la parte de sucursal, le estoy agregando de otro que vi, en realidad estoy armando de a poco uno a mi manera si con código más limpio mejor.

    Saludos

    martes, 14 de septiembre de 2021 20:57
  • Lo que me falta es la base de datos y me pareció que usas un DataSet diseñado, ¿puede ser? O sea, no puro ADO.Net. Entonces, ¿me podrías decir en que parte pones la cadena de conexión? porque me parece que puede estar difícil de encontrar. Para mi gusto los DataSet tipados (si es eso lo que usas) no son prácticos, me gusta usar puro ADO.Net, pero bueno, cada uno/a tiene sus preferencias y hay que respetar.

    Lo que decís "le estoy agregando de otro que vi", me suena como que estás buscando códigos por Internet para tu aplicación de WinForms, la verdad me parecería mucho mejor que trates de hacer tus propios códigos vos solo/a, así vas aprendiendo y avanzando más firmemente, y cualquier cosa me preguntas, pero si copias código de otros es más difícil aprender y lograr mayores metas, más, si no lo entendes ese código. 

    miércoles, 15 de septiembre de 2021 2:05
  • Hola.

    1.- La base de datos del de proyecto lo había colocado en el de original con el nombre de Ventas.sql.

    2.- Cómo DataSet diseñado? la cadena de conexion está en DConexion.Cn: 

    public DataTable MostrarSucursal()
            {           
                try
                {
                    SqlCon.ConnectionString = DConexion.Cn; ----> aquí coloco la cadena de conexion que llama a la clase Dconexion
                    SqlCon.Open();
                    SqlCommand cmd = new SqlCommand("sp_listarsucursal", SqlCon);
                    SqlDataReader LeerFilas;
                    DataTable Tabla = new DataTable();
                    cmd.CommandType = CommandType.StoredProcedure;
                    LeerFilas = cmd.ExecuteReader();
                    Tabla.Load(LeerFilas);
                    LeerFilas.Close();
                    return Tabla;
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    if (SqlCon.State == ConnectionState.Open) SqlCon.Close();
                }
            }

    3.- No es que estoy buscando código, más que todo voy en la base de datos, agregando cosas, como estaba practicando en uno de ventas que aún no lo subo a github pero lo estaba haciendo de la otra manera que creo que no es correcto en capas, en este proyecto que vi, en otro proyecto vi que había en su base de datos sucursal, todo era en entity framework y aún no estoy listo en hacer de esa manera.

    En resumen, solo me baso en la base de datos y trato de armar a mi manera, también programarlo en la manera que le entiendo, como me explicaste, ahorita lo nuevo sería la de la capa entidad, solo logré guardar pero para actualizar tengo ese error para rellenar los textbox al hacer clic en la grilla.

    Saludos

    miércoles, 15 de septiembre de 2021 22:25
  • Hola

    Antes que me olvide te quería preguntar si entendiste y pudiste hacer bien lo de pasar datos del Form padre al Form hijo, que no me dijiste nada. Y creo que tampoco me dijiste nada si anduvo bien lo del árbol binario que te pasé unos códigos. No te pido, como ya manifesté muchas veces en el Foro, que me marques los puntos (porque estoy para ayudar, no por los puntos), pero si me gustaría, si puede ser, que me digan si les sirvió de algo mi ayuda, sólo para quedarme contento.

    Con respecto a la aplicación VentasOficial, lo que yo haría primero es sacar todos los DataSet y TableAdapter hechos en diseño, porque (gracias a Dios) no los usas y además me daba un error que me tenía preocupado (que comenté esa linea que estaba al cohete), que entiendo que es porque no le agregaste la conexión, pero no importa, saca todo eso que no sirve para nada. También mencionaste Entity Framework, que si bien a muchos les gusta, yo por mi parte te recomendaría que sigas con ADO.Net puro, que me parece más fácil y sencillo de usar. (Además si vas a usar EF tengo serias dudas de poder ayudarte porque no lo conozco en detalle). 

    Bueno, aquí te paso los 2 archivos que modifiqué, con lo cual el programa funciona bien sin errores, lo que si falta es más desarrollo, por ejemplo, yo haría que si haces doble click en la planilla y se llenan los TextBox, que el Button muestre "Actualizar", y si haces click en él, en lugar de agregar un registro, haga el Update del que seleccionaste, que haya un Button "Reset" para volver a Insert, y un Button para eliminar una sucursal. Cambié en DSucursal.cs el evento CellContentDoubleClick por CellMouseDoubleClick, porque el primero requiere que se haga doble click pero sobre el texto de la celda, si no, no funciona (de ahí el "Content"). Saqué la marca de comentario de la cadena de conexión y abrí la conexión en el método MostrarSucursal() porque si no, no funcionaba, y puse el método Mostrar() en el método del evento Load, porque si no, no se llena la grilla. En PSucursal, comenté la linea this.sp_listarsucursalTableAdapter.Fill(this.ventasDataSet2.sp_listarsucursal);

    que no sirve para nada. Y así, funciona perfecto. Estas eran cosas que creo vos te podrías haber dado cuenta solo, si mirabas un poco y debuggeabas, porque sos un programador joven pero no tanto, pero bueno, estabas distraído seguramente.

    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.SqlClient;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace CapaDatos
    {
        public class DSucursal
        {
            public int id { get; set; }
            public string Sucursal { get; set; }
            public string Ubicacion { get; set; }
            public int TelefonoSucursal { get; set; }
            public bool EstadoSucursal { get; set; }
    
            public DSucursal()
            {
    
            }
            public DSucursal(string Sucursal, string Ubicacion, int TelefonoSucursal, bool EstadoSucursal)
            {
                this.Sucursal = Sucursal;
                this.Ubicacion = Ubicacion;
                this.TelefonoSucursal = TelefonoSucursal;
                this.EstadoSucursal = EstadoSucursal;
            }
            SqlConnection SqlCon = new SqlConnection();
            public bool InsertarSucursal(DSucursal objd)
            {
                SqlCon.ConnectionString = DConexion.Cn;
                SqlCon.Open();
                SqlCommand cmd = new SqlCommand();
                cmd.Connection = SqlCon;
                cmd.CommandText = "sp_insertarsucursal";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@Sucursal", objd.Sucursal);
                cmd.Parameters.AddWithValue("@Ubicacion", objd.Ubicacion);
                cmd.Parameters.AddWithValue("@TelefonoSucursal", objd.TelefonoSucursal);
                cmd.Parameters.AddWithValue("@EstadoSucursal", objd.EstadoSucursal);
                if (cmd.ExecuteNonQuery() == 1)
                {
                    if (SqlCon.State == ConnectionState.Open) SqlCon.Close();
                    return true;
                }
                else
                {
                    if (SqlCon.State == ConnectionState.Open) SqlCon.Close();
                    return false;
                }
            }
            public bool ActualizarSucursal(DSucursal objd)
            {
                SqlCon.ConnectionString = DConexion.Cn;
                SqlCon.Open();
                SqlCommand cmd = new SqlCommand();
                cmd.Connection = SqlCon;
                cmd.CommandText = "sp_actualizarsucursal";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@id", objd.id);
                cmd.Parameters.AddWithValue("@Sucursal", objd.Sucursal);
                cmd.Parameters.AddWithValue("@Ubicacion", objd.Ubicacion);
                cmd.Parameters.AddWithValue("@TelefonoSucursal", objd.TelefonoSucursal);
                cmd.Parameters.AddWithValue("@EstadoSucursal", objd.EstadoSucursal);
                if (cmd.ExecuteNonQuery() == 1)
                {
                    if (SqlCon.State == ConnectionState.Open) SqlCon.Close();
                    return true;
                }
                else
                {
                    if (SqlCon.State == ConnectionState.Open) SqlCon.Close();
                    return false;
                }
            }
            public bool EliminarSucursal(DSucursal objd)
            {
                SqlCon.ConnectionString = DConexion.Cn;
                SqlCon.Open();
                SqlCommand cmd = new SqlCommand();
                cmd.Connection = SqlCon;
                cmd.CommandText = "sp_eliminarsucursal";
                cmd.CommandType = CommandType.StoredProcedure;
                cmd.Parameters.AddWithValue("@id", objd.id);
                if (cmd.ExecuteNonQuery() == 1)
                {
                    if (SqlCon.State == ConnectionState.Open) SqlCon.Close();
                    return true;
                }
                else
                {
                    if (SqlCon.State == ConnectionState.Open) SqlCon.Close();
                    return false;
                }
            }
            public DataTable MostrarSucursal()
            {           
                try
                {
                    SqlCon.ConnectionString = DConexion.Cn;
                    SqlCon.Open();
                    SqlCommand cmd = new SqlCommand("sp_listarsucursal", SqlCon);
                    SqlDataReader LeerFilas;
                    DataTable Tabla = new DataTable();
                    cmd.CommandType = CommandType.StoredProcedure;
                    LeerFilas = cmd.ExecuteReader();
                    Tabla.Load(LeerFilas);
                    LeerFilas.Close();
                    return Tabla;
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    if (SqlCon.State == ConnectionState.Open) SqlCon.Close();
                }
                
            }
        }
    }
    
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using CapaNegocio;
    namespace VentasOficial
    {
        public partial class PSucursal : Form
        {
            public PSucursal()
            {
                InitializeComponent();
            } 
            private void button1_Click(object sender, EventArgs e)
            {
                NSucursal.InsertarSucursal(this.textBox1.Text.Trim(), this.textBox2.Text.Trim(), Convert.ToInt32(textBox3.Text),Convert.ToBoolean(checkBox1.Checked));
                MessageBox.Show("Guardado Correctamente");
            }
    
            private void PSucursal_Load(object sender, EventArgs e)
            {
                // TODO: esta línea de código carga datos en la tabla 'ventasDataSet2.sp_listarsucursal' Puede moverla o quitarla según sea necesario.
                // this.sp_listarsucursalTableAdapter.Fill(this.ventasDataSet2.sp_listarsucursal);
                Mostrar();
            }
            private int id;
            private DataTable tabla;
            private void Mostrar()
            {
                tabla = NSucursal.Mostrar();
                this.dataGridView1.DataSource = tabla;
                //this.OcultarColumnas();
                //lblTotal.Text = "Total Registros: " + Convert.ToString(dataListado.Rows.Count);
            }
    
            private void dataGridView1_CellMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
            {
                id = tabla.Rows[e.RowIndex].Field<int>(0);
                textBox1.Text = tabla.Rows[e.RowIndex].Field<string>(1);
                textBox2.Text = tabla.Rows[e.RowIndex].Field<string>(2);
                textBox3.Text = tabla.Rows[e.RowIndex].Field<int>(3).ToString();
                if (checkBox1.Checked == true)
                {
                    checkBox1.Checked = tabla.Rows[e.RowIndex].Field<bool>(4);
                }
                else
                {
                    checkBox1.Checked = tabla.Rows[e.RowIndex].Field<bool>(4);
                }
            }
        }
    }
    

    Y falta una opción para salir del programa.

    Saludos

    jueves, 16 de septiembre de 2021 19:20
  • Hola, me perdí un tiempo, empecé a trabajar y eso me a quitado mucho tiempo y el estudio.

    1.- Lo del form padre al form hijo, si le entendí logré hacerlo. 

    2.- Lo del árbol binario me sirvió como guía, ya que por el tiempo tuve que hacerlo con otro compañero que nos ayudo bastante, nos costó entender pero al final se entendió.

    3.- Sobre venta oficial, no me salía al comienzo pero, como no podía seguir programando recién volví a retomar lo que estaba haciendo, y me funcionó.

    4.- Una consulta fuera de todo esto, como te mencioné recién entre a trabajar, utilizo mucho excel y tabla dinámicas, mi pregunta es.....me gustaría cargar mis datos de excel en un datagridview y los datos cargados guardarlos en sql server, para luego crear procedimientos almacenados para crear los reportes que necesito con gráficos, para luego exportarlos para excel pero que cada reporte con gráfico se guarde en hoja separada en un mismo excel. Lo que quiero es ver cómo optimizar en la parte de reportes, no soy muy bueno con excel y tabla dinámicas. Todo esto si se puede mejor en asp.

    Saludos

    jueves, 30 de septiembre de 2021 4:21
  • Hola

    Para pasar datos de Excel a WinForms tenes que usar el modelo de objetos de Excel o Primary Interop Assemblies, lo podes descargar de Internet o como Nuget Package, creo que tenes que tener instalado Excel. Podes googlear y encontrar ejemplos, además yo si bien lo usé no lo recuerdo de memoria, pero si te tropezas con algo específico ahí me podes preguntar. Para pasar del DataGridView a la base de datos, también es fácil con ADO.Net. Si es esto solo no me parece que necesites hacerlo web, pero eso vos lo sabrás. 

    Saludos

    jueves, 30 de septiembre de 2021 22:53
  • Hola Tigre Pablito

    Una consulta si puedes ayudarme es con la conexión de sql.

    Problema

    Tengo una maquina física el cual solo tengo instalado visual studio y una máquina virtual con vmware donde sólo tengo instalado sql server ambos 2019, la máquina física puedo hacer ping a la virtual y la virtual a la física.

    En el visual estudio puedo conectar perfectamente al sql y a la base de datos de la máquina virtual y se me guarda el app.config, hago la clásica prueba de conexión y me conecta con la IP de sql server.

    Posibles soluciones que hice

    1.- Colocar reglas de entrada y salida en el firewall en la máquina virtual, tanto para el tcp con puerto 1433 y udp con puerto 1434, tabien hice una regla para el programa sql server y para el browser de sql.

    2.- En sqlserver configuration manager, en la opción de configuracion de red sql server, luego protocolos.

    • Memoria compartida = Habilitado
    • Canalizaciones con nombre = Habilitado
    • TCP/IP = Habilitado

    En TCP/IP agregue la ip y el puerto 1433 en la pestaña direcciones IP

    3.- En el sql server verique que tenga habilitado la opción de PERMITIR CONEXIONES REMOTAS CON ESTE SERVIDOR eso está habilitado.

    Pregunta:

    Qué más podría hacer para que en el visual studio al correr el programa no me salga el error 26 de sql server?

    Ojo 1: Me refiero al programa que que estoy haciendo cuando pregunte sobre sucursal que másç arriba me ayudas.

    Ojo 2: Ambas están en la misma red

    ERROR:

    System.Data.SqlClient.SqlException: 'Error relacionado con la red o específico de la instancia mientras se establecía una conexión con el servidor SQL Server. No se encontró el servidor o éste no estaba accesible. Compruebe que el nombre de la instancia es correcto y que SQL Server está configurado para admitir conexiones remotas. (provider: SQL Network Interfaces, error: 26 - Error al buscar el servidor o instancia especificado)'
    lunes, 18 de octubre de 2021 17:29