100% encontró este documento útil (1 voto)
418 vistas70 páginas

Comunicación entre formularios en WinForms

Correcciones Geodésicas Sobre Trabajos Topográficos,Correcciones Geodésicas Sobre Trabajos Topográficos
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd
100% encontró este documento útil (1 voto)
418 vistas70 páginas

Comunicación entre formularios en WinForms

Correcciones Geodésicas Sobre Trabajos Topográficos,Correcciones Geodésicas Sobre Trabajos Topográficos
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como DOCX, PDF, TXT o lee en línea desde Scribd

WinForm

Introduccin

Existen varias formas pasar informacin entre formularios, especialmente si queremos


hacerlo sin generar una fuerte dependencia entre estos.
Este mismo tema lo haba abordado en artculos anteriores por lo que retomaremos el
anlisis de las distintas formas de lograr la comunicacin sin generar dependencia
entre los forms
Los artculos anteriores en los cuales he tratado el tema:
Comunicar formularios de forma desacoplada
Comunicar formularios MDI
Para ejemplificar haremos uso de un form que permite listar y editar productos de la
base de datos Northwind.

Analizaremos 3 formas de comunicar formularios y enviar datos entre ellos:

Usando Interfaces

Usando eventos

Usando el evento FormClosed

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 primer paso ser definir la interfaz

siendo implementada por el formulario padre receptora de la informacin seleccionada


por el usuario

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.

Desde el form hijo (frmFindSupplier) se define el constructor que recibir los


parmetro

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.

Primero se valida que exista seleccin en el grid, se recupera la entidad seleccionada


pasndola como parametro al mtodo Selected() definido en la interfaz, por ultimo se
cierra el formulario. El form padre recibe el valor seleccionado y actualiza los controles
o entidades que hagan falta.
La secuencia completa quedara de esta forma

1. El form padre (frmEditProductos) implementa la interfaz.


2. En el botn de bsqueda se crea la instancia del form hijo (frmFindSupplier),
pasando como parmetro en el constructor la instancia del forma padre
(utilizando this)
3. El form hijo recibe en el constructor los valores por parmetro
4. Se selecciona un tem del grid y se invoca al mtodo de la interfaz
5. El form padre recibe la seleccin y actualiza los datos
Como ventaja se puede mencionar que cualquier form que implemente la interfaz
podr reutilizar la funcionalidad de bsqueda implementada al no quedar acoplados los
formularios que interactuan.

Usando eventos

Permite enviar una accin directa al form que se subscriba al evento.


El ejemplo va a estar representado en la comunicacin del form de edicin de
productos y la bsqueda de categoras.

Partamos analizando la implementacin del form hijo (frmFindCategory), este expone


el evento, que es lanzado cuando se selecciona una categora del grid

Para definir datos especficos en el intercambio de informacin se crea una clase que
hereda de EventArgs

Desde el form padre (frmEditProductos) simplemente se debe adjuntar al evento la


instancia del form hijo

Repasemos como seria la ejecucin en secuencia

1. El form hijo expone el evento, utilizado para informar la seleccin a quien lo


haya invocado.
2. Se define una clase que hereda de EventArgs para poder pasar informacin
adicional como argumento del evento.
3. El form padre que realiza la bsqueda crea la instancia del forma hijo y se
adjunta al evento, adems crea el mtodo que recibir la accin.
4. Cuando se selecciona un tem del grid se valida si alguien esta adjunto en el
evento y se invoca.

Actualizar los datos al cerrar el form (evento FormClosed)

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

Analicemos los pasos

1. En el form padre (frmAdminProducts) instancia el form hijo (frmEditProductos)


y se adjunta el evento de cierre del form
2. Cuando se presiona el boton de aceptar o cancelar se devuelve el DialogResult
cerrando el form, esto lanza el evento FormClosed
3. Desde el form padre se atrapa el evento y se realiza la recarga de los datos del
grid

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

domingo, 17 de febrero de 2013

[Winform] Instanciar Form conociendo su nombre

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.

por ejemplo para abrir el form de alta de cliente se identifica

pero si otro form se encuentra dentro de una carpeta es necesario definir el


namespace donde se encuentra

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;

private void btnAbrirForm_Click(object sender, EventArgs e)


