martes, 21 de septiembre de 2010

[WinForms] Inicio de sesión (I PARTE)

Todos los que hemos desarrollados programas de escritorio (C#, VB, FoxPro o demás lenguajes) y para la web (ASP, PHP) sabemos la importancia de controlar los usuarios que tienen acceso a la aplicación y a que opciones del menú estos tienen acceso,.

La forma de iniciar sesión es única para todas la aplicaciones, Usuario y Contraseña de acceso, validar contra la base de datos o conectar con las credenciales del usuario de Windows logeado en el momento, pero para mi esta ultima puede ser peligrosa ya que cualquier usuario podría accesar a la aplicación usando la computadora de otro usuario que se haya descuidado.

Bien, el objetivo de este articulo es ayudar a tomar una decisión sobre como hacer esto cuando estamos desarrollando una aplicación para múltiples usuarios, la técnica que usaremos en nuestro ejemplo será iniciar sesión como usuario local de SQL Server, en este ejemplo utilizares esta base de datos, para que así no nos preocupemos en guardar la contraseña de acceso en una tabla y encryptar está, así también evitamos usar el “sa” de SQL Server para conectarnos, ya que esto nos perjudica, ya que no podemos cambiar la contraseña de este porque si lo hacemos ningún usuario que use nuestra aplicación podrá conectarse y también perdemos la funcionalidad de poder usar original_login() como valor predeterminado en algunos campos de nuestras tablas.

siempre validaremos contra una tabla local en nuestra base de datos para saber si el usuario puede usar nuestra aplicación y saber a que opciones del menú este tiene acceso.

nuestro lenguaje de programación será C# .Net en VS2008 y también lo hare en VB, así que manos al teclado.

Primero lo primero, debemos crear un formulario que permita al usuario ingresar su Usuario y Contraseña de acceso, como también seleccionar el servidor de SQL Server al que se conectaran, este ultimo podría ser tomado de un archivo de configuración el problema puede ser cuando cambiamos el servidor de SQL server tendríamos que ir computadora por computadora cambiando esta dirección en el archivo de configuración, pero bien lo dejo a criterio de cada quien.

así que nuestro formulario debería lucir así:

image

  • TextBox Usuario.
    Name txtUsuario
  • TextBox Contraseña.
    Name txtContraseña
    UseSystemPasswordChar

    True

  • ComboBox Servidor de Conexión.
    Name cboBoxServidorConexion
    Button Aceptar
    Name bAceptar
    Evento Click bAceptar_Click

Además he agregado un label y un ProgressBar (tal como se muestra en la imagen) estos con la intención de buscar los servidores de SQL Server en un hilo y que el usuario vea el progreso de la búsqueda, también lo utilizare al momento de presionar el botón Aceptar, la validación del usuario la hare en un hilo para que el usuario no tenga la sensación de que la aplicación se ha pegado, ya que siempre que hacemos conexión la primera vez suele demorar un tiempo.

ya creado el formulario, veamos el código, pieza por pieza.

1 using System.Data;
2 using System.Data.Sql;
3 using System.Drawing;
4 using System.Linq;
5 using System.Text;
6 using System.Windows.Forms;
7
8 using CP = ConnectionProvider; // Proveedor de Conecciones.
9 using DE = AppEjemplo.Entidades;
10
11 namespace AppEjemplo.Dialogos
12 {
13 public partial class IniciarSession : Form
14 {
15 // Mecanismo para enumera todas las instancias de SQL Server incluidas en la red local.
16 SqlDataSourceEnumerator SqlInstances = SqlDataSourceEnumerator.Instance;
17 }
18 }


Constructor del formulario.



1 public IniciarSession()
2 {
3 InitializeComponent();
4 #region -> Hilo para buscar servidores de SQL Server
5
6 BackgroundWorker buscarServidoresSQL = new BackgroundWorker();
7 buscarServidoresSQL.WorkerReportsProgress = true;
8 buscarServidoresSQL.DoWork += new DoWorkEventHandler(buscarServidoresSQL_DoWork);
9 buscarServidoresSQL.ProgressChanged += new ProgressChangedEventHandler(buscarServidoresSQL_ProgressChanged);
10 buscarServidoresSQL.RunWorkerCompleted += new RunWorkerCompletedEventHandler(buscarServidoresSQL_RunWorkerCompleted);
11 buscarServidoresSQL.RunWorkerAsync();
12
13 #endregion
14 }
15
en el constructor creamos un BackgroundWorker, lo programamos y lo ejecutamos para que inicie a buscar los servidores de SQL Server disponibles en nuestra red.


Hilo para buscar servidores SQL.



1 #region -> Eventos de hilo para recuperar todas las instancias de SQL Server disponibles
2
3 void buscarServidoresSQL_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
4 {
5 // oculta label y barra de progreso.
6 lblMensaje.Visible = false;
7 pbBusqueda.Visible = false;
8 }
9
10 void buscarServidoresSQL_ProgressChanged(object sender, ProgressChangedEventArgs e)
11 {
12 if (e.UserState is bool)
13 {
14 // muestra label y barra de progreso
15 lblMensaje.Visible = true;
16 pbBusqueda.Visible = true;
17 }
18 else if (e.UserState is string)
19 {
20 // agrega servidor SQL server a items del combobox
21 cboBoxServidorConexion.Items.Add((string)e.UserState);
22 }
23 }
24
25 void buscarServidoresSQL_DoWork(object sender, DoWorkEventArgs e)
26 {
27 BackgroundWorker bgw = sender as BackgroundWorker;
28
29 bgw.ReportProgress(0, true); // mostrar label y barra de progreso.
30 DataTable servidores = SqlInstances.GetDataSources(); // obtiene servidores SQL Server disponibles en la red local.
31 foreach(DataRow instance in servidores.Rows)
32 {
33 string serverName = instance.Field<string>("ServerName");
34 string instanceName = instance.Field<string>("InstanceName");
35 // Reporta servidor encontrado para ser agreado a ComboBox
36 bgw.ReportProgress(0, string.Format("{0}\\{1}", serverName, instanceName));
37 }
38 }
39
40 #endregion
41


Botón Aceptar (Evento Click)


1 private void bAceptar_Click(object sender, EventArgs e)
2 {
3 BackgroundWorker bgw = new BackgroundWorker();
4 bgw.WorkerReportsProgress = true;
5 bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
6 bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
7 bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
8 bgw.RunWorkerAsync(new string[] { txtUsuario.Text, txtContraseña.Text, cboBoxServidorConexion.Text });
9 }

como podemos ver aqui creamos otro hilo para que al validar el usuario no se congele la interfaz, hacemos uso del Label y ProgressBar para notificar el progreso, pasando como parámetros al hilo el usuario, contraseña e instancia de SQL Server seleccionada.


Hilo para validar usuario



1 void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
2 {
3 if (e.UserState is bool)
4 {
5 lblMensaje.Text = (bool)e.UserState ? "Validando Usuario y Contraseña" : lblMensaje.Text;
6 lblMensaje.Visible = (bool)e.UserState;
7 pbBusqueda.Visible = (bool)e.UserState;
8 }
9 }
10
11 void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
12 {
13 if (e.Result is bool)
14 {
15 if ((bool)e.Result)
16 {
17 // Autenticación de usuario exitosa.
18 AppEjemploHelper.SetUserConnected(txtUsuario.Text);
19 DialogResult = DialogResult.OK; // Cierra formulario InicioSession.
20 }
21 else
22 {
23 MessageBox.Show("No se logro establecer conexión\n\nVerifique el usuario y la contraseña de acceso.",
24 "Acceso invalido",
25 MessageBoxButtons.OK,
26 MessageBoxIcon.Error);
27 txtUsuario.Focus();
28 }
29 }
30 }
31
32 void bgw_DoWork(object sender, DoWorkEventArgs e)
33 {
34 #region -> Recuperamos parametros pasados al hilo
35
36 string UID = ((string[])e.Argument)[0];
37 string PWD = ((string[])e.Argument)[1];
38 string DSource = ((string[])e.Argument)[2];
39
40 #endregion
41
42 BackgroundWorker bgw = sender as BackgroundWorker;
43
44 bgw.ReportProgress(0, true); // Mostrar Label y Barra de progreso.
45 // Validar usuario y contraseña.
46 // true = si la autenticación es correcta.
47 // false = en caso contrario
48 e.Result = CP.SQLProvider.ConnectToSqlServer(UID, PWD, DSource, AppEjemploHelper.InitialCatalog);
49 if ((bool)e.Result && UID.ToLower() != "sa")
50 {
51 #region -> verificamos si el usuario esta registrado en nuestr tabla de usuarios.
52
53 DE.Usuarios usuario = DE.UsuariosDA.Usuario(UID);
54 e.Result = (usuario != null);
55 // si el usuario no esta registrado en nuestra tabla de usuarios.
56 // no tendra acceso a la aplicación.
57 if ((bool)e.Result) // El usuario esta registrado.
58 e.Result = usuario.Activo;
59 // si el usuario no esta activo en la tabla de usuario no tendra
60 // acceso a la aplicación.
61
62 #endregion
63 }
64 bgw.ReportProgress(0, false); // ocultar label y barra de progreso.
65 }
66

y modificamos el archivo Program.cs para que tras que inicie la aplicación nos muestre el formulario para iniciar sesión.


1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Windows.Forms;
5
6 namespace AppEjemplo
7 {
8 static class Program
9 {
10 /// <summary>
11 /// The main entry point for the application.
12 /// </summary>
13 [STAThread]
14 static void Main()
15 {
16 Application.EnableVisualStyles();
17 Application.SetCompatibleTextRenderingDefault(false);
18
19 Dialogos.IniciarSession loginIn = new Dialogos.IniciarSession();
20 if (loginIn.ShowDialog() == DialogResult.OK)
21 {
22 Application.Run(new Principal());
23 }
24 else
25 {
26 Application.Exit();
27 }
28 }
29 }
30 }
31

Para VBasic .Net en VS2008 se a usado ApplicationEvents.vb


[VB]


1 Namespace My
2
3 ' The following events are available for MyApplication:
4 '
5 ' Startup: Raised when the application starts, before the startup form is created.
6 ' Shutdown: Raised after all application forms are closed. This event is not raised if the application terminates abnormally.
7 ' UnhandledException: Raised if the application encounters an unhandled exception.
8 ' StartupNextInstance: Raised when launching a single-instance application and the application is already active.
9 ' NetworkAvailabilityChanged: Raised when the network connection is connected or disconnected.
10
11 Partial Friend Class MyApplication
12 Private Sub MyApplication_Startup(ByVal sender As Object, _
13 ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupEventArgs) Handles Me.Startup
14 Dim loginIn As New IniciarSession()
15 If loginIn.ShowDialog() <> DialogResult.OK Then
16 e.Cancel = True
17 End If
18 End Sub
19 End Class
20
21 End Namespace
22


Obteniendo el siguiente resultado como se muestra en la imagen a continuación.


image



image


 


Bien, si les ha parecido bueno este articulo, no dejen de leer la segunda parte en la cual explicare como activar y desactivar las opciones del Menú a las cuales tiene acceso el usuario.


y aqui les dejo el código completo del formulario para inicio de sesión, en C# y VB, y al final estará disponible el proyecto para que pueda ser descargado en el cual se incluirá el fuente de “ConnectionProvider” este ultimo proyecto funciona a puro Reflection para crear las sentencias Select, Insert, Update y Delete según la entidad.


[C#] – Código completo.



1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Data.Sql;
6 using System.Drawing;
7 using System.Linq;
8 using System.Text;
9 using System.Windows.Forms;
10
11 using CP = ConnectionProvider;
12 using DE = AppEjemplo.Entidades;
13
14 namespace AppEjemplo.Dialogos
15 {
16 public partial class IniciarSession : Form
17 {
18 // Mecanismo para enumera todas las instancias de SQL Server incluidas en la red local.
19 SqlDataSourceEnumerator SqlInstances = SqlDataSourceEnumerator.Instance;
20
21 public IniciarSession()
22 {
23 InitializeComponent();
24 #region -> Hilo para buscar servidores de SQL Server
25
26 BackgroundWorker buscarServidoresSQL = new BackgroundWorker();
27 buscarServidoresSQL.WorkerReportsProgress = true;
28 buscarServidoresSQL.DoWork += new DoWorkEventHandler(buscarServidoresSQL_DoWork);
29 buscarServidoresSQL.ProgressChanged += new ProgressChangedEventHandler(buscarServidoresSQL_ProgressChanged);
30 buscarServidoresSQL.RunWorkerCompleted += new RunWorkerCompletedEventHandler(buscarServidoresSQL_RunWorkerCompleted);
31 buscarServidoresSQL.RunWorkerAsync();
32
33 #endregion
34 }
35
36 #region -> Eventos de hilo para recuperar todas las instancias de SQL Server disponibles
37
38 void buscarServidoresSQL_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
39 {
40 lblMensaje.Visible = false;
41 pbBusqueda.Visible = false;
42 }
43
44 void buscarServidoresSQL_ProgressChanged(object sender, ProgressChangedEventArgs e)
45 {
46 if (e.UserState is bool)
47 {
48 lblMensaje.Visible = true;
49 pbBusqueda.Visible = true;
50 }
51 else if (e.UserState is string)
52 {
53 cboBoxServidorConexion.Items.Add((string)e.UserState);
54 }
55 }
56
57 void buscarServidoresSQL_DoWork(object sender, DoWorkEventArgs e)
58 {
59 BackgroundWorker bgw = sender as BackgroundWorker;
60 bgw.ReportProgress(0, true);
61 DataTable servidores = SqlInstances.GetDataSources();
62 foreach(DataRow instance in servidores.Rows)
63 {
64 string serverName = instance.Field<string>("ServerName");
65 string instanceName = instance.Field<string>("InstanceName");
66 bgw.ReportProgress(0, string.Format("{0}\\{1}", serverName, instanceName));
67 }
68 }
69
70 #endregion
71
72 private void bAceptar_Click(object sender, EventArgs e)
73 {
74 BackgroundWorker bgw = new BackgroundWorker();
75 bgw.WorkerReportsProgress = true;
76 bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);
77 bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
78 bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
79 bgw.RunWorkerAsync(new string[] { txtUsuario.Text, txtContraseña.Text, cboBoxServidorConexion.Text });
80 }
81
82 #region -> Eventos de hilo para validar usuario y contraseña de acceso a SQL Server
83
84 void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
85 {
86 if (e.UserState is bool)
87 {
88 lblMensaje.Text = (bool)e.UserState ? "Validando Usuario y Contraseña" : lblMensaje.Text;
89 lblMensaje.Visible = (bool)e.UserState;
90 pbBusqueda.Visible = (bool)e.UserState;
91 }
92 }
93
94 void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
95 {
96 if (e.Result is bool)
97 {
98 if ((bool)e.Result)
99 {
100 AppEjemploHelper.SetUserConnected(txtUsuario.Text);
101 DialogResult = DialogResult.OK;
102 }
103 else
104 {
105 MessageBox.Show("No se logro establecer conexión\n\nVerifique el usuario y la contraseña de acceso.",
106 "Acceso invalido",
107 MessageBoxButtons.OK,
108 MessageBoxIcon.Error);
109 txtUsuario.Focus();
110 }
111 }
112 }
113
114 void bgw_DoWork(object sender, DoWorkEventArgs e)
115 {
116 #region -> Recuperamos parametros pasados al hilo
117
118 string UID = ((string[])e.Argument)[0];
119 string PWD = ((string[])e.Argument)[1];
120 string DSource = ((string[])e.Argument)[2];
121
122 #endregion
123
124 BackgroundWorker bgw = sender as BackgroundWorker;
125 bgw.ReportProgress(0, true);
126 e.Result = CP.SQLProvider.ConnectToSqlServer(UID, PWD, DSource, AppEjemploHelper.InitialCatalog);
127 if ((bool)e.Result && UID.ToLower() != "sa")
128 {
129 #region -> verificamos si el usuario esta registrado en nuestr tabla de usuarios.
130
131 DE.Usuarios usuario = DE.UsuariosDA.Usuario(UID);
132 e.Result = (usuario != null); // si el usuario no esta registrado en nuestra tabla de usuarios.
133 // no tendra acceso a la aplicación.
134 if ((bool)e.Result)
135 e.Result = usuario.Activo; // si el usuario no esta activo en la tabla de usuario no tendra
136 // acceso a la aplicación.
137
138 #endregion
139 }
140 bgw.ReportProgress(0, false);
141 }
142
143 #endregion
144 }
145 }
146

[VB] - Código completo


1 Imports System.Data.Sql
2 Imports System.ComponentModel
3
4 Imports CP = ConnectionProvider
5 Imports DE = AppEjemploVB.Entidades
6
7 Public Class IniciarSession
8
9 'Mecanismo para enumera todas las instancias de SQL Server incluidas en la red local.
10 Dim SqlInstances As SqlDataSourceEnumerator = SqlDataSourceEnumerator.Instance
11
12 Sub New()
13
14 ' This call is required by the Windows Form Designer.
15 InitializeComponent()
16
17 ' Add any initialization after the InitializeComponent() call.
18 Dim buscarServidoresSQL As New BackgroundWorker()
19 buscarServidoresSQL.WorkerReportsProgress = True
20 AddHandler buscarServidoresSQL.DoWork, AddressOf buscarServidoresSQL_DoWork
21 AddHandler buscarServidoresSQL.ProgressChanged, AddressOf buscarServidoresSQL_ProgressChanged
22 AddHandler buscarServidoresSQL.RunWorkerCompleted, AddressOf buscarServidoresSQL_RunWorkerCompleted
23 buscarServidoresSQL.RunWorkerAsync()
24 End Sub
25
26 #Region "-> Eventos de hilo para recuperar todas las instancias de SQL Server disponibles"
27
28 Private Sub buscarServidoresSQL_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
29 lblMensaje.Visible = False
30 pbBusqueda.Visible = False
31 End Sub
32
33 Private Sub buscarServidoresSQL_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
34 If (TypeOf e.UserState Is Boolean) Then
35 lblMensaje.Visible = True
36 pbBusqueda.Visible = True
37 ElseIf (TypeOf e.UserState Is String) Then
38 cboBoxServidorConexion.Items.Add(CType(e.UserState, String))
39 End If
40 End Sub
41
42 Private Sub buscarServidoresSQL_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
43 Dim bgw As BackgroundWorker = CType(sender, BackgroundWorker)
44 bgw.ReportProgress(0, True)
45 Dim servidore As DataTable = SqlInstances.GetDataSources()
46 For Each instance As DataRow In servidore.Rows
47 Dim serverName As String = instance.Field(Of String)("ServerName")
48 Dim instanceName As String = instance.Field(Of String)("InstanceName")
49 bgw.ReportProgress(0, String.Format("{0}\{1}", serverName, instanceName))
50 Next
51 End Sub
52
53 #End Region
54
55 Private Sub bAceptar_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles bAceptar.Click
56 Dim bgw As New BackgroundWorker()
57 bgw.WorkerReportsProgress = True
58 AddHandler bgw.ProgressChanged, AddressOf bgw_ProgressChanged
59 AddHandler bgw.RunWorkerCompleted, AddressOf bgw_RunWorkerCompleted
60 AddHandler bgw.DoWork, AddressOf bgw_DoWork
61 bgw.RunWorkerAsync(New String() {txtUsuario.Text, txtContraseña.Text, cboBoxServidorConexion.Text})
62 End Sub
63
64 #Region "-> Eventos de hilo para validar usuario y contraseña de acceso a SQL Server"
65
66 Private Sub bgw_ProgressChanged(ByVal sender As Object, ByVal e As ProgressChangedEventArgs)
67 If TypeOf e.UserState Is Boolean Then
68 lblMensaje.Text = IIf(CType(e.UserState, Boolean), "Validando Usuario y Contraseña", "")
69 lblMensaje.Visible = CType(e.UserState, Boolean)
70 pbBusqueda.Visible = CType(e.UserState, Boolean)
71 End If
72 End Sub
73
74 Private Sub bgw_RunWorkerCompleted(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs)
75 If TypeOf e.Result Is Boolean Then
76 If CType(e.Result, Boolean) Then
77 AppEjemploHelper.SetUserConnected(txtUsuario.Text)
78 DialogResult = Windows.Forms.DialogResult.OK
79 Else
80 MessageBox.Show("No se logro establecer conexión" & vbCrLf & vbCrLf & "Verifique el usuario y la contraseña de acceso.", _
81 "Acceso invalido", _
82 MessageBoxButtons.OK, _
83 MessageBoxIcon.Error)
84 txtUsuario.Focus()
85 End If
86 End If
87 End Sub
88
89 Private Sub bgw_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs)
90 Dim UID As String = CType(e.Argument, String())(0)
91 Dim PWD As String = CType(e.Argument, String())(1)
92 Dim DSource As String = CType(e.Argument, String())(2)
93
94 Dim bgw As BackgroundWorker = CType(sender, BackgroundWorker)
95 bgw.ReportProgress(0, True)
96
97 e.Result = CP.SQLProvider.ConnectToSqlServer(UID, PWD, DSource, AppEjemploHelper.InitialCatalog)
98 If CType(e.Result, Boolean) And UID.ToLower() <> "sa" Then
99
100 Dim usuario As DE.Usuarios = DE.UsuariosDA.Usuarios(UID)
101 e.Result = usuario IsNot Nothing
102 If CType(e.Result, Boolean) Then
103 e.Result = usuario.Activo
104 End If
105
106 End If
107
108 bgw.ReportProgress(0, False)
109 End Sub
110
111 #End Region
112
113 End Class

