Comunicación entre formularios en WinForms
Comunicación entre formularios en WinForms
Introduccin
Usando Interfaces
Usando eventos
Usando interfaces
Una interfaz permite definir un contrato entre clases, permitiendo que una invoque la
funcionalidad de cualquier otra que respete ese contrato definido.
Recordemos que los formularios en definitiva no son mas que clases que se instancian
por lo que puede aplicarse los mismos conceptos de POO (Programacin orientada a
objetos).
La implementacin de esta tcnica se encuentra en la bsqueda de un proveedor en el
form de edicin de los productos.
El form padre ser quien instancie el form hijo pasando su instancia por parmetro en
el constructor, adems de algn otro valor adicional de ser necesario, como en este
caso un filtro para limitar los tems visualizados en el grid de seleccin.
La variable caller contendr la instancia del form padre que implementa la interfaz,
mientras que _supplierName tendr el input del usuario para filtrar las entidades.
Al seleccionar un tem del grid se dar por seleccionada la entidad y se enviara la
accin al form padre a travs de la instancia invocando el mtodo definido en la
interfaz.
Usando eventos
Para definir datos especficos en el intercambio de informacin se crea una clase que
hereda de EventArgs
Para actualizar un grid luego de una accin en otro form podra utilizarse eventos
existentes, como ser el FormClosed, con este evento se puede realizar una accin al
cerrar un form
Descarga
El articulo fue desarrollando usando Visual Studio 2012, la base de datos es Sql
Compact (.sdf)
Durante la compilacin se descargaran las dependencias por medio de Nuget.
[C#]
Publicado por Leandro Tuttini en 14:38 13 comentarios:
Etiquetas: C#, WinForm
Introduccin
Conociendo solo el nombre del formulario se abra una instancia del mismo.
Para poder realizar esta accin es necesario conocer el nombre del form, pero tambin
bajo que namespace se ubica.
Uso de Reflection
Para poder abrirlo vamos a necesitara Reflection el cual no permitir descubrir el Type
del form basado en la unin del namespace + nombre
es por eso que se define:
using System.Reflection;
[C#]
Introduccin
Determinar si un form ha sido abierto, evitando que una nueva instancia sea creada.
La seleccin de botn abrir una instancia, pero las siguientes pulsaciones solo harn
que el mismo form pase al frente.
Cdigo
Introduccin
Listar los forms del proyecto puede parecer una tarea a simple vista fcil, pero tiene
sus particularidades.
En el ejemplo se listara los nombre de los formularios, pudiendo abrirlo mediante la
seleccin en una lista.
Listar Formularios
Para listar los formularios podremos ayudarnos de linq, este nos permitir filtrar por el
tipo de clase del cual heredan los forms.
Para obtener las propiedades (como ser el Text) del formulario se requiere una
instancia del mismo. Recordemos que en .net todos son objetos, por lo que un form
requiere de una instancia para poder acceder a sus propiedades.
lstForms.DisplayMember = "Text";
lstForms.DataSource = formList;
}
Tambin se puede realizar la misma tarea sin hacer uso de linq
lstForms.DataSource = formList;
}
Cdigo
Introduccin
validar si el nodo tiene un nodo padre, para lo cual ser necesario verificar si
todos los hijos estn marcados
Estas acciones se realizan sobre el mismo evento ya que implica de forma contextual
al nodo que interviene en la accin
node.Checked = e.Node.Checked;
}
}
tvCategoryproducts.AfterCheck += tvCategoryproducts_AfterCheck;
}
Linq nos puede evitar tener que escribir un montn de cdigo, sino lo creen solo basta
con dar una mirada a la implementacin alternativa que evala los nodos marcados
En este caso hice uso de los mtodo de extensin ayudado con lambda, pero se podra
haber usado la notacin linq sin problemas
}
//
// Se valida si el nodo tiene hijos
// si los tiene se recorren y asignan el estado del nodo que se esta
evaluando
//
if (e.Node.Nodes.Count > 0)
{
e.Node.Nodes.Cast<TreeNode>().ToList().ForEach(x => x.Checked =
e.Node.Checked);
}
tvCategoryproducts.AfterCheck += tvCategoryproducts_AfterCheck_UsingLinq;
}
Cdigo
[C# SkyDrive]
Publicado por Leandro Tuttini en 21:51 16 comentarios:
Etiquetas: WinForm
Introduccin
Este artculo representa continuacin del anterior
Filtros Condicionales (1/2)
en realidad se podra ver como una extensin, ya que aqu el objetivo consiste en
poder aplicar mltiples filtro usando de cursos, y no solo uno como fue en el artculo
previo
La interaccin en la pantalla, para esta funcionalidad en concreto, esta dada por los
siguientes pasos:
Algo que hay que aclarar antes de abordar las soluciones planteadas es que la
instruccin IN de sql no soporta de forma directa el uso de parmetros, es por eso que
existen actualmente varios caminos a tomar ante esta situacin, aqu solo expondr
dos de ellos, pero existen algunos otros.
Entre los temas tratados se podrn encontrar
1. Comunicacin entre formularios
2. Filtro IN, concatenado el string
3. Filtro por medio de XML
13.
14.private void btnCancelar_Click(object sender, EventArgs e)
15.{
16.this.DialogResult = DialogResult.Cancel;
17.}
P.PersonID,
04.P.LastName,
05.P.FirstName,
06.P.HireDate,
07.P.EnrollmentDate
08.FROM Person P
09.LEFT JOIN CourseInstructor CI
10.ON P.PersonID = CI.PersonID
11.WHERE ((@FirstName IS NULL) OR (P.FirstName LIKE '%' + @FirstName
+ '%'))
12.AND ((@LastName IS NULL) OR (P.LastName LIKE '%' + @LastName +'%'))
13.AND ((@HireDateFrom IS NULL) OR (P.HireDate >= @HireDateFrom))
14.AND ((@HireDateTo IS NULL) OR (P.HireDate <= @HireDateTo))
15.AND ((@EnrollmentDateFrom IS NULL) OR (P.EnrollmentDate >=
@EnrollmentDateFrom))
16.AND ((@EnrollmentDateTo IS NULL) OR (P.EnrollmentDate <=
@EnrollmentDateTo))
17.AND ((@Course IS NULL) OR (CI.CourseID IN ({0})))";
18.
19.if (filter.Course != null)
20.{
21.string courseFilter = string.Join(",", filter.Course.ConvertAll(x =>
x.CourseID.ToString()).ToArray());
22.sql = sql.Replace("{0}", courseFilter);
23.}
24.
25.List<PersonEntity> list = new List<PersonEntity>();
26.
27.using (SqlConnection conn
= newSqlConnection(ConfigurationManager.ConnectionStrings["default"].ToSt
ring()))
28.{
29.conn.Open();
30.
31.SqlCommand cmd = new SqlCommand(sql, conn);
32.
33.if (string.IsNullOrEmpty(filter.FirstName))
34.cmd.Parameters.AddWithValue("@FirstName", DBNull.Value);
35.else
36.cmd.Parameters.AddWithValue("@FirstName",
filter.FirstName);
37.
38.cmd.Parameters.AddWithValue("@LastName",string.IsNullOrEmpty(filter.Fi
rstName) ? (object)DBNull.Value : filter.LastName);
39.
40.cmd.Parameters.AddWithValue("@HireDateFrom",
filter.HireDateFrom.HasValue ? filter.HireDateFrom.Value.Date :
(object)DBNull.Value);
41.cmd.Parameters.AddWithValue("@HireDateTo", filter.HireDateTo.HasValue
? filter.HireDateTo.Value.Date : (object)DBNull.Value);
42.
43.cmd.Parameters.AddWithValue("@EnrollmentDateFrom",
filter.EnrollmentDateFrom.HasValue ? filter.EnrollmentDateFrom.Value.Date
: (object)DBNull.Value);
44.cmd.Parameters.AddWithValue("@EnrollmentDateTo",
filter.EnrollmentDateTo.HasValue ? filter.EnrollmentDateTo.Value.Date :
(object)DBNull.Value);
45.
46.cmd.Parameters.AddWithValue("@Course", filter.Course == null ?
(object)DBNull.Value : "");
47.
48.SqlDataReader reader = cmd.ExecuteReader();
49.
50.while (reader.Read())
51.{
52.list.Add(LoadPerson(reader));
53.}
54.
55.return list;
56.}
57.
58.}
El mismo sigue todas las reglas mencionadas en la parte 1 del articulo, solo que filtro
de cursos tiene una particularidad.
Es preciso notar las lneas 19-23, en estas es cuando se une al string de la consulta
principal, la lista de cursos seleccionados.
La lnea 46, sigue representando la anulacin o no del filtro del cursos, esto es
necesario en caso de no enviarse ningn tem en la seleccin.
int
P.PersonID,
08.P.LastName,
09.P.FirstName,
10.P.HireDate,
11.P.EnrollmentDate
12.FROM Person P
13.LEFT JOIN CourseInstructor CI
14.ON P.PersonID = CI.PersonID
15.LEFT JOIN OPENXML(@idoc, '/courses/course', 2)
16.WITH (id
45.cmd.Parameters.AddWithValue("@EnrollmentDateTo",
filter.EnrollmentDateTo.HasValue ? filter.EnrollmentDateTo.Value.Date :
(object)DBNull.Value);
46.
47.if (filter.Course != null)
48.{
49.XElement root = new XElement("courses");
50.List<XElement> couseList = filter.Course.ConvertAll(x
=> newXElement("course", x.CourseID));
51.root.Add(couseList.ToArray());
52.
53.cmd.Parameters.AddWithValue("@Course", root.ToString());
54.}
55.else
56.{
57.cmd.Parameters.AddWithValue("@Course", DBNull.Value);
58.}
59.
60.
61.SqlDataReader reader = cmd.ExecuteReader();
62.
63.while (reader.Read())
64.{
65.list.Add(LoadPerson(reader));
66.}
67.
68.return list;
69.}
70.
71.}
Las lneas 47-58, implementan la conversin a xml de los cursos, esta toman la forma
<courses>
<course>1045</course>
<course>1061</course>
</courses>
o sea cada curso seleccionado representa un tag en el xml
La consulta tiene algunas particularidades como ser las dos primeras lneas
Para mas informacin sobre como trabajar el xml en T-SQL, un excelente recurso es el
MSDN Library
OPEN XML
Usar OPENXML
Se puede adems realizar pruebas puntuales del xml para entender su funcionamiento,
por ejemplo
DECLARE @Course As VARCHAR(1000)
SET @Course = N'<courses>
<course>1045</course>
<course>1061</course>
</courses>'
DECLARE @idoc int
EXEC sp_xml_preparedocument @idoc OUTPUT, @Course
SELECT * FROM OPENXML(@idoc, '/courses/course', 2)
WITH (id int 'text()')
Estas podran ser ejecutadas en el Sql Server Management Studio
Links tiles
Arrays and Lists in SQL Server 2000 and Earlier
Ejemplo de Cdigo
La base de datos fue creada con Sql Server Express 2008 R2, en caso de tener
problemas con al misma en el proyecto DataAccess esta la carpeta script con el .sql
que puede usar para crear estructura y datos.
[C#]
Introduccin
Al desarrollar una aplicacin un aspecto del cual seguramente habr que pensar es
como otorgar al usuario informacin que el sea til y simple de analizar.
En este punto juega un papel fundamental como disear los filtros aplicado a las
entidades, para poder se lo mas preciso posible en la bsqueda.
Es por eso que en este artculo se plantea la implementacin de bsqueda por medio
de clases que denominare Criteria, ya que esta sern las encargadas de proporcionar
los datos seleccionados por el usuario en la pantalla.
Como punto ventajoso de esta tcnica se podra mencionar que la clase evita tener que
redefinir los parmetros del mtodo de bsqueda cada vez que se quiera extender la
funcionalidad. Esto es muy importante si se tiene la idea de aplicar algn patrn como
ser el Repository.
Los requerimientos cambiantes en las aplicaciones hacen que en un primer momento
un filtro simple de dos campos cubra la necesidad, pero el uso diario por parte del
usuario ira cambiando esta visin, solicitando agregar nuevo filtros, el uso de una clase
como tmplate de filtro facilita esta tarea ya que el mtodo no cambia en sus
parmetros, solo la clase Criteria se vera afectada y por supuesto la query que aplique
el filtro.
Estructura de la solucin
Analicemos un poco como esta estructurado el ejemplo.
Solo son dos capas, una de acceso a datos y otro de presentacin, se realizo de esta
forma para no complicar el desarrollo del ejemplo, porque aqu no se pretenda
mostrar una arquitectura completa, sino solo apuntar a entender como aplicar filtros
de forma correcta.
Es muy importante remarcar la clase de nombre PersonCriteria, la cual actuara solo
cuando los filtros entran en accion, y la clase PersonEntity, quien representa de la
entidad de negocio.
Analicemos la diferencia entre las dos entidades:
01.public class PersonCriteria
02.{
03.public string FirstName { get; set; }
04.public string LastName { get; set; }
05.
06.public DateTime? EnrollmentDateFrom { get; set; }
07.public DateTime? EnrollmentDateTo { get; set; }
08.
09.public DateTime? HireDateFrom { get; set; }
10.public DateTime? HireDateTo { get; set; }
11.
12.public CourseEntity Curse { get; set; }
13.}
01.public class PersonEntity
02.{
03.public int PersonID {get; set;}
04.public string LastName { get; set; }
05.public string FirstName { get; set; }
06.
07.public DateTime? HireDate { get; set; }
08.public DateTime? EnrollmentDate { get; set; }
09.}
Las propiedades de cada clase tienen unas cuantas diferencias porque sus
responsabilidades y lo que representantas son diferentes.
Anlisis de la Presentacin
La pantalla que se presentara al usuario incluir varios filtros que actan de forma
combinada.
11.Curse = Convert.ToInt32(cmbCourse.SelectedValue)== -1
? null : newCourseEntity() { CourseID =
Convert.ToInt32(cmbCourse.SelectedValue) }
12.};
13.
14.dgvPersonList.DataSource = PersonDAL.Select(filter);
15.
16.}
La presentacin conoce como asignar la propiedad con el dato correcto, es porque eso
que all se observan validaciones para determinar si asignar el dato o no.
Acceso a Datos
Esta es la capa con mayor responsabilidad, lgicamente porque ser la encargada de
aplicar el filtro, pero todo se reduce a un simple truco en la query que habilita o no el
filtro en el WHERE
P.PersonID,
04.P.LastName,
05.P.FirstName,
06.P.HireDate,
07.P.EnrollmentDate
08.FROM Person P
09.LEFT JOIN CourseInstructor CI
10.ON P.PersonID = CI.PersonID
11.WHERE ((@FirstName IS NULL) OR (P.FirstName LIKE '%' + @FirstName
+ '%'))
12.AND ((@LastName IS NULL) OR (P.LastName LIKE '%' + @LastName +'%'))
13.AND ((@HireDateFrom IS NULL) OR (P.HireDate >= @HireDateFrom))
14.AND ((@HireDateTo IS NULL) OR (P.HireDate <= @HireDateTo))
15.AND ((@EnrollmentDateFrom IS NULL) OR (P.EnrollmentDate >=
@EnrollmentDateFrom))
16.AND ((@EnrollmentDateTo IS NULL) OR (P.EnrollmentDate <=
@EnrollmentDateTo))
17.AND ((@Course IS NULL) OR (CI.CourseID = @Course))";
18.
19.List<PersonEntity> list = new List<PersonEntity>();
20.
21.using (SqlConnection conn
= newSqlConnection(ConfigurationManager.ConnectionStrings["default"].ToSt
ring()))
22.{
23.conn.Open();
24.
25.SqlCommand cmd = new SqlCommand(sql, conn);
26.
27.if (string.IsNullOrEmpty(filter.FirstName))
28.cmd.Parameters.AddWithValue("@FirstName", DBNull.Value);
29.else
30.cmd.Parameters.AddWithValue("@FirstName",
filter.FirstName);
31.
32.cmd.Parameters.AddWithValue("@LastName",string.IsNullOrEmpty(filter.Fi
rstName) ? (object)DBNull.Value : filter.LastName);
33.
34.cmd.Parameters.AddWithValue("@HireDateFrom",
filter.HireDateFrom.HasValue ? filter.HireDateFrom.Value.Date :
(object)DBNull.Value);
35.cmd.Parameters.AddWithValue("@HireDateTo", filter.HireDateTo.HasValue
? filter.HireDateTo.Value.Date : (object)DBNull.Value);
36.
37.cmd.Parameters.AddWithValue("@EnrollmentDateFrom",
filter.EnrollmentDateFrom.HasValue ? filter.EnrollmentDateFrom.Value.Date
: (object)DBNull.Value);
38.cmd.Parameters.AddWithValue("@EnrollmentDateTo",
filter.EnrollmentDateTo.HasValue ? filter.EnrollmentDateTo.Value.Date :
(object)DBNull.Value);
39.
40.cmd.Parameters.AddWithValue("@Course", filter.Curse == null ?
(object)DBNull.Value: filter.Curse.CourseID);
41.
42.
43.SqlDataReader reader = cmd.ExecuteReader();
44.
45.while (reader.Read())
46.{
47.list.Add(LoadPerson(reader));
48.}
49.
50.return list;
51.}
52.
53.}
Analicemos una seccin pequea para entenderlo:
((@FirstName IS NULL) OR (P.FirstName LIKE '%' + @FirstName + '%'))
en esta se tienen dos parte, la primera compara el parmetro con NULL, por lo tanto si
desde el cdigo .net enviamos un DbNull.Value estaramos anulando este filtro, porque
esta comparacin aplicara siempre para todos los registros.
En la segunda mitad ser donde el filtro se aplica, este al tener un valor no se vera
afectado por la primer seccin.
Adems quise mostrar que existen varias formas desde cdigo de asignar el
parmetro, es por eso que la lnea 27 la asignacin del parmetro se efecta con un if
completo, mientras que el resto lo hace en sola linea, esto fue solo para remarcar que
no hay una nica forma de asignar el parmetro, si la comparacin en una lnea queda
compleja (o poco clara) se puede pasar a varias lneas.
Este es todo el truco, no es nada difcil de implementar, y conocerlo puede ayudar
brindar al usuario filtros mas potentes que agreguen valor a la aplicacin.
Ejemplo de Cdigo
La base de datos fue creada con Sql Server Express 2008 R2, en caso de tener
problemas con al misma en el proyecto DataAccess esta la carpeta script con el .sql
que puede usar para crear estructura y datos.
[C#]
[VB.NET]
Introduccin
En mas de una oportunidad surgi el hecho de comunicar formulario y pasar
informacin entre los mismos, cuando estos se invocan uno a otra la solucin es
relativamente simple, pero que sucede cuando las distintas invocaciones deben
capturar y conservar los datos en cada paso que realizan.
Es aqu donde entra en juego un patrn bien simple de aplicar pero no por eso menos
potente, hago referencia al singleton.
Para el presente artculo platearemos un escenario nada complicado, simplemente
algunos forms donde se capturan datos nmeros, pero esta informacin deber ser
usada en la ultima pantalla, la cual realiza el calculo con los datos recolectados en los
pasos previos.
04.
05.private RecolectarDatos()
06.{
07.}
08.
09.public static RecolectarDatos Instance()
10.{
11.if (datos == null)
12.{
13.datos = new RecolectarDatos();
14.}
15.
16.return datos;
17.}
18.
19.public int Dato1 { get; set; }
20.public int Dato2 { get; set; }
21.public int Dato3 { get; set; }
22.
23.public int Dato4 { get; set; }
24.public int Dato5 { get; set; }
25.}
El primer punto es definir una instancia static del propia clase singleton (lnea 3)
Segundo, definir el constructor de la clase como privado (lnea 5), de esta forma no se
podrn crear instancia de la clase, salvo desde dentro de la propia clase.
Tercero, definir un mtodo o propiedad static, la cual ser el punto central por donde
se podr recuperar una instancia de la clase (lnea 9), en este punto es donde se valida
y crea la instancia de la variable static definida mas arriba, es por esto que siempre se
retorna esta misma instancia.
El resto del cdigo pueden ser mtodo, propiedades, otras instancias de clases, o sea
se puede aplicar cualquier concepto de POO sin problemas, porque el patron singleton
devuelve una nica instancia, pero esta tiene comportamiento como cualquier otro
objeto.
Al llegar al ultimo formulario era necesario informar al primero que las operaciones se
realizaron correctamente, pero entre estos formulario no hay visibilidad alguna, no se
aplico ninguna pasamano de instancias entre los formularios, o algo que los
comunique.
Es por eso que la subscripcin a un evento genrico resulta ideal para avisar de una
determinada accin sin ser necesario pasar instancias.
[C#]
[VB.NET]
Introduccin
En el presente artculo se implementa un ejemplo sencillo de como listar directorios
con su contenido, incluyendo adems el icono asociado a la extensin del archivo.
Algunas pruebas
Si bien durante las primeras pruebas se hizo uso de la funcionalidad
Icon.ExtractAssociatedIcon
para obtener la imagen relacionada con la extensin del archivo, note que no siempre
se recuperaba el icono correcto, es por eso que investigando algo mas en detalle
encontr que por medio de las api se puede realizar esto mismo.
La implementacin de esta se puede hallar en el archivo ExtractIcon.cs
Si bien en el cdigo solo he dejado una de estas alternativas
01.private void LoadFileList(string path)
02.{
03.string[] files = Directory.GetFiles(path);
04.
05.lvFiles.Items.Clear();
06.
07.foreach (var item in files)
08.{
09.string extension = Path.GetExtension(item);
10.
11.if (!imlSmall.Images.ContainsKey(extension))
12.{
13.Icon iconSmall = FileExplorer.ExtractIconClass.GetIcon(item,true);
14.imlSmall.Images.Add(extension, iconSmall);
15.Icon iconLarge = FileExplorer.ExtractIconClass.GetIcon(item,false);
16.imlLarge.Images.Add(extension, iconLarge);
17.}
18.
19.ListViewItem listviewItem = newListViewItem(Path.GetFileName(item),
extension);
20.lvFiles.Items.Add(listviewItem);
21.
22.}
23.}
podran cambiarse las lneas 13 a 16 para hacer uso del
Icon.ExtractAssociatedIcon()
y comprobar por uno mismo la diferencia en el icono que se obtiene.
Ejemplo de cdigo
[C#]
[VB.NET]
Introduccin
explico como destrabar esta situacin para poder as implementarlo como lo haran en
c#, definiendo el Main() como inicio de la aplicacin.
[VB.NET]
01.<STAThread()> _
02.Friend Shared Sub Main()
03.
04.Application.EnableVisualStyles()
05.Application.SetCompatibleTextRenderingDefault(False)
06.
07.AddHandler Application.ThreadException, AddressOf Application_ThreadExc
eption
08.
09.Application.Run(New Form1())
10.
11.End Sub
12.
13.Private Shared Sub Application_ThreadException(ByVal sender As Object, B
yVal e AsSystem.Threading.ThreadExceptionEventArgs)
14.
15.MessageBox.Show(e.Exception.Message)
16.
17.End Sub
El cdigo en ambos lenguajes preserva el mismo concepto, pero la forma en como se
adjunta el evento difiera bastante.
Entonces, ahora si sucediera algo como lo reflejado en la imagen
Si bien en una primer instancia hacer uso de un mensaje podra ayudar a detectar el
problema en la aplicacin, este podra evolucionar en un log a un archivo para dejar
tracking de lo sucedido, en este caso no solo se pondra el mensaje del problema, sino
que adems se podra agregar el StackTrace para poder analizar que mtodos se
fueron ejecutando hasta causar el fallo.
En este caso el log a un archivos ser muy simple
[C#]
01.static void Application_ThreadException(object sender,
System.Threading.ThreadExceptionEventArgs e)
02.{
03.using(FileRecordSequence record
= newFileRecordSequence("application.log", FileAccess.Write))
04.{
05.
06.string message = string.Format("[{0}]Message::{1} StackTrace:: {2}",
DateTime.Now,
07.e.Exception.Message,
08.e.Exception.StackTrace);
09.
10.record.Append(CreateData(message), SequenceNumber.Invalid,
11.SequenceNumber.Invalid,
12.RecordAppendOptions.ForceFlush);
13.}
14.}
15.
16.
17.private static IList<ArraySegment<byte>> CreateData(string str)
18.{
19.Encoding enc = Encoding.Unicode;
20.
21.byte[] array = enc.GetBytes(str);
22.
23.ArraySegment<byte>[] segments = new ArraySegment<byte>[1];
24.segments[0] = new ArraySegment<byte>(array);
25.
26.return Array.AsReadOnly<ArraySegment<byte>>(segments);
27.}
[VB.NET]
01.Private Shared Sub Application_ThreadException(ByVal sender As Object, B
yVal e AsSystem.Threading.ThreadExceptionEventArgs)
02.
03.Using record As New FileRecordSequence("application.log",
FileAccess.Write)
04.
05.Dim message As String = String.Format("[{0}]Message::{1} StackTrace::
{2}", DateTime.Now, e.Exception.Message, e.Exception.StackTrace)
06.
07.record.Append(CreateData(message), SequenceNumber.Invalid,
SequenceNumber.Invalid, RecordAppendOptions.ForceFlush)
08.End Using
09.
10.End Sub
11.
12.
13.Private Shared Function CreateData(ByVal str As String) As IList(Of
ArraySegment(OfByte))
14.
15.Dim enc As Encoding = Encoding.Unicode
16.
17.Dim _array As Byte() = enc.GetBytes(str)
18.
19.Dim segments As ArraySegment(Of Byte)() = New ArraySegment(Of Byte)(0)
{}
20.segments(0) = New ArraySegment(Of Byte)(_array)
21.
22.Return Array.AsReadOnly(Of ArraySegment(Of Byte))(segments)
23.
24.End Function
Ahora el evento global de errores tiene bastante mas cdigo, en donde se arma un
mensaje bastante mas til, el cual ser enviado a la funcionalidad de log para registrar
el suceso.
Para recuperar el archivo, solo deben ir a la ubicacin donde esta el .exe, en este caso
debera esta en la carpeta \bin\Debug del proyecto.
Lo mas probable es que el archivo no se legible a simple vista porque este sistema de
log trabaja con el concepto de entradas de registros, es por eso que se confecciono un
formulario muy simple para poder recuperar la informacin de forma visual.
[C#]
[VB.NET]
Introduccin
El objetivo de este articulo ser reflejar los siguiente puntos:
- Grabar una imagen en un campo de una base de datos
- Recuperar la imagen visualizndola en un celda del DataGridView y en un PictureBox
- Comunicar un formulario pasndole informacin en el constructor para la edicin de
una entidad
- Detectar el cierre del formulario para actualizar la informacin de los empleados
Dominio de la aplicacin
Bien empezaremos definiendo al estructura de datos de la aplicacin.
El dominio consiste en Empleados, con informacin bsica del mismo, mas un campo
de imagen el cual es opcional por lo cual esta marcado para permitir nulos.
El atributo Estado Civil tomara los valores de 1 a 4, los cuales fueron definido en un
enumerado.
Adicionalmente el empleado podr tener Estudios realizados, los cuales se asignan
marcando de una lista.
El diseo de la interfaz es simple, un formulario principal que lista los usuarios.
Y otro que edita el usuario cuando se realiza un doble click en la fila de la lista.
comunicacin son entidades definidas por medio de clases que modelan el dominio de
la aplicacin.
Si bien el esquema de estas entidades son muy parecido al de la base de datos ,se
debe apreciar que la entidad Empleado posee una relacin con la de estudios, lo cual
posibilita la navegacin de la informacin asociada.
Cargar lista de empleados (con imgenes)
En esta seccin analizaremos como cargar las imgenes en el DataGridView, tomando
esta informacin desde la base de datos.
El primer paso ser recuperar los registros de la tabla, incluyendo la imagen.
[C#]
01.public static List<EmpleadoEntity> ObtenerTodos()
02.{
39.if (cargarRelaciones)
40.{
41.empleado.Estudios =
EstudiosDAL.ObtenerAsignadoEmpleado(empleado.IdEmpleado);
42.}
43.
44.return empleado;
45.}
[VB.NET]
01.Public Shared Function ObtenerTodos() As List(Of EmpleadoEntity)
02.
03.Dim lista As New List(Of EmpleadoEntity)()
04.
05.Using conn As New
SqlConnection(ConfigurationManager.ConnectionStrings("default").ToString(
))
06.conn.Open()
07.
08.Dim query As String = "SELECT IdEmpleado, Nombre, Apellido,
FechaNacimiento, EstadoCivil, Imagen" & _
09."FROM Empleados"
10.
11.Dim cmd As New SqlCommand(query, conn)
12.
13.Dim reader As SqlDataReader = cmd.ExecuteReader()
14.
15.While reader.Read()
16.lista.Add(ConvertirEmpleado(reader, False))
17.End While
18.
19.End Using
20.
21.Return lista
22.
23.End Function
24.
25.
26.Private Shared Function ConvertirEmpleado(ByVal reader As IDataReader,
ByVal cargarRelaciones As Boolean) As EmpleadoEntity
27.
28.Dim empleado As New EmpleadoEntity()
29.
30.empleado.IdEmpleado = Convert.ToInt32(reader("IdEmpleado"))
31.empleado.Nombre = Convert.ToString(reader("Nombre"))
32.empleado.Apellido = Convert.ToString(reader("Apellido"))
33.empleado.FechaNacimiento =
Convert.ToDateTime(reader("FechaNacimiento"))
34.empleado.EstadoCivil = Convert.ToInt16(reader("EstadoCivil"))
35.
36.If reader("Imagen") IsNot DBNull.Value Then
37.empleado.Imagen = DirectCast(reader("Imagen"), Byte())
38.End If
39.
40.If cargarRelaciones Then
41.empleado.Estudios =
EstudiosDAL.ObtenerAsignadoEmpleado(empleado.IdEmpleado)
42.End If
43.
44.Return empleado
45.
46.End Function
Es preciso notar como se asigna la imagen a la entidad
[C#]
1.if (reader["Imagen"] != DBNull.Value)
2.empleado.Imagen = (byte[])reader["Imagen"];
[VB.NET]
1.If reader("Imagen") IsNot DBNull.Value Then
2.empleado.Imagen = DirectCast(reader("Imagen"), Byte())
3.End If
lo cual resulta muy simple.
En el formulario ListaEmpleados se usa esta informacin para cargar el DataGridView
[C#]
16.End If
17.Next
18.
19.End Sub
seguramente una pregunta que se hagan es porque en este mtodo se esta
recorriendo las filas del DataGridView despus de asignar los datos al mismo ?
bien esto tiene una explicacin, resulta que la celda de imagen del DataGridView
requiere un tipo Image para poder trabajar, pero la entidad que proporcionamos
devuelve un array de byte, lo cual no es compatible, entonces es que se aprovecha la
capacidad del DataGridView para retornar la entidad que bindea a cada fila para
obtener la entidad Empleado y convertir el array de byte al tipo Image.
Tambin debo aclara que esto se intento hacer en un evento, como ser el
CellFormatting, pero resulto un desastre porque este evento repintaba constantemente
la informacin generando un parpadeo molesto en el control, se podra haber realizado
esto mismo en el evento DataBindingComplete, pero para este caso no vala la pena
separa la funcionalidad, por eso se dejo all mismo.
En ese mtodo se hace uso del la funcionalidad de helper que no es mas que un
grupo de funciones que facilitan ciertas tareas, en este caso convierten el array de
byte en Image.
Comunicar Formulario y detectar cierre del form hijo
Al realizar un doble click en la celda del DataGridView este entra en modo edicin, esto
implica simplemente toma la informacin y pacrsela al formulario
Desde ListaEmpleados se trabaja con el evento tomando solo el id del empleado, y
en la instancia del formulario se lo pasa en el constructor. Tambin en la misma
operacin se adjunta el evento FormClosing para poder detectar el cierre el form y
proceder a la actualizacin del la lista de empleados.
[C#]
01.private void dgvEmpleados_CellContentDoubleClick(object sender,
DataGridViewCellEventArgs e)
02.{
03.int IdEmpleado =
Convert.ToInt32(dgvEmpleados.Rows[e.RowIndex].Cells["IdEmpleado"].Value);
04.
05.//
06.// al pasarle un id de empleado este lo cargara para su edicion
07.//
08.EditarEmpleado frmEditar = new EditarEmpleado(IdEmpleado);
09.frmEditar.FormClosing
+= new FormClosingEventHandler(frmEditar_FormClosing);
10.
11.frmEditar.Show();
12.}
13.
14.void frmEditar_FormClosing(object sender, FormClosingEventArgs e)
15.{
16.//
17.// al cerrarse el form de edicion se ingresa a este evento
18.// para actualizar la informacion del listado
19.//
20.
21.EditarEmpleado frmEdit = sender as EditarEmpleado;
22.
23.if(frmEdit.DialogResult == DialogResult.OK)
24.CargarListaEmpleados();
25.
26.}
[VB.NET]
01.Private Sub dgvEmpleados_CellContentDoubleClick(ByVal sender As Object,
ByVal e AsDataGridViewCellEventArgs)
02.
03.Dim IdEmpleado As Integer =
Convert.ToInt32(dgvEmpleados.Rows(e.RowIndex).Cells("IdEmpleado").Value)
04.
05.'
06.' al pasarle un id de empleado este lo cargara para su edicion
07.'
08.Dim frmEditar As New EditarEmpleado(IdEmpleado)
09.AddHandler frmEditar.FormClosing, New FormClosingEventHandler(AddressOf
frmEditar_FormClosing)
10.
11.frmEditar.Show()
12.
13.End Sub
14.
15.Private Sub frmEditar_FormClosing(sender As Object,
e As FormClosingEventArgs)
16.'
17.' al cerrarse el form de edicion se ingresa a este evento
18.' para actualizar la informacion del listado
19.'
20.
21.Dim frmEdit As EditarEmpleado = TryCast(sender, EditarEmpleado)
22.
23.If frmEdit.DialogResult = DialogResult.OK Then
24.CargarListaEmpleados()
25.End If
26.
27.End Sub
En el formulario que recibe esta informacin EditarEmpleado solo utiliza el id del
empleado, porque con este ira a la db y recuperara la entidad para cargar el
formulario, no hace falta enviar la entidad completa, solo la mnima informacin como
para que este form pueda recuperar el resto.
[C#]
01.public partial class EditarEmpleado : Form
02.{
03.private int? _idEmpleado = null;
04.
05.public EditarEmpleado()
06.{
07.InitializeComponent();
08.}
09.
10.public EditarEmpleado(int idEmpleado)
11.: this()
12.{
13._idEmpleado = idEmpleado;
14.}
15.
16..
17..
18..
19.}
[VB.NET]
12.
13.this.DialogResult = DialogResult.OK;
14.this.Close();
15.}
16.
17.private void btnCancelar_Click(object sender, EventArgs e)
18.{
19.this.DialogResult = DialogResult.Cancel;
20.this.Close();
21.}
[VB.NET]
01.Private Sub btnGuardar_Click(sender As Object, e As EventArgs)
02.
03..
04..
05..
06.
07.'
08.' se graba
09.'
10.EmpleadosDAL.Save(empleado)
11.
12.Me.DialogResult = DialogResult.OK
13.Me.Close()
14.
15.End Sub
16.
17.Private Sub btnCancelar_Click(ByVal sender As Object, ByVal e As EventArg
s)
18.
19.Me.DialogResult = DialogResult.Cancel
20.Me.Close()
21.
22.End Sub
Editar Empleado (mostrando la imagen en un Picturebox)
Esta tarea es bastante simple, solo implica recuperar una entidad en concreto haciendo
uso de la cada de datos y cargar la entidad en los controles para presentar esta al
usuario.
[C#]
01.public static EmpleadoEntity ObtenerById(int idEmpleado)
02.{
03.EmpleadoEntity empleado = null;
04.
05.using (SqlConnection conn
= newSqlConnection(ConfigurationManager.ConnectionStrings["default"].ToSt
ring()))
06.{
07.conn.Open();
08.
09.string query = @"SELECT IdEmpleado, Nombre, Apellido, FechaNacimiento,
EstadoCivil, Imagen
10.FROM Empleados WHERE IdEmpleado = @idEmpleado";
11.
12.SqlCommand cmd = new SqlCommand(query, conn);
13.cmd.Parameters.AddWithValue("@idEmpleado", idEmpleado);
14.
15.SqlDataReader reader = cmd.ExecuteReader();
16.
17.if (reader.Read())
18.{
19.empleado = ConvertirEmpleado(reader, true);
20.}
21.
22.}
23.
24.return empleado;
25.}
[VB.NET]
01.Public Shared Function ObtenerById(ByVal idEmpleado As Integer) As Emplea
doEntity
02.
03.Dim empleado As EmpleadoEntity = Nothing
04.
05.Using
conn As NewSqlConnection(ConfigurationManager.ConnectionStrings("default")
.ToString())
06.conn.Open()
07.
08.Dim query As String = "SELECT IdEmpleado, Nombre, Apellido,
FechaNacimiento, EstadoCivil, Imagen " & _
09."FROM Empleados WHERE IdEmpleado = @idEmpleado"
10.
11.Dim cmd As New SqlCommand(query, conn)
12.cmd.Parameters.AddWithValue("@idEmpleado", idEmpleado)
13.
14.Dim reader As SqlDataReader = cmd.ExecuteReader()
15.
16.If reader.Read() Then
17.empleado = ConvertirEmpleado(reader, True)
18.End If
19.
20.End Using
21.
22.Return empleado
23.
24.End Function
Una vez que se tiene la entidad solo se carga esta en los contoles.
[C#]
01.private void EditarEmpleado_Load(object sender, EventArgs e)
02.{
03.CargarEstadoCivil();
04.CargarEstudio();
05.
06.//por defecto se carga una imagen de no disponible
07.picImagenEmpleado.Image = ImageHelper.ObtenerImagenNoDisponible();
08.
09.//
10.// se carga la informacion del empleado que se quiere editar
11.//
12.if (_idEmpleado.HasValue)
13.{
26.End If
27.
28.'
29.' Se obtienen los estudios del empleado
30.'
31.
32.AsignarEstudios(empleado)
33.End If
34.
35.
36.End Sub
37.
38.Private Sub CargarEstadoCivil()
39.
40.cbEstadoCivil.DisplayMember = "Descripcion"
41.cbEstadoCivil.ValueMember = "IdEstadoCivil"
42.cbEstadoCivil.DataSource = EstadoCivilDAL.ObtenerTodos()
43.
44.End Sub
45.
46.Private Sub CargarEstudio()
47.dgvEstudios.AutoGenerateColumns = False
48.dgvEstudios.DataSource = EstudiosDAL.ObtenerTodos()
49.End Sub
50.
51.Private Sub AsignarEstudios(empleado As EmpleadoEntity)
52.
53.Dim rows As List(Of DataGridViewRow) = (From
row In dgvEstudios.Rows.Cast(Of DataGridViewRow)() _
54.Let idEstudio = Convert.ToInt32(row.Cells("IdEstudio").Value) _
55.Join dd In empleado.Estudios OnidEstudio Equals dd.IdEstudio _
56.Select row).ToList()
57.
58.
59.'rows.ForEach(Function(o) o.Cells("Seleccion").Value = True)
60.rows.ForEach(Function(o)
InlineAssignHelper(o.Cells("Seleccion").Value,True))
61.
62.End Sub
08.End If
09.
10.End Sub
Persistir la edicin
La operacin de persistir los datos o guardarlos requiere dos etapas, una implica tomar
la informacin de los controles y generar los objetos de dominio que la capa de datos
requiere para llevar a cabo la actualizacin en la base de datos, la segunda ser
simplemente procesar la entidad generando los INSERT y UPDATE necesario.
[C#]
01.private void btnGuardar_Click(object sender, EventArgs e)
02.{
03.//
04.// Se crea la entidad
05.//
06.EmpleadoEntity empleado = new EmpleadoEntity()
07.{
08.IdEmpleado = _idEmpleado.GetValueOrDefault(),
09.Nombre = txtNombre.Text,
10.Apellido= txtApellido.Text,
11.FechaNacimiento = dtpFechaNacimiento.Value,
12.EstadoCivil = Convert.ToInt16(cbEstadoCivil.SelectedValue),
13.Imagen = ImageHelper.ImageToByteArray(picImagenEmpleado.Image)
14.};
15.
16.//
17.// Se asignan los estudios seleccionados, se unicializa la lista
18.// para cargar la selecciond el usuario
19.//
20.empleado.Estudios = new List<EstudioEntity>();
21.
22.IEnumerable<DataGridViewRow> rowsSelected =
dgvEstudios.Rows.Cast<DataGridViewRow>()
23..Where(o => Convert.ToBoolean(o.Cells["Seleccion"].Value));
24.
25.foreach (DataGridViewRow row in rowsSelected)
26.{
27.EstudioEntity estudio = new EstudioEntity()
28.{
29.IdEstudio = Convert.ToInt32(row.Cells["IdEstudio"].Value)
30.};
31.
32.empleado.Estudios.Add(estudio);
33.}
34.
35.//
36.// se graba
37.//
38.EmpleadosDAL.Save(empleado);
39.
40.this.DialogResult = DialogResult.OK;
41.this.Close();
42.}
[VB.NET]
01.Private Sub btnGuardar_Click(sender As Object, e As EventArgs)
02.'
03.' Se crea la entidad
04.'
05.Dim empleado As New EmpleadoEntity() With { _
06..IdEmpleado = _idEmpleado.GetValueOrDefault(), _
07..Nombre = txtNombre.Text, _
08..Apellido = txtApellido.Text, _
09..FechaNacimiento = dtpFechaNacimiento.Value, _
10..EstadoCivil = Convert.ToInt16(cbEstadoCivil.SelectedValue), _
11..Imagen = ImageHelper.ImageToByteArray(picImagenEmpleado.Image) _
12.}
13.
14.'
15.' Se asignan los estudios seleccionados, se unicializa la lista
16.' para cargar la selecciond el usuario
17.'
18.empleado.Estudios = New List(Of EstudioEntity)()
19.
20.Dim rowsSelected As IEnumerable(Of DataGridViewRow) =
dgvEstudios.Rows.Cast(Of DataGridViewRow)().Where(Function(o)
Convert.ToBoolean(o.Cells("Seleccion").Value))
21.
22.For Each row As DataGridViewRow In rowsSelected
23.
24.Dim estudio As New EstudioEntity() With { _
25..IdEstudio = Convert.ToInt32(row.Cells("IdEstudio").Value) _
26.}
27.
28.empleado.Estudios.Add(estudio)
29.
30.Next
31.
32.'
33.' se graba
34.'
35.EmpleadosDAL.Save(empleado)
36.
37.Me.DialogResult = DialogResult.OK
38.Me.Close()
39.
40.End Sub
Es preciso remarcar como en la entidad Empleado es cargada la lista de Estudios que
este posee y solo una entidad es enviada desde la Presentacin a la capa de datos.
Durante esta operacin tambin se toma la imagen del PictureBox y se convierte en un
array de byte.
La actualizacin realiza varias operaciones las cuales son muy extensas en cdigo, por
lo tanto no ser puesta directa en el articulo, pero si podrn verse en el cdigo de
ejemplo que puede descargarse.
Cdigo de Ejemplo
La base de datos utilizada en el ejemplo es la Sql Server Express 2008 R2, como ver
en la solucin el mdf esta integrado al Visual Studio, por lo tanto con solo tener el sql
server express instado esta debera funciona adjuntndose sola al servicio.
En la carpeta script del proyecto DataAccess se encuentra un archivo .sql con las
instrucciones para crear la estructura de tablas y datos que se requieren para este
articulo.
[C#]
[VB.NET]