{
Assembly asm = Assembly.GetEntryAssembly();
Type formtype = asm.GetType(string.Format("{0}.{1}", txtNamespace.Text,
txtNombreForm.Text));
Form f = (Form)Activator.CreateInstance(formtype);
f.Show();
}
Se toma el assembly donde se encuentran los formularios y se obtiene el tipo, con el
cual se creara la instancia dinmicamente
Cdigo

[C#]

Publicado por Leandro Tuttini en 6:38 3 comentarios:


Etiquetas: WinForm

domingo, 10 de febrero de 2013

[WinForms] Verificar si el form esta abierto (instancia nica)

Introduccin

Determinar si un form ha sido abierto, evitando que una nueva instancia sea creada.

Validar instancia entorno SDI

Para validar la existencia de la instancia de un formulario se har uso


delApplication.OpenForms, con este y la ayuda de linq podremos buscar la existencia
del form abierto.

La seleccin de botn abrir una instancia, pero las siguientes pulsaciones solo harn
que el mismo form pase al frente.

private void btnAbrirFormclientes_Click(object sender, EventArgs e)


{
//se localiza el formulario buscandolo entre los forms abiertos
Form frm = Application.OpenForms.Cast<Form>().FirstOrDefault(x =>
x isfrmEdicionCliente);
if (frm != null)
{
//si la instancia existe la pongo en primer plano
frm.BringToFront();
return;
}
//sino existe la instancia se crea una nueva
frm = new frmEdicionCliente();
frm.Show();
}
El truco esta en poder determinar la lista de forma abiertos

Validar instancia entorno MDI

En un entorno MDI la validacin es similar solo cambia la propiedad usada para


determinar la lista de forms abiertos

private void abrirFormClienteToolStripMenuItem_Click(object sender,


EventArgs e)
{
//se localiza el formulario buscandolo entre los forms abiertos
Form frm = this.MdiChildren.FirstOrDefault(x => x is frmEdicionCliente);
if (frm != null)
{
//si la instancia existe la pongo en primer plano
frm.BringToFront();
return;
}
//se abre el form de clientes
frm = new frmEdicionCliente();
frm.MdiParent = this;
frm.Show();
}

Cdigo

Publicado por Leandro Tuttini en 15:02 No hay comentarios:


Etiquetas: WinForm

domingo, 3 de febrero de 2013

[WinForms] Listar formulario del proyecto

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.

private void frmPrincipal_Load(object sender, EventArgs e)


{
Assembly asm = Assembly.GetExecutingAssembly();
List<Form> formList = asm.GetTypes()
.Where(x => x.IsSubclassOf(typeof(Form)))
.Select(x => (Form)Activator.CreateInstance(x))
.ToList();

lstForms.DisplayMember = "Text";
lstForms.DataSource = formList;
}
Tambin se puede realizar la misma tarea sin hacer uso de linq

private void frmPrincipal_Load(object sender, EventArgs e)


{
Assembly asm = Assembly.GetExecutingAssembly();
List<Form> formList = new List<Form>();
foreach (Type item in asm.GetTypes())
{
if (item.IsSubclassOf(typeof(Form)))
{
formList.Add((Form)Activator.CreateInstance(item));
}
}
lstForms.DisplayMember = "Text";

lstForms.DataSource = formList;
}
Cdigo

Publicado por Leandro Tuttini en 11:57 2 comentarios:


Etiquetas: WinForm

jueves, 17 de mayo de 2012

[Winforms] TreeView Cambiar Estado Node con CheckBox

Introduccin

Muchas veces es necesario cambiar el estado de tems predecesores o hijo segn


ciertos criterios, el control TreeView por posee una estructura jerrquica en sus datos
es un caso de esta situacin.

Validar estado de los nodos

En la implementacin de cdigo que se puede observar aplica conceptos simples como


de validacin de estados de los nodos del treeview.
Bsicamente consiste en dos parte

validar si el nodo tiene un nodo padre, para lo cual ser necesario verificar si
todos los hijos estn marcados

cambiar el estado a todos los hijos segn la marca de este nodo

Estas acciones se realizan sobre el mismo evento ya que implica de forma contextual
al nodo que interviene en la accin

private void tvCategoryproducts_AfterCheck(object sender,


TreeViewEventArgs e)
{
//
// Se remueve el evento para evitar que se ejecute nuevamente por accion
de cambio de estado
// en esta operacion
//
tvCategoryproducts.AfterCheck -= tvCategoryproducts_AfterCheck;
//
// Se valida si el nodo marcado tiene presedente
// en caso de tenerlo se debe evaluar los nodos al mismo nivel para
determinar si todos estan marcados,
// si lo estan se marca tambien el nodo padre
//
if (e.Node.Parent != null)
{
bool result = true;
foreach (TreeNode node in e.Node.Parent.Nodes)
{
if (!node.Checked)
{
result = false;
break;
}
}
e.Node.Parent.Checked = result;
}
//
// 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)
{
foreach (TreeNode node in e.Node.Nodes)
{

node.Checked = e.Node.Checked;
}
}

tvCategoryproducts.AfterCheck += tvCategoryproducts_AfterCheck;
}

Optimizacin usando Linq

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

private void tvCategoryproducts_AfterCheck_UsingLinq(object sender,


TreeViewEventArgs e)
{
//
// Se remueve el evento para evitar que se ejecute nuevamente por accion
de cambio de estado
// en esta operacion
//
tvCategoryproducts.AfterCheck -= tvCategoryproducts_AfterCheck_UsingLinq;
//
// Se valida si el nodo marcado tiene presedente
// en caso de tenerlo se debe evaluar los nodos al mismo nivel para
determinar si todos estan marcados,
// si lo estan se marca tambien el nodo padre
//
if (e.Node.Parent != null)
{
e.Node.Parent.Checked = !e.Node.Parent.Nodes.Cast<TreeNode>().Any(x =>
!x.Checked);

}
//
// 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

Se utilizo Visual Studio 2010 y Sql Compect 3.5

[C# SkyDrive]
Publicado por Leandro Tuttini en 21:51 16 comentarios:
Etiquetas: WinForm

sbado, 17 de septiembre de 2011

Filtros Condicionales (2/2) Implementar filtros mltiples

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

1- Comunicacin entre formularios


En el formulario de bsqueda (frmBusqueda) encontrar un cdigo como el siguiente

01.private void btnBuscarCursos_Click(object sender, EventArgs e)


02.{
03.List<CourseEntity> selectedCourses =
txtCursos.Tag as List<CourseEntity>;
04.
05.using (frmSeleccionarCursos frmcursos
= newfrmSeleccionarCursos(selectedCourses))
06.{
07.if (frmcursos.ShowDialog(this) == DialogResult.OK)
08.{
09.txtCursos.Tag = frmcursos.CursosSeleccionados;
10.txtCursos.Text = string.Join(", ",
frmcursos.CursosSeleccionados.Select(x => x.Title).ToArray());
11.}
12.}
13.}
Como frmSeleccionarCursos se abre de forma modal puede esperarse en el
ShowDialog() hasta tanto el Form sea cerrado, cuando esta operacin se lleve a cabo y
se detecte la aceptacin satisfactoria del Form se procede a tomar los cursos
seleccionados y asignarlos al control que contendr la informacin.
En este caso se hace uso de la propiedad Tag del Textbox para mantener la
informacin seleccionada
En el formulario de seleccin dispone de una propiedad para que el formulario que lo
invoco pueda acceder a la informacin sin necesidad de recurrir directo de los controles
del propio Form. A su vez solo el evento del botn Aceptar es que el cierra el form con
una resultado aceptado para procesar la seleccin.
01.public List<CourseEntity> CursosSeleccionados
02.{
03.get
04.{
05.return lstCursosSelected.Items.Cast<CourseEntity>().ToList();
06.}
07.}
08.
09.private void btnAceptar_Click(object sender, EventArgs e)
10.{
11.this.DialogResult = DialogResult.OK;
12.}

13.
14.private void btnCancelar_Click(object sender, EventArgs e)
15.{
16.this.DialogResult = DialogResult.Cancel;
17.}

2 -Filtro IN, concatenado el string


Esta primera implementacin se podra decir que es la mas estndar y directa, aunque
hay que remarcar que no es la mas bonita.
En la clase PersonDAL se cuenta el mtodo Select()
01.public static List<PersonEntity> Select(PersonCriteria filter)
02.{
03.string sql = @"SELECT

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.

3 -Filtro por medio de XML


Si bien esta implementacin no es estandar para todas las base de datos, ya que
requiere de soporte para xml, si es la que mejor cierra en cuanto al uso de
parmetros.
En este caso la lista de cursos seleccionado es convertida a un xml, el cual se asigna al
parmetro para luego unirlo al join de la consulta.
01.public static List<PersonEntity> SelectByXml(PersonCriteria filter)
02.{
03.string sql = @"
04.DECLARE @idoc

int

05.EXEC sp_xml_preparedocument @idoc OUTPUT, @Course


06.
07.SELECT

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

int 'text()') AS CL ON CI.CourseID = CL.id

17.WHERE ((@FirstName IS NULL) OR (P.FirstName LIKE '%' + @FirstName


+ '%'))
18.AND ((@LastName IS NULL) OR (P.LastName LIKE '%' + @LastName + '%'))
19.AND ((@HireDateFrom IS NULL) OR (P.HireDate >= @HireDateFrom))
20.AND ((@HireDateTo IS NULL) OR (P.HireDate <= @HireDateTo))
21.AND ((@EnrollmentDateFrom IS NULL) OR (P.EnrollmentDate >=
@EnrollmentDateFrom))
22.AND ((@EnrollmentDateTo IS NULL) OR (P.EnrollmentDate <=
@EnrollmentDateTo))
23.AND ((@Course IS NULL) OR (CL.id IS NOT NULL))";
24.
25.
26.List<PersonEntity> list = new List<PersonEntity>();
27.
28.using (SqlConnection conn
= newSqlConnection(ConfigurationManager.ConnectionStrings["default"].ToSt
ring()))
29.{
30.conn.Open();
31.
32.SqlCommand cmd = new SqlCommand(sql, conn);
33.
34.if (string.IsNullOrEmpty(filter.FirstName))
35.cmd.Parameters.AddWithValue("@FirstName", DBNull.Value);
36.else
37.cmd.Parameters.AddWithValue("@FirstName", filter.FirstName);
38.
39.cmd.Parameters.AddWithValue("@LastName",string.IsNullOrEmpty(filter.Fi
rstName) ? (object)DBNull.Value : filter.LastName);
40.
41.cmd.Parameters.AddWithValue("@HireDateFrom",
filter.HireDateFrom.HasValue ? filter.HireDateFrom.Value.Date :
(object)DBNull.Value);
42.cmd.Parameters.AddWithValue("@HireDateTo", filter.HireDateTo.HasValue
? filter.HireDateTo.Value.Date : (object)DBNull.Value);
43.
44.cmd.Parameters.AddWithValue("@EnrollmentDateFrom",
filter.EnrollmentDateFrom.HasValue ? filter.EnrollmentDateFrom.Value.Date
: (object)DBNull.Value);

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

DECLARE @idoc int


EXEC sp_xml_preparedocument @idoc OUTPUT, @Course
cuya finalidad es inicializar el xml que luego es usado en el join
LEFT JOIN OPENXML(@idoc, '/courses/course', 2)
WITH (id int 'text()') AS CL ON CI.CourseID = CL.id
Aqu el text() representa justamente el contenido del tag, y por medio del
/courses/course (el cual es un selector de XPath), se toma cada tag de curso.

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#]

Publicado por Leandro Tuttini en 20:10 3 comentarios:


Etiquetas: C#, WinForm

domingo, 11 de septiembre de 2011

Filtros Condicionales (1/2)

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.

El punto clave en al presentacin ser la lgica encargada de cargar el criterio de


bsqueda:
01.private void btnFiltrar_Click(object sender, EventArgs e)
02.{
03.PersonCriteria filter = new PersonCriteria()
04.{
05.FirstName = txtNombre.Text,
06.LastName = txtApellido.Text,
07.EnrollmentDateFrom = chkFechaInscripcionDesde.Checked ?
(DateTime?)dtpFechaInscripcionDesde.Value : null,
08.EnrollmentDateTo = chkFechaInscripcionHasta.Checked ?
(DateTime?)dtpFechaInscripcionHasta.Value : null,
09.HireDateFrom = chkFechaContratacionDesde.Checked ?
(DateTime?)dtpFechaContratacionDesde.Value : null,
10.HireDateTo = chkFechaContratacionHasta.Checked ?
(DateTime?)dtpFechaContratacionHasta.Value : null,

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

01.public static List<PersonEntity> Select(PersonCriteria filter)


02.{
03.string sql = @"SELECT

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]

Publicado por Leandro Tuttini en 20:20 23 comentarios:


Etiquetas: C#, WinForm

domingo, 17 de julio de 2011

[Winforms] Singleton - Pasar datos entre formularios

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.

1- Singleton, Pasar datos entre formularios


Implementar el patrn es muy simple, solo se deben aplicar ciertas reglas que abrigan
a tomar la misma instancia.
01.public class RecolectarDatos
02.{
03.private static RecolectarDatos datos;

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.

2 Eventos, Informar de cambios entre formulario

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.

01.public static class CompleteEvents


02.{
03.public delegate void CompleteHandler(CompleteEventArgs args);
04.public static event CompleteHandler Complete;
05.
06.public static void RaiseEvent(int calculo)
07.{
08.if(Complete != null)
09.Complete(new CompleteEventArgs(calculo));
10.}
11.}
12.
13.public class CompleteEventArgs : EventArgs
14.{
15.public CompleteEventArgs(int calculo)
16.{
17.this.Calculo = calculo;
18.}
19.
20.public int Calculo { get; set; }
21.}
El formulario principal se adjunta al evento definido en la clase, mientras que en el
ultimo formulario es invocado el mtodo RaiseEvent() que lanzara la accin.
Adicionalmente se define un argumento del evento para informar el resultado del
calculo, igualmente en este caso por aplicar singleton, quizs no era necesario, ya que
se podra haber tomado directamente los valores de cada dato recolectado y realizar
nuevamente el calculo.

Cdigo del ejemplo

[C#]

[VB.NET]

Publicado por Leandro Tuttini en 18:34 27 comentarios:


Etiquetas: WinForm

sbado, 2 de julio de 2011

[WinForm] Listar Archivos del Directorio seleccionado

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

Debo remarcar que la implementacin que se encuentre en la clase ExtractIcon.cs, no


es de mi autora, sino que use de gua ejemplo como ser
Extracting Icons from Files
ExtractIconClass.cs
Building a Better ExtractAssociatedIcon

[C#]

[VB.NET]

Publicado por Leandro Tuttini en 23:20 5 comentarios:


Etiquetas: WinForm

lunes, 20 de junio de 2011

[Winforms] Control global de Errores Implementar Log

Introduccin

He notado en varias oportunidades que en ciertas circunstancias surgen errores no


controlador que son difciles de rastrear
Estos es consecuencia de un descuido, al no agregar correctamente en todos los
puntos importantes el try..catch, o simplemente por la lgica de la aplicacin por se
grande implicara un gran esfuerzo poner en cada evento el control de errores.
Es por eso que definir un control global a nivel de aplicacin podra ayudar, atrapando
el problema y registrando que sucedi y donde, esta informacin es muy preciada
cuando se esta perdido y no se sabe que produce el problema, mas cuando se esta en
el entorno de produccin y no se cuenta con una herramienta de debug
En este artculo se trataran los siguientes temas:
1. Control global de errores

2. Log usando System.IO.Log

1- Control Global Errores

El implementar la lgica para controlar globalmente los errores no es nada difcil, el


truco esta en adjuntarse a un evento, concretamente:
Application.ThreadException
Un buen lugar para realizar esta asignacin del evento es el archivos Program.cs,
dentro del mtodo Main()
[C#]
01.[STAThread]
02.static void Main()
03.{
04.Application.EnableVisualStyles();
05.Application.SetCompatibleTextRenderingDefault(false);
06.
07.Application.ThreadException
+= newThreadExceptionEventHandler(Application_ThreadException);
08.
09.Application.Run(new Form1());
10.}
11.
12.static void Application_ThreadException(object sender,
System.Threading.ThreadExceptionEventArgs e)
13.{
14.MessageBox.Show(e.Exception.Message);
15.}
Para lograr esto mismo en vb.net requiere de algunos paso adicionales, ya que vb.net
no brinda un acceso transparente al mtodo Main, ni permite que este sea asignado
como inicio de la aplciacion, pero en este articulo
Winforms, realizar tareas antes de inicializar aplicacin

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

O sea, si se ingresara caracteres en el calculo, el no atrapar el error en un bloque


try..catch, hara que la aplicacin finalice de forma brusca cerrndose, pero al tener
definido el evento ThreadException, entrara en accin tomando el error y mostrando
el mensaje.
Con estos simples paso ya tenemos el control global de errores implementado.

2- Log usando System.IO.Log

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]

Publicado por Leandro Tuttini en 21:31 28 comentarios:


Etiquetas: WinForm

domingo, 13 de febrero de 2011

[WinForms] Edicin Empleados Grabar imagen en base de


datos

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.

La arquitectura de la aplicacin es en dos capas, la presentacin se comunica con


DataAccess pasndole la informacin que necesita persistir o recuperar, el medio de

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.{

03.List<EmpleadoEntity> lista = new List<EmpleadoEntity>();


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";
11.
12.SqlCommand cmd = new SqlCommand(query, conn);
13.
14.SqlDataReader reader = cmd.ExecuteReader();
15.
16.while (reader.Read())
17.{
18.lista.Add(ConvertirEmpleado(reader, false));
19.}
20.
21.}
22.
23.return lista;
24.}
25.
26.private static EmpleadoEntity ConvertirEmpleado(IDataReader
reader, boolcargarRelaciones)
27.{
28.EmpleadoEntity empleado = 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"] != DBNull.Value)
37.empleado.Imagen = (byte[])reader["Imagen"];
38.

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#]

01.private void CargarListaEmpleados()


02.{
03.dgvEmpleados.AutoGenerateColumns = false;
04.dgvEmpleados.DataSource = EmpleadosDAL.ObtenerTodos();
05.
06.foreach (DataGridViewRow row in dgvEmpleados.Rows)
07.{
08.//se asigna el alto de la fila para poder ver la imagen correctamente
09.row.Height = 120;
10.
11.//se recupera la entidad que es asignada como dato
12.EmpleadoEntity empleado = row.DataBoundItem as EmpleadoEntity;
13.
14.if (empleado.Imagen == null)
15.row.Cells["Imagen"].Value = ImageHelper.ObtenerImagenNoDisponible();
16.else
17.row.Cells["Imagen"].Value =
ImageHelper.ByteArrayToImage(empleado.Imagen);
18.}
19.
20.}
[VB.NET]
01.Private Sub CargarListaEmpleados()
02.dgvEmpleados.AutoGenerateColumns = False
03.dgvEmpleados.DataSource = EmpleadosDAL.ObtenerTodos()
04.
05.For Each row As DataGridViewRow In dgvEmpleados.Rows
06.'se asigna el alto de la fila para poder ver la imagen correctamente
07.row.Height = 120
08.
09.'se recupera la entidad que es asignada como dato
10.Dim empleado As EmpleadoEntity = TryCast(row.DataBoundItem,
EmpleadoEntity)
11.
12.If empleado.Imagen Is Nothing Then
13.row.Cells("Imagen").Value = ImageHelper.ObtenerImagenNoDisponible()
14.Else
15.row.Cells("Imagen").Value =
ImageHelper.ByteArrayToImage(empleado.Imagen)

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]

01.Public Partial Class EditarEmpleado


02.Inherits Form
03.
04.Private _idEmpleado As Nullable(Of Integer) = Nothing
05.
06.Public Sub New()
07.InitializeComponent()
08.End Sub
09.
10.Public Sub New(idEmpleado As Integer)
11.Me.New()
12._idEmpleado = idEmpleado
13.End Sub
14.
15..
16..
17..
18.End Class
Cuando se termina la edicin se retorna como un resultado del formulario que indicara
si se grabo o se cancelo el formulario, esto se logra por medio del DialogResult.
[C#]
01.
02.private void btnGuardar_Click(object sender, EventArgs e)
03.{
04..
05..
06..
07.
08.//
09.// se graba
10.//
11.EmpleadosDAL.Save(empleado);

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.{

14.EmpleadoEntity empleado = EmpleadosDAL.ObtenerById(_idEmpleado.Value);


15.
16._idEmpleado = empleado.IdEmpleado;
17.txtNombre.Text = empleado.Nombre;
18.txtApellido.Text = empleado.Apellido;
19.dtpFechaNacimiento.Value = empleado.FechaNacimiento;
20.
21.cbEstadoCivil.SelectedValue = Convert.ToInt32(empleado.EstadoCivil);
22.
23.if (empleado.Imagen == null)
24.picImagenEmpleado.Image = ImageHelper.ObtenerImagenNoDisponible();
25.else
26.picImagenEmpleado.Image =
ImageHelper.ByteArrayToImage(empleado.Imagen);
27.
28.//
29.// Se obtienen los estudios del empleado
30.//
31.AsignarEstudios(empleado);
32.
33.}
34.
35.}
36.
37.private void CargarEstadoCivil()
38.{
39.cbEstadoCivil.DisplayMember = "Descripcion";
40.cbEstadoCivil.ValueMember = "IdEstadoCivil";
41.cbEstadoCivil.DataSource = EstadoCivilDAL.ObtenerTodos();
42.}
43.
44.private void CargarEstudio()
45.{
46.dgvEstudios.AutoGenerateColumns = false;
47.dgvEstudios.DataSource = EstudiosDAL.ObtenerTodos();
48.}
49.
50.private void AsignarEstudios(EmpleadoEntity empleado)
51.{
52.

53.List<DataGridViewRow> rows = (from


row indgvEstudios.Rows.Cast<DataGridViewRow>()
54.let idEstudio = Convert.ToInt32(row.Cells["IdEstudio"].Value)
55.join dd in empleado.Estudios on idEstudio equals dd.IdEstudio
56.select row).ToList();
57.
58.
59.rows.ForEach(o => o.Cells["Seleccion"].Value = true);
60.
61.}
[VB.NET]
01.Private Sub EditarEmpleado_Load(ByVal sender As Object, ByVal e As EventA
rgs)
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 Then
13.Dim empleado As EmpleadoEntity =
EmpleadosDAL.ObtenerById(_idEmpleado.Value)
14.
15._idEmpleado = empleado.IdEmpleado
16.txtNombre.Text = empleado.Nombre
17.txtApellido.Text = empleado.Apellido
18.dtpFechaNacimiento.Value = empleado.FechaNacimiento
19.
20.cbEstadoCivil.SelectedValue = Convert.ToInt32(empleado.EstadoCivil)
21.
22.If empleado.Imagen Is Nothing Then
23.picImagenEmpleado.Image = ImageHelper.ObtenerImagenNoDisponible()
24.Else
25.picImagenEmpleado.Image =
ImageHelper.ByteArrayToImage(empleado.Imagen)

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

La asignacin de la imagen es muy simple y similar a como se trabajo en el


DataGridview, solo se convierte el array de byte recuperado de la tabla en la base de
datos y se carga directo en el PictureBox.
Algo interesante a remarcar es la forma en como se carga la lista de Estudios, en esta
se usan dos consultas separadas, por un lado la primera que carga todos los estudios
existentes, si se ingresara en modo de alta de empleado solo esta lista seria cargada,
pero si se esta editando se recupera la lista de estudios asignados a ese empleado en
particular y por medio de una consulta linq (mtodo AsignarEstudios) se realiza la
unin entres ambas listas aquellas filas del DataGridView que coincidan sern
marcadas porque son las que el empleado tiene registradas en la tabla.
Bsqueda de una imagen (cargar archivo a un PictureBox)
Un punto que debera ser menor pero que he visto con mucha dificultad en preguntas
en los foros, es como cargar seleccionar un archivo y cargarlo en un PictureBox?
El cdigo es muy simple pero suele generar problemas.
[C#]
01.private void btnBuscarImagen_Click(object sender, EventArgs e)
02.{
03.OpenFileDialog fileDialog = new OpenFileDialog();
04.fileDialog.Filter = "Archivo JPG|*.jpg";
05.
06.if (fileDialog.ShowDialog() == DialogResult.OK)
07.{
08.picImagenEmpleado.Image = Image.FromFile(fileDialog.FileName);
09.}
10.}
[VB.NET]
01.Private Sub btnBuscarImagen_Click(ByVal sender As Object, ByVal e As Even
tArgs)
02.
03.Dim fileDialog As New OpenFileDialog()
04.fileDialog.Filter = "Archivo JPG|*.jpg"
05.
06.If fileDialog.ShowDialog() = DialogResult.OK Then
07.picImagenEmpleado.Image = Image.FromFile(fileDialog.FileName)

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]

Publicado por Leandro Tuttini en 20:03 141 comentarios:

También podría gustarte