16 comentarios:

  1. hola amigo soy luis muñoz del foro de msdn, fui el que formule la pregunta del tema de las matriculas y reingreso. Estuve chekando tu blog, basicamente me sirvio la parte del login o inicio de sesion, en lo respecta a matriculas, debo reconocer que aun soy un poco novato en el tema de analizar, me gustaria saber si es posible que pueda contactar contigo para aunque sea me puedas mostrar algunas tablas del sistema que hiciste en VFP (Visual Fox Pro), hay alguna posibilidad de chekarlas y yo me pueda guiar de ahi para adaptarlo a mis necesidades, te dejo mi msn para alguna respuesta tuya, luis_rp15@hotmail.com

    ResponderEliminar
  2. Hola marvin, estan elegante tus controles, es muy complicado crear controles y adaptarlos segun sea la necesidad??

    saludos.

    ResponderEliminar
  3. Saludos Marvin soy participante de MSDN (RiveraCorrea) el la forma como creastes los controles me parece excelente, hay alguna forma que me puedas facilitar el fuente de como creastes el TextEditor es que me gustaria crear controles de esa forma como tu los has creado.
    yriveracorrea@gmail.com
    saludos y gracias

    ResponderEliminar
  4. Hola Anónimo...

    la verdad que no es tan facil, pero tampoco es dificil... bueno!!!! siempre digo esto... yo creo que para crear un control hay que tener un objetivo para saber que tienes que buscar y como lo enfrentaras, generalmente hacer un control por hacer uno no creo que te ayude mucho ya que no sabes la funcionalidad que le quieres dar ni tienes un objetivo para el.

    lo importante es saber que quieres hacer para buscar las posibilidades de hacerlo.

    Salu2,

    ResponderEliminar
  5. el fuente!!!!!... bueno, no es imposible.... solo que por el momento no estan disponibles...

    Salu2,

    ResponderEliminar
  6. hola marvin de nuevo RiveraCorrea, algun consejo u articulo claro sobre el cual pueda iniciar, de verdad que me parece super lo que hicistes con el control del TextBox.

    en lo que me puedas colaborar agradecido estare.

    saludos.

    ResponderEliminar
  7. Hola Marvin, como estas??
    Estaba viendo como poder lograr hacer el control TextEditor con esas variantes que definis y me es imposible de crearlo.
    Quería saber si me puedes dar una mano para poder crearla.

    mi correo por cualquier cosa es llequini@gmail.com

    Desde ya muchas gracias y saludos desde Uruguay

    ResponderEliminar
  8. Hola Leonardo, ya te respondi en el foro de C# y como lo menciono en el foro, este control nacio a raiz de una pregunta que se hizo en el foro de VBasic sobre como cambiar el borde del TextBox, no es que no te queria ayudar, pero a segun pude ver no es facil encontrar el codigo sobre como hacer este tipo de control o sobre como extender de esta manera el control TextBox, bueno!!!... realmente no he encontrado un ejemplo que incluya el código, vi uno pero esta en C++....

    bueno, revisa el link que te publique a la pregunta en el foro de VBasic ya que me sirvio de base....

    Salu2

    ResponderEliminar
  9. Gracias Marvin por las respuestas en el foro, estaré viendo que puedo hacer. Ojalá encuentre algo parecido al tuyo, me conformaria con lograr una imagen de busqueda y que el texto de busqueda quede de la forma ela que creaste.

    Salu2 y gracias

    ResponderEliminar
  10. hola quisiera que me facilite todo el proyecto porfa
    este es mi correo adrivict-men@hotmail.com

    ResponderEliminar
  11. déjame lo busco porque este fue uno de los primeros artículos que escribí y aun no había definido el tema del blog, y lo he orientado más a la creación/personalización de controles.

    Salu2,

    ResponderEliminar
  12. Marvin. Te felicito, muy buen blog ha sido de bastante ayuda. Me podrias colaborar con el código fuente de este artículo ([WinForms] Inicio de sesión (I PARTE)), que tengo problemas con "ConnectionProvider" para implementarlo.

    Y estoy a la espera de la segunda parte de este artículo. Gracias de antemano

    ResponderEliminar
  13. amigo tengo una duda donde va la base de datos de todos los usuarios,en que parte lo localiza

    ResponderEliminar
  14. la tabla de usuarios, deberia de ser una tabla más dentro de la base de datos del programa que estes desarrollando.

    ResponderEliminar
  15. Muy buena publicación amigo, pero tengo dudas desde que comencé a leer a cerca de éstas librerías:

    using CP = ConnectionProvider;
    using DE = AppEjemplo.Entidades;

    Te agradecería la explicación, saludos.

    ResponderEliminar
  16. Hola Marvin

    Gracias por tus aporte me sirven de ayuda para iniciarme en la programación.

    te solicito una explicación acerca de donde puedo conseguir estas librerias.

    using CP = ConnectionProvider;
    using DE = AppEjemplo.Entidades;


    Gracias.

    ResponderEliminar