<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Quemando cromo &#187; Programacion</title>
	<atom:link href="http://cromo.cda-ie.es/category/programacion/feed/" rel="self" type="application/rss+xml" />
	<link>http://cromo.cda-ie.es</link>
	<description>Hacía calor, la noche que quemamos a Cromo</description>
	<lastBuildDate>Tue, 27 Dec 2011 08:07:53 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>El problema de la barra de progreso</title>
		<link>http://cromo.cda-ie.es/2011/12/27/el-problema-de-la-barra-de-progreso/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=el-problema-de-la-barra-de-progreso</link>
		<comments>http://cromo.cda-ie.es/2011/12/27/el-problema-de-la-barra-de-progreso/#comments</comments>
		<pubDate>Tue, 27 Dec 2011 08:07:53 +0000</pubDate>
		<dc:creator>Cubano</dc:creator>
				<category><![CDATA[Visual Basic 2005]]></category>
		<category><![CDATA[Visual Basic 2008]]></category>
		<category><![CDATA[Visual Basic 2010]]></category>

		<guid isPermaLink="false">http://cromo.cda-ie.es/?p=382</guid>
		<description><![CDATA[El año pasado, intentando desembarazarme de una tarea tediosa que dejaba frito mi equipo, usé mi primera función recursiva con utilidad práctica. Como la tarea consumía su tiempo, tiré de un BackGroundWorker. Componente sencillo de usar y con ejemplos bastante claros en la ayuda de Visual Studio. No problem: tenía un valor de entrada pedido [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">El año pasado, intentando desembarazarme de una tarea tediosa que dejaba frito mi equipo, usé mi primera función recursiva con utilidad práctica. Como la tarea consumía su tiempo, tiré de un </span><span style="font-family:Courier New"><strong>BackGroundWorker</strong></span><span style="font-family:Verdana">. Componente sencillo de usar y con ejemplos bastante claros en la ayuda de Visual Studio. No problem: tenía un valor de entrada pedido por el usuario, se realizaban n operaciones de forma recursiva, se obtenía un valor de salida.<br />
</span></span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Como el proceso era algo largo (mínimo unos diez segundos, pero podía llegar superar el minuto en la versión 2, que admitía de entrada una serie de valores), se me ocurrió usar una barra de progreso para mantener informado al usuario.<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Entonces empezaron los problemas.<br />
</span></p>
<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">Todos los ejemplos que he visto que usan el evento </span><span style="font-family:Courier New"><strong>ProgressChanged</strong></span><span style="font-family:Verdana"> del </span><span style="font-family:Courier New"><strong>BackGroundWorker</strong></span><span style="font-family:Verdana"> para alimentar una barra de progreso presentan una serie de operaciones en secuencia con llamadas a </span><span style="font-family:Courier New"><strong>ProgressChanged</strong></span><span style="font-family:Verdana"> entre ellas, es decir: hago paso 1; progreso cambiado; hago paso 2; progreso cambiado… Sin embargo, mi problema era otro: tengo una instancia de mi clase de trabajo, cojo los valores del usuario (a través de </span><span style="font-family:Courier New"><strong>DoWorkEventArgs.Argument</strong></span><span style="font-family:Verdana">), se los paso, y le digo &#8220;ea, majo. Tú puedes&#8221; llamando al método </span><span style="font-family:Courier New"><strong>MiClase.HacerCurro</strong></span><span style="font-family:Verdana">. Ahí dentro se harán las n operaciones (recursivas), número desconocido hasta que se hagan.<br />
</span></span></p>
<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">Vale, pensé. Puedo provocar un evento </span><span style="font-family:Courier New"><strong>OperaciónCompletada</strong></span><span style="font-family:Verdana"> cada vez que complete una operación. Pero la instancia de </span><span style="font-family:Courier New"><strong>MiClase</strong></span><span style="font-family:Verdana"> está declarada dentro del </span><span style="font-family:Courier New"><strong>DoWork</strong></span><span style="font-family:Verdana"> del </span><span style="font-family:Courier New"><strong>BackGroundWorker</strong></span><span style="font-family:Verdana">. No tengo acceso desde fuera. ¿Cómo lo hago?<br />
</span></span></p>
<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">¿Y si meto alguien que escuche en el </span><span style="font-family:Courier New"><strong>DoWork</strong></span><span style="font-family:Verdana">? De esta forma tendría mi &#8220;escuchador&#8221; de eventos en el mismo hilo. Es decir, una clase, digamos </span><span style="font-family:Courier New"><strong>Escuchante</strong></span><span style="font-family:Verdana">, a la que le paso mi instancia de </span><span style="font-family:Courier New"><strong>MiClase</strong></span><span style="font-family:Verdana"> y la del </span><span style="font-family:Courier New"><strong>BackGroundWorker</strong></span><span style="font-family:Verdana">. Escuchante gestiona el evento </span><span style="font-family:Courier New"><strong>OperacionCompletada</strong></span><span style="font-family:Verdana"> provocando a su vez el </span><span style="font-family:Courier New"><strong>ProgressChanged</strong></span><span style="font-family:Verdana">, que actualiza la barra de progreso.<br />
</span></span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">No sé si será una solución muy limpia, pero funcionó.<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">En resumen, algo así:<br />
</span></p>
<p ><span style="font-family:Courier New; font-size:10pt">Public Class Escuchante<br />
</span></p>
<p ><span style="font-family:Courier New; font-size:10pt">Private WithEvents _MiClase as MiClase<br />
</span></p>
<p ><span style="font-family:Courier New; font-size:10pt">Private _MiBGW as BackGroundWorker<br />
</span></p>
<p ><span style="font-family:Courier New; font-size:10pt">Public Sub New(mc as MiClase, bw as BackGroundWorker)<br />
</span></p>
<p style="margin-left: 36pt"><span style="font-family:Courier New; font-size:10pt">_MiClase=mc<br />
</span></p>
<p style="margin-left: 36pt"><span style="font-family:Courier New; font-size:10pt">_MiBGW=bw<br />
</span></p>
<p ><span style="font-family:Courier New; font-size:10pt">End Sub<br />
</span></p>
<p ><span style="font-family:Courier New; font-size:10pt">Private Sub OpCompletada(sender as Object, e as OperacionCompletadaEventArgs) _<br />
</span></p>
<p ><span style="font-family:Courier New; font-size:10pt">                                    Handles _MiClase.OperacionCompletada<br />
</span></p>
<p style="margin-left: 36pt"><span style="font-family:Courier New; font-size:10pt">_MiBGW.ReportProgress=e.NumOp<br />
</span></p>
<p ><span style="font-family:Courier New; font-size:10pt">End Sub<br />
</span></p>
<p ><span style="font-family:Courier New; font-size:10pt">End Class</span></p>
]]></content:encoded>
			<wfw:commentRss>http://cromo.cda-ie.es/2011/12/27/el-problema-de-la-barra-de-progreso/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quiero mi Date Null, gracias</title>
		<link>http://cromo.cda-ie.es/2011/09/27/quiero-mi-date-null-gracias/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=quiero-mi-date-null-gracias</link>
		<comments>http://cromo.cda-ie.es/2011/09/27/quiero-mi-date-null-gracias/#comments</comments>
		<pubDate>Tue, 27 Sep 2011 15:13:48 +0000</pubDate>
		<dc:creator>Cubano</dc:creator>
				<category><![CDATA[Visual Basic 2010]]></category>

		<guid isPermaLink="false">http://cromo.cda-ie.es/?p=365</guid>
		<description><![CDATA[Quiero mi bo cadillo decía ese gran filósofo moderno que es Homer Simpson. Algo parecido he estado yo pidiendo estos días. El problema se me ha presentado en una aplicación con acceso a datos, con un montaje convencional: dataset tipado + bindingsource + controles en el formulario. Uno de los campos era una fecha y [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt"><em>Quiero mi bo cadillo</em> decía ese gran filósofo moderno que es Homer Simpson. Algo parecido he estado yo pidiendo estos días. El problema se me ha presentado en una aplicación con acceso a datos, con un montaje convencional: dataset tipado + bindingsource + controles en el formulario. Uno de los campos era una fecha y podía tener valores nulos. Hasta ahí bien. El problema es que en ediciones podía recuperar el valor nulo. En esos casos, fallaba la validación de datos y no se guardaba nada, aunque tampoco conseguía saber por qué: tuve que echar mano del evento BindingComplete del BindingSource para enterarme que se producía un error en la conversión de tipos. El tipo Date no acepta nulos.<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Tras una larga búsqueda en internet, casi todo en inglés, averigüé dos cosas: que era un problema común y muy molesto y que no tenía solución fácil. También hallé <a href="http://blogs.msdn.com/b/dchandnani/archive/2005/08/15/451979.aspx">unas pistas</a> que me indicaron el camino. Un trabajo no muy limpio, pero que funciona, dividido en dos partes: el enlace al control y la datatable correspondiente.</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">En enlace, usando el diseñador (DataBindings, Avanzado) para asignar un formato de fecha corta, queda así:</span></p>
<p><span id="more-365"></span></p>
<p style="text-align: left"><span style="font-size:10pt"><span style="color:blue; font-family:Courier New">Me</span><span style="font-family:Courier New">.txtFecha.DataBindings.Add(<span style="color:blue">New</span> Binding(<span style="color:#a31515">&#8220;Text&#8221;</span>, <span style="color:blue">Me</span>.bsDatos, <span style="color:#a31515">&#8220;Fecha&#8221;</span>, <span style="color:blue">True</span>, DataSourceUpdateMode.OnValidation, <span style="color:blue">Nothing</span>, <span style="color:#a31515">&#8220;d&#8221;</span>))</span><span style="font-family:Verdana"></span></span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Donde Nothing representa cómo se van a mostrar los valores nulos de la base de datos, la propiedad NullValue del Binding. Ésa sabemos que funciona bien, nos interesa gestionar el otro sentido de la actualización, hacia el origen de datos. Para ello tenemos la propiedad <strong>DataSourceNullValue</strong>. Sacamos el enlace del designer y lo gestionamos a mano, creándonos el Binding:<br />
</span></p>
<p style="text-align: left"><span style="font-family:Courier New; font-size:10pt"><span style="color:blue">Dim</span> mBinding <span style="color:blue">As</span>	<span style="color:blue">New</span> Binding(<span style="color:#a31515">&#8220;Text&#8221;</span>, <span style="color:blue">Me</span>.bsDatos, <span style="color:#a31515">&#8220;Fecha&#8221;</span>, <span style="color:blue">True</span>, DataSourceUpdateMode.OnValidation, <span style="color:#a31515">&#8220;&#8221;</span>, <span style="color:#a31515">&#8220;d&#8221;</span>)<br />
</span><span style="font-family:Courier New; font-size:10pt">mBinding.DataSourceNullValue = <span style="color:blue">Date</span>.MinValue</span><br />
<span style="font-size:10pt"><span style="color:blue; font-family:Courier New">Me</span><span style="font-family:Courier New">.txtFecha.DataBindings.Add(mBinding)</span></span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Primer punto: indicamos que el NullValue para el origen de datos no va a ser DBNull (por defecto y causa de los errores), sino un valor de fecha válido, en este caso el menor valor posible (Date.MinValue).</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Segundo punto: cambiamos el Nothing del NullValue por String.Empty o &#8220;&#8221;. La razón es muy sencilla: NullValue nos indica dos cosas. Por una parte, si (en este caso) la fecha en la DataTable es DBNull, lo representa por lo que le indiquemos ahí. Lo segundo, si el valor de la propiedad del control (Text, en este caso) coincide con el NullValue en un cambio de datos (altas, modificaciones), le pasa al origen de datos (la DataTable) lo que le tengamos dicho en DataSourceNullValue. Y el Nothing ahí no nos vale: si no hay nada en Text, obtenemos una cadena vacía, String.Empty o &#8220;&#8221;, no un valor Nothing.</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Con esto, pues, si dejamos el textbox txtFecha vacío, obtendremos en el campo Fecha de la fila correspondiente un valor fecha igual a Date.MinValue. Que no es lo que queremos: buscamos un DBNull.</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Eso lo hacemos en otra parte: gestionando el evento <strong>ColumChanging</strong> del DataTable. Modo rápido: en el diseñador del dataset tipado hacemos doble click sobre la columna en cuestión y nos genera el código automáticamente. En la gestión del evento incluimos el código para que nos filtre la fecha y si es igual a Date.MinValue, cambie el valor propuesto del campo a DBNull.value:</span></p>
<p style="text-align: left"><span style="font-family:Courier New; font-size:10pt"><span style="color:blue">If</span> (e.Column.ColumnName = <span style="color:blue">Me</span>.FechaColumn.ColumnName) <span style="color:blue">Then<br />
</span></span><br />
       <span style="font-family:Courier New; font-size:10pt"><span style="color:blue">If</span> IsDate(e.ProposedValue) <span style="color:blue">AndAlso</span><br />
			<span style="color:blue">CDate</span>(e.ProposedValue) = <span style="color:blue">Date</span>.MinValue <span style="color:blue">Then<br />
</span></span><span style="font-family:Courier New; font-size:10pt">        e.ProposedValue = DBNull.Value<br />
</span><span style="font-family:Courier New; font-size:10pt"><br />
			<span style="color:blue">End If<br />
</span></span><span style="font-size:10pt"><span style="color:blue; font-family:Courier New">End If</span></span><span style="font-family:Verdana"><br />
			</span></span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Con esto, ya tenemos nuestro campo fecha que acepta valores nulos. No es muy elegante, quizás, pero no se me ha ocurrido ninguna otra forma.</span></p>
]]></content:encoded>
			<wfw:commentRss>http://cromo.cda-ie.es/2011/09/27/quiero-mi-date-null-gracias/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>El valor no puede ser nulo. Nombre del parámetro: objectType</title>
		<link>http://cromo.cda-ie.es/2011/09/22/el-valor-no-puede-ser-nulo-nombre-del-parametro-objecttype/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=el-valor-no-puede-ser-nulo-nombre-del-parametro-objecttype</link>
		<comments>http://cromo.cda-ie.es/2011/09/22/el-valor-no-puede-ser-nulo-nombre-del-parametro-objecttype/#comments</comments>
		<pubDate>Thu, 22 Sep 2011 08:57:24 +0000</pubDate>
		<dc:creator>Cubano</dc:creator>
				<category><![CDATA[Visual Basic 2010]]></category>

		<guid isPermaLink="false">http://cromo.cda-ie.es/2011/09/22/el-valor-no-puede-ser-nulo-nombre-del-parametro-objecttype/</guid>
		<description><![CDATA[El error en cuestión me salía en el diseñador de Visual Basic Express 2010 de forma insistente. Una molestia a la hora de trabajar con el diseñador, más que nada, porque luego la aplicación funcionaba sin problemas. Ayer me dediqué a buscarle solución, tirando de Google. El problema, apuntaban en varios sitios, se daba en [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">El error en cuestión me salía en el diseñador de Visual Basic Express 2010 de forma insistente. Una molestia a la hora de trabajar con el diseñador, más que nada, porque luego la aplicación funcionaba sin problemas. Ayer me dediqué a buscarle solución, tirando de Google.<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">El problema, apuntaban en varios sitios, se daba en formularios o controles de usuario heredados y estaba causado al gestionar eventos de objetos de alguna de las clases padre con la cláusula Handles en la clase hija. Tras investigar con calma mi usercontrol, encontré que el causante del desaguisado era, efectivamente, una cláusula handles que apuntaba a un evento de un BindingSource del abuelo del usercontrol. Quité el Handles y lo sustituí por un AddHandler en el constructor y todo funciona con suavidad.</span></p>
]]></content:encoded>
			<wfw:commentRss>http://cromo.cda-ie.es/2011/09/22/el-valor-no-puede-ser-nulo-nombre-del-parametro-objecttype/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Tocamiento de narices de un TextBox</title>
		<link>http://cromo.cda-ie.es/2011/03/31/tocamiento-de-narices-de-un-textbox/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=tocamiento-de-narices-de-un-textbox</link>
		<comments>http://cromo.cda-ie.es/2011/03/31/tocamiento-de-narices-de-un-textbox/#comments</comments>
		<pubDate>Thu, 31 Mar 2011 09:44:55 +0000</pubDate>
		<dc:creator>Cubano</dc:creator>
				<category><![CDATA[Visual Basic 2010]]></category>

		<guid isPermaLink="false">http://cromo.cda-ie.es/?p=346</guid>
		<description><![CDATA[Ando estos días programando una serie de plantillas y controles personalizados. He estado preparando un TextBox personalizado con posibilidad de validación y filtrado de datos de serie o mediante validadores/filtradores a medida que implementen cierta interfaz. Hasta ahí sin problema. Como el tipo de TextBox en cuestión va a ir montado, como regla general, sobre [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Ando estos días programando una serie de plantillas y controles personalizados. He estado preparando un TextBox personalizado con posibilidad de validación y filtrado de datos de serie o mediante validadores/filtradores a medida que implementen cierta interfaz. Hasta ahí sin problema. Como el tipo de TextBox en cuestión va a ir montado, como regla general, sobre un formulario o un control de usuario que incorporan un sistema de gestión del estado (VerDatos, Modificar, Nuevo, etc.) se me ha ocurrido que el TextBox ajuste su estado ReadOnly automáticamente. Vale, sin problema. Ha funcionado a la primera. Salvo por un pequeño detalle visual.<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Al indicar ReadOnly=True el color de fondo del TextBox cambia.<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Pensé en controlar el evento ReadOnlyChanged, pero no conozco el color de fondo anterior al cambio y, francamente, saberlo complicaría más las cosas (reescribir la propiedad BackColor y el método OnReadOnlyChanged). Reescribir el método OnPaint, que de todas formas ni se me pasaba por la cabeza, no serviría porque, leído <a href="http://www.dotnetmonster.com/Uwe/Forum.aspx/winform-controls/2267/TextBox-readonly-background-colour"><strong>aquí</strong></a>, el cambio de color provocado por el cambio de ReadOnly se hace &#8220;por su cuenta&#8221; (<em>Actually, .Net TextBox is a simple wrapper of Win32 Edit control, and it passes the painting issue to the Win32 Edit control</em>). En el mensaje en cuestión se propone sobreescribir la propiedad ReadOnly del TextBox, algo que parece sencillo, simple y razonablemente elegante.<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Salvo por otro pequeño problema: ReadOnly es una palabra reservada. Todo intento de crear una propiedad que se llame ReadOnly es frustrada por el propio IDE. ¿Cómo se hace para llamar a una propiedad como una palabra reservada?<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Este pequeño problema me ha llevado bastante rato solucionarlo, así que lo comparto. Es una tontería y tiene su lógica, pero me ha hecho perder un tiempo precioso.<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Usar corchetes:<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Public Overloads Property [ReadOnly] as Boolean<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">En fin, sigamos…</span></p>
]]></content:encoded>
			<wfw:commentRss>http://cromo.cda-ie.es/2011/03/31/tocamiento-de-narices-de-un-textbox/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Pegar datos de una hoja de Excel a un DataGridView</title>
		<link>http://cromo.cda-ie.es/2011/01/18/pegar-datos-de-una-hoja-de-excel-a-un-datagridview/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=pegar-datos-de-una-hoja-de-excel-a-un-datagridview</link>
		<comments>http://cromo.cda-ie.es/2011/01/18/pegar-datos-de-una-hoja-de-excel-a-un-datagridview/#comments</comments>
		<pubDate>Tue, 18 Jan 2011 21:05:29 +0000</pubDate>
		<dc:creator>Cubano</dc:creator>
				<category><![CDATA[Visual Basic 2005]]></category>
		<category><![CDATA[Visual Basic 2008]]></category>
		<category><![CDATA[Visual Basic 2010]]></category>

		<guid isPermaLink="false">http://cromo.cda-ie.es/?p=338</guid>
		<description><![CDATA[Un intento de simplificar el uso de una aplicación por lo demás bastante simple (de cara al usuario) me ha llevado a jugar con el portapapeles para permitir que el usuario copie una serie de valores de una hoja de cálculo (Excel, para más señas) y las pegue en un DataGridView, algo que me ha [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Un intento de simplificar el uso de una aplicación por lo demás bastante simple (de cara al usuario) me ha llevado a jugar con el portapapeles para permitir que el usuario copie una serie de valores de una hoja de cálculo (Excel, para más señas) y las pegue en un DataGridView, algo que me ha costado un buen rato, no porque sea complicado, sino porque no he encontrado información al respecto por Internet (seguramente por la torpeza &#8220;buscativa&#8221; de uno).</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">El problema, como digo, no es complicado. Por una parte, acceder al portapapeles es muy sencillo, mediante My.Computer.Clipboard y hay varios métodos para extraer su contenido según lo que contenga. En este caso (la copia de texto) usaremos el método GetText, que nos devuelve un String. El dónde gestionar esto ya queda a gusto del consumidor pero, para mí, lo más fácil es añadir un menú contextual con el habitual atajo de teclado (Ctrl + V).</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">El siguiente paso es tratar ese texto. Cualquier colección de celdas de Excel es copiada al portapapeles como un conjunto de valores separados por tabulación. El otro día mostraba el uso de la clase <b><a href="http://cromo.cda-ie.es/2010/11/23/leer-ficheros-csv-y-de-campos-de-ancho-fijo-en-visual-basic/">TextFieldParser</a></b> para procesar ficheros de texto que nos viene como anillo al dedo. Bueno, casi, porque al TextFieldParser no le podemos pasar un String, está pensado para procesar ficheros. Si vemos sus constructores, a uno se le pasa la ruta de un fichero, a otro un Stream, precisamente para tratar con un fichero, y a la tercera sobrecarga, un TextReader.</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">¿Qué es un TextReader (era la primera vez que lo veía)? Según la biblioteca de MSDN, TextReader es la clase abstracta de StreamReader (éste si lo conocía) y StringReader. Éste último tiene en su nombre la palabra mágica, String, así que merece la pena echar un ojo. Me da igual los tutoriales que nos puedan aparecer ni los mil usos que tenga StringReader. Para el caso que nos ocupa, todo se reduce a crear un StringReader a partir del texto contenido en el portapapeles:</span></p>
<p><span style="font-family:Courier New; font-size:10pt"><span style="color:blue">Dim</span> miTexto <span style="color:blue">As</span> <span style="color:#2b91af">StringReader</span></span></p>
<p><span style="font-family:Courier New; font-size:10pt">miTexto = <span style="color:blue">New</span> <span style="color:#2b91af">StringReader</span>(<span style="color:blue">My</span>.Computer.Clipboard.GetText)</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Y pasarle ese StringReader a un TextFieldParser, con lo que estamos casi, casi, en el mismo caso del ejemplo de TextFieldParser. Ya podemos trabajar de forma simple con el texto del portapapeles. Sólo hay que añadir los controles de errores necesarios y decidir qué vamos a hacer con cada fila válida devuelta por el TextFieldParser. Siguiendo la lógica del título de esta entrada, pasárselas al DataGridView, pero tengo que confesar que no lo hice así. La razón es muy simple: me interesaba más pasárselo directamente al origen de datos del DataGridView.</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Para mi caso concreto (Origen de datos + BindingSource + DataGridView), antes de iniciar el proceso de lectura de TextFieldParser, llamé al CancelEdit del BindingSource para evitar la posibilidad de que se generase una fila en blanco o no válida (si se da el caso, se saltaría la validación de datos) y le quité también la referencia al DataSource:</span></p>
<p style="text-align: justify"><span style="font-family:Courier New; font-size:10pt"><span style="color:blue">Me</span>.bsArticulos.CancelEdit()<br />
</span></p>
<p><span style="font-family:Courier New; font-size:10pt"><span style="color:blue">Me</span>.bsArticulos.DataSource = <span style="color:#a31515">&#8220;&#8221;<br />
</span></span></p>
<p><span style="font-family:Verdana; font-size:10pt">Después de añadir los valores del portapapeles al origen de datos, se vuelve a enlazar el BindingSource con éste y listo. Bueno, vale, no he pegado realmente en el DataGridView. Espero que nadie se sienta estafado.<br />
</span></p>
<p><span style="font-family:Verdana; font-size:10pt">Nos vemos en el <em>Forlon.</em></span></p>
]]></content:encoded>
			<wfw:commentRss>http://cromo.cda-ie.es/2011/01/18/pegar-datos-de-una-hoja-de-excel-a-un-datagridview/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Leer ficheros csv y de campos de ancho fijo en Visual Basic</title>
		<link>http://cromo.cda-ie.es/2010/11/23/leer-ficheros-csv-y-de-campos-de-ancho-fijo-en-visual-basic/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=leer-ficheros-csv-y-de-campos-de-ancho-fijo-en-visual-basic</link>
		<comments>http://cromo.cda-ie.es/2010/11/23/leer-ficheros-csv-y-de-campos-de-ancho-fijo-en-visual-basic/#comments</comments>
		<pubDate>Tue, 23 Nov 2010 14:21:10 +0000</pubDate>
		<dc:creator>Cubano</dc:creator>
				<category><![CDATA[Visual Basic 2005]]></category>
		<category><![CDATA[Visual Basic 2008]]></category>

		<guid isPermaLink="false">http://cromo.cda-ie.es/?p=323</guid>
		<description><![CDATA[En mi vuelta al servicio activo he tenido que procesar dos ficheros en texto plano: uno con campos separados por un carácter y otro con campos de ancho fijo. En su día ya lo hice (concretamente para un csv, valores separados por comas), pero no me acordaba de cómo lo hice (fue al poco de [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">En mi vuelta al servicio activo he tenido que procesar dos ficheros en texto plano: uno con campos separados por un carácter y otro con campos de ancho fijo. En su día ya lo hice (concretamente para un csv, valores separados por comas), pero no me acordaba de cómo lo hice (fue al poco de empezar con VB2005) y no tenía a mano ni el código que hice ni el libro que usé de referencia, así que busqué en Internet.<br />
</span></p>
<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">Y fíjese usted por donde, encontré una forma más simple de hacer las cosas que no conocía: usando la clase </span><a href="http://msdn.microsoft.com/es-es/library/ms128079.aspx"/><span style="font-family:Courier New">TextFieldParser</span></a><span style="font-family:Verdana">.</span></p>
<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">En la forma más fácil, le pasamos en el constructor la ruta del archivo a tratar. Le indicamos luego qué es, si de ancho fijo (</span><span style="font-family:Courier New"><strong>FixedWith</strong></span><span style="font-family:Verdana">) o separado por carácter (</span><span style="font-family:Courier New"><strong>Delimited</strong></span><span style="font-family:Verdana">) con la propiedad </span><span style="font-family:Courier New"><strong>TextFieldType</strong></span><span style="font-family:Verdana">. Si es el segundo caso, con el método </span><span style="font-family:Courier New"><strong>SetDelimiters</strong></span><span style="font-family:Verdana"> indicamos el delimitador (por ejemplo, </span><span style="font-family:Courier New">MiTfp.SetDelimiters(vbTab)</span><span style="font-family:Verdana"> para indicar campos separados por una tabulación).</span></p>
<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">En el primero, indicaríamos el ancho de los campos mediante una lista de enteros que le pasamos al método </span><span style="font-family:Courier New"><strong>SetFieldWiths</strong></span><span style="font-family:Verdana">: </span><span style="font-family:Courier New">MiTfp.SetFieldWiths(2, 3, 12, 85, 5)</span><span style="font-family:Verdana">.</span></p>
<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">Para leer cada línea tenemos el método </span><span style="font-family:Courier New"><strong>ReadFields</strong></span><span style="font-family:Verdana">, que nos devuelve un array de cadenas con cada campo de la línea del fichero en la posición correspondiente del array. Se tarda más en explicar que en hacer.</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Hay un pequeño problema adicional: la codificación de caracteres. Por defecto, se usa UTF8. Ahora mismo no recuerdo si Vista y 7 usan UTF8 por defecto, pero XP y versiones anteriores de Windows, no, y podemos encontrarnos con problemas con algunos caracteres, como ñ, tildes, etc. En ese caso, seguramente estemos usando Windows occidental (Windows 1252) o, más raramente, ISO-8859-1. Podemos indicarle esto al TextFieldParser en el constructor, pasándole el código de <a href="http://msdn.microsoft.com/es-es/library/system.text.encoding%28v=VS.100%29.aspx">la página de códigos</a> a emplear:</span></p>
<p style="text-align: justify"><span style="font-family:Courier New; font-size:10pt">Dim MiTfp as New TextFieldParser(MiRuta, System.Text.Encoding.GetEncoding(1252))<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Lo más largo, como siempre, es el control de errores, o el propio tratamiento de los valores que hayamos leído, pero el hecho de leer en sí más simple no puede ser.<br />
</span></p>
]]></content:encoded>
			<wfw:commentRss>http://cromo.cda-ie.es/2010/11/23/leer-ficheros-csv-y-de-campos-de-ancho-fijo-en-visual-basic/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>DataRepeater y el evento Enter de sus controles</title>
		<link>http://cromo.cda-ie.es/2010/11/16/datarepeater-y-el-evento-enter-de-sus-controles/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=datarepeater-y-el-evento-enter-de-sus-controles</link>
		<comments>http://cromo.cda-ie.es/2010/11/16/datarepeater-y-el-evento-enter-de-sus-controles/#comments</comments>
		<pubDate>Tue, 16 Nov 2010 10:24:18 +0000</pubDate>
		<dc:creator>Cubano</dc:creator>
				<category><![CDATA[Visual Basic 2005]]></category>
		<category><![CDATA[Visual Basic 2008]]></category>

		<guid isPermaLink="false">http://cromo.cda-ie.es/2010/11/12/datarepeater-y-el-evento-enter-de-sus-controles/</guid>
		<description><![CDATA[A la hora de evitar que el usuario manazas edite el valor de algún control, una opción habitual es controlar el evento Enter de ese control o de su contenedor (un GroupBox, un Panel&#8230;), mandando el foco a otro control mediante el método OtroControl.Select(). Esto funciona para buena parte de los controles disponibles en Windows [...]]]></description>
			<content:encoded><![CDATA[<p><span style="font-family:Verdana; font-size:10pt">A la hora de evitar que el usuario manazas edite el valor de algún control, una opción habitual es controlar el evento Enter de ese control o de su contenedor (un GroupBox, un Panel&#8230;), mandando el foco a otro control mediante el método OtroControl.Select(). Esto funciona para buena parte de los controles disponibles en Windows Forms, pero no para todos (dtpicker, optionbutton&#8230; hay varios que pasarán de nosotros).<br />
</span></p>
<p><span style="font-family:Verdana; font-size:10pt">Si nosotros queremos usar este sistema con controles que estén dentro de un DataRepeater&#8230; En fin, probadlo si queréis, pero ya os digo que la aplicación se quedará muerta si mandáis el foco a un control que esté fuera del DataRepeater. ¿Entonces?<br />
</span></p>
<p><span style="font-family:Verdana; font-size:10pt">Pues no salgamos. Pasemos el foco al ítem del DataRepeater al que pertenezca el control y listo:<br />
</span></p>
<p><span style="font-family:Courier New; font-size:10pt"><strong>MiDataRepeater.CurrentItem.Select()<br />
</strong></span></p>
<p><span style="font-family:Verdana; font-size:10pt">El control DataRepeater es más versátil que el habitual DataGridView, pero es caprichoso como él solo.<br />
</span></p>
]]></content:encoded>
			<wfw:commentRss>http://cromo.cda-ie.es/2010/11/16/datarepeater-y-el-evento-enter-de-sus-controles/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Evitar error en consultas de agregado de LINQ</title>
		<link>http://cromo.cda-ie.es/2010/10/28/evitar-error-en-consultas-de-agregado-de-linq/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=evitar-error-en-consultas-de-agregado-de-linq</link>
		<comments>http://cromo.cda-ie.es/2010/10/28/evitar-error-en-consultas-de-agregado-de-linq/#comments</comments>
		<pubDate>Thu, 28 Oct 2010 20:11:53 +0000</pubDate>
		<dc:creator>Cubano</dc:creator>
				<category><![CDATA[Visual Basic 2008]]></category>

		<guid isPermaLink="false">http://cromo.cda-ie.es/2010/10/28/evitar-error-en-consultas-de-agregado-de-linq/</guid>
		<description><![CDATA[Nombre pomposo de la entrada pero que contesta a un problema que se me ha presentado esta mañana (volver a trabajar tiene estas cosas): al calcular el valor máximo de un campo en un conjunto de datos con una consulta LINQ sobre un dataset (por ejemplo, el importe más alto de un determinado cliente) se [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Nombre pomposo de la entrada pero que contesta a un problema que se me ha presentado esta mañana (volver a trabajar tiene estas cosas): al calcular el valor máximo de un campo en un conjunto de datos con una consulta LINQ sobre un dataset (por ejemplo, el importe más alto de un determinado cliente) se producía una excepción si no se devuelve ningún valor (en el ejemplo, si ese cliente no tiene pedidos). Una búsqueda por Internet me ha apuntado como solución usar extensiones de métodos, algo que no domino y se me antojaba excesivamente complicado para la tarea.<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Tras pensarlo un poco, he probado a convertir el valor del campo pasado a la instrucción MAX a un tipo Nullable, que acepta valores nulos. En mi caso, como era valores enteros, esa parte de la consulta LINQ quedaría tal que así:<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Courier New; font-size:10pt">Into Max(Ctype(Fila.Campo, Integer?))<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">La consulta ya no da error, y sólo nos queda controlar si hay valor devuelto (propiedad HasValue), convertirlo al tipo que queramos (Integer, en el ejemplo) y seguir adelante. Más sencillo se me hace que otras ideas que he visto por ahí, y más rápido.<br />
</span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Como siempre, si a alguien le resulta de utilidad, pues mejor.</span></p>
]]></content:encoded>
			<wfw:commentRss>http://cromo.cda-ie.es/2010/10/28/evitar-error-en-consultas-de-agregado-de-linq/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Jugando con colecciones Dictionary</title>
		<link>http://cromo.cda-ie.es/2009/12/24/jugando-con-colecciones-dictionary/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=jugando-con-colecciones-dictionary</link>
		<comments>http://cromo.cda-ie.es/2009/12/24/jugando-con-colecciones-dictionary/#comments</comments>
		<pubDate>Thu, 24 Dec 2009 14:23:01 +0000</pubDate>
		<dc:creator>Cubano</dc:creator>
				<category><![CDATA[Visual Basic 2008]]></category>

		<guid isPermaLink="false">http://cromo.cda-ie.es/?p=249</guid>
		<description><![CDATA[Esta entrada podría valer como la tercera de la serie &#8220;Mantener actualizados unos ComboBox&#8221;, pero va en modo corto, que es nochebuena y me dan ganas de hacer algo más acorde con la fecha (ver La tumba de las luciérnagas, por ejemplo). Hagamos un breve resumen: tenemos un formulario maestro-detalle de series y las releases [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify"><span style="font-family: Verdana; font-size: 10pt">Esta entrada podría valer como la tercera de la serie &#8220;Mantener actualizados unos ComboBox&#8221;, pero va en modo corto, que es nochebuena y me dan ganas de hacer algo más acorde con la fecha (ver <em>La tumba de las luciérnagas</em>, por ejemplo). Hagamos un breve resumen: tenemos un formulario maestro-detalle de series y las <em>releases</em> o distintas versiones de esas series. Como el número de <em>releases</em> por serie es pequeño (0 a 3 ó 4) y el número de campos de la tabla <em>releases</em> es amplio, en lugar de usar una rejilla o algún control parecido, decidí emplear un TabControl que mostrara tantas pestañas como <em>releases</em> y, en cada una, un UserControl con todos los datos. Es decir, esto:</span> </p>
<p style="text-align: center;"><img style="border: 0pt none; margin: 5px;" src="http://i114.photobucket.com/albums/n280/ercubano/Foro/pestagnarelease.jpg" alt="" width="400" height="200" /></p>
<p><span id="more-249"></span></p>
<p style="text-align: justify"><span style="font-family: Verdana; font-size: 10pt">Cada ComboBox del UserControl muestra el contenido de otra tabla (sí, son unas cuantas). Podría haber dejado los combos sólo para altas y modificaciones y haber usado una consulta más específica en lugar de traerme la tabla <em>releases</em> a capón, pero aquí no me gustó la idea (en otra aplicación con un problema parecido es lo que hice; claro, que también usaba SQL Server y pude crear una vista). En lugar de eso, aprovechando que todas esas tablas tienen (y tendrán) un pequeño número de registros (menos de veinte), decidí cargar todos esos datos en memoria. Bueno, no todos, sólo la clave primaria y el campo a mostrar en el ComboBox. Para ello hice una clase sencilla y una <a href="http://cromo.cda-ie.es/2009/07/31/mantener-actualizados-unos-combobox-ii/" target="_blank">BindingList personalizada</a> con métodos de ordenación específicos. También una enumeración con todas las tablas.</span></p>
<p style="text-align: justify"><span style="font-family: Verdana; font-size: 10pt">Junté todo en una colección tipo Dictionary. Sus pares de valores eran el valor de la enumeración (que identifica la tabla) y la BindingList personalizada con el conjunto de datos de la tabla. Ahora sólo queda pasar ese Dictionary a cada UserControl y que éste enlace cada ComboBox a la BindingList correspondiente. ¿Cómo? Pues, por ejemplo, creando otro Dictionary que para cada tabla (valor de la enumeración) tenga el ComboBox correspondiente. Así:</span> </p>
<table border="0" width="90%" align="center" bgcolor="#e6e6fa">
<tr>
<td>
<pre style="font-family: 'Courier New', Courier, monospace; font-size:9pt"><span style="color:#0000FF">Private</span> _MisCombos <span style="color:#0000FF">As</span> <span style="color:#0000FF">New</span> Dictionary(<span style="color:#0000FF">Of</span> enTablas, ComboBox)</pre>
</td>
</tr>
</table>
<p style="text-align: justify"><span style="font-family: Verdana; font-size: 10pt">Y en el constructor del UserControl:</span></p>
<table border="0" width="90%" align="center" bgcolor="#e6e6fa">
<tr>
<td>
<pre style="font-family: 'Courier New', Courier, monospace; font-size:9pt"><span style="color:#0000FF">With</span> _MisCombos
    .Add(enTablas.Fansub, cboFansub)
    .Add(enTablas.Subtitulos, cboSubtitulos)
    .Add(enTablas.Archivador, cboArchivador)
    .Add(enTablas.Video, cboCodecVideo)
    .Add(enTablas.Audio, cboCodecAudio)
    .Add(enTablas.Valoracion, cboRValoracion)
    .Add(enTablas.TipoArchivo, cboContenedor)
    .Add(enTablas.Soporte, cboSoporte)
    .Add(enTablas.Calidad, cboCalidad)
    .Add(enTablas.Resolucion, cboResolucion)
<span style="color:#0000FF">End</span> <span style="color:#0000FF">With</span></pre>
</td>
</tr>
</table>
<p style="text-align: justify"><span style="font-family: Verdana; font-size: 10pt">Lo siguiente es pasarle al UserControl el Dictionary con el contenido de las tablas, ya sea mediante una propiedad, un método o un constructor con parámetros. Supongamos que a ese Dictionary lo llamamos _LoQueHayEnLosCombos. Recordemos: ese Dictionary está formado por la enumeración de las tablas, enTablas, y por la BindingList personalizada, lista de una clase con dos propiedades, Id y Descripcion. Ya con todo eso junto, el enlace de cada combo con su tabla correspondiente se puede hacer así:</span></p>
<table border="0" width="90%" align="center" bgcolor="#e6e6fa">
<tr>
<td>
<pre style="font-family: 'Courier New', Courier, monospace; font-size:9pt"><span style="color:#008000">'Y, ahora, la magia:</span>
<span style="color:#0000FF">For</span> <span style="color:#0000FF">Each</span> Combo <span style="color:#0000FF">As</span> KeyValuePair(<span style="color:#0000FF">Of</span> enTablas, ComboBox) <span style="color:#0000FF">In</span> _MisCombos
    Combo.Value.DataSource = _LoQueHayEnLosCombos(Combo.Key)
    Combo.Value.DisplayMember = <span style="color:#B22222">"Descripcion"</span>
    Combo.Value.ValueMember = <span style="color:#B22222">"Id"</span>
<span style="color:#0000FF">Next</span></pre>
</td>
</tr>
</table>
<p style="text-align: justify"><span style="font-family: Verdana; font-size: 10pt">En fin, un ejemplo un poco tonto de cómo relacionar colecciones mediante Dictionary que compartan la misma Key. Si a alguien le sirve de algo, como siempre, me alegro.</span></p>
]]></content:encoded>
			<wfw:commentRss>http://cromo.cda-ie.es/2009/12/24/jugando-con-colecciones-dictionary/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Usando un TableAdapterManager</title>
		<link>http://cromo.cda-ie.es/2009/11/10/usando-un-tableadaptermanager/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=usando-un-tableadaptermanager</link>
		<comments>http://cromo.cda-ie.es/2009/11/10/usando-un-tableadaptermanager/#comments</comments>
		<pubDate>Tue, 10 Nov 2009 22:21:52 +0000</pubDate>
		<dc:creator>Cubano</dc:creator>
				<category><![CDATA[Visual Basic 2008]]></category>

		<guid isPermaLink="false">http://cromo.cda-ie.es/?p=218</guid>
		<description><![CDATA[Una de las entradas de este blog que más visitas registra hace referencia a un error en el código generado de los TableAdapterManager, un componente de acceso a datos para usarlo junto con datasets tipados y sus respectivos tableadapters. Dicho error fue solucionado en el SP1 de Visual Studio y de las versiones Express, por [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">Una de las <a href="http://cromo.cda-ie.es/2008/05/27/tableadaptermanager/" target="_blank">entradas</a> de este blog que más visitas registra hace referencia a un error en el código generado de los </span><span style="font-family:Courier New">TableAdapterManager</span><span style="font-family:Verdana">, un componente de acceso a datos para usarlo junto con datasets tipados y sus respectivos tableadapters. <a href="http://cromo.cda-ie.es/2008/08/01/a-vueltas-con-el-tableadaptermanager/" target="_blank">Dicho error</a> fue solucionado en el SP1 de Visual Studio y de las versiones Express, por lo que ya creía el tema olvidado. Sin embargo, parece que su uso despierta bastantes dudas, así que me vais a permitir que retome el tema con un pequeño ejemplo.<br />
</span></span></p>
<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">Un </span><span style="font-family:Courier New">TableAdapterManager</span><span style="font-family:Verdana"> es un componente que se genera al crear un dataset tipado. Por lo tanto, si no usamos un dataset tipado ni necesitamos saber que tal cosa existe. Un </span><span style="font-family:Courier New">TableAdapterManager</span><span style="font-family:Verdana"> automatiza el proceso de actualizar varias tablas relacionadas, manteniendo (o intentándolo) la integridad de los datos al hacer los update, insert y deletes en el orden apropiado. Hemos dicho que el </span><span style="font-family:Courier New">TableAdapterManager</span><span style="font-family:Verdana"> se crea con un dataset tipado, por lo que sólo tendrá utilidad si este dataset tipado tiene varias tablas relacionadas.<br />
</span></span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Veamos el siguiente ejemplo, un dataset tipado, SeriesDataSet, con tres tablas: Series, Género y GeneroSerie. GeneroSerie es la tabla que permite la relación n:m entre géneros y series (una serie puede tener varios géneros, puede ser un drama romántico de ciencia ficción, por ejemplo).<br />
</span></p>
<p><span id="more-218"></span></p>
<p style="text-align: center;"><img style="border: 0pt none; margin: 5px;" src="http://i114.photobucket.com/albums/n280/ercubano/Foro/dsSeries.jpg" alt="" width="450" height="351" /><br />
<span style="font-family:Courier New; font-size:10pt"><em>SeriesDataSet</em></span></p>
<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">Vamos a suponer que tenemos un formulario donde el usuario podrá dar de alta, modificar y eliminar series y sus géneros. Es decir, variarán los datos de las datatables Series y GeneroSerie. A la hora de pasar esos cambios de datos a la base de datos debemos tener presente la relación de las tablas. Así, a la hora de insertar nuevas filas deberemos insertar primero las series y luego sus géneros, pero a la hora de borrar deberemos borrar primero los géneros de la serie y luego la serie. Esto significa que no podemos hacer un procedimiento genérico llamando alegremente a los métodos </span><span style="font-family:Courier New">Update</span><span style="font-family:Verdana"> de los respectivos tableadapters. Pero podemos usar el método </span><span style="font-family:Courier New">UpdateAll</span><span style="font-family:Verdana"> del </span><span style="font-family:Courier New">TableAdapterManager</span><span style="font-family:Verdana"> y que sea él quien se coma el marrón. Algo así:</span></span></p>
<table border="0" width="90%" align="center" bgcolor="#e6e6fa">
<tbody>
<tr>
<td>
<pre style="font-family: 'Courier New', Courier, monospace; font-size:9pt"><span style="color:#0000FF">Public</span> <span style="color:#0000FF">Function</span> GuardaDatos(<span style="color:#0000FF">ByVal</span> dsSeries <span style="color:#0000FF">As</span> SeriesDataSet) <span style="color:#0000FF">As</span> <span style="color:#0000FF">Integer</span>
    <span style="color:#0000FF">Dim</span> Result <span style="color:#0000FF">As</span> <span style="color:#0000FF">Integer</span>
    <span style="color:#0000FF">Dim</span> tamSeries <span style="color:#0000FF">As</span> <span style="color:#0000FF">New</span> SeriesDataSetTableAdapters.TableAdapterManager
    <span style="color:#0000FF">Dim</span> taSeries <span style="color:#0000FF">As</span> <span style="color:#0000FF">New</span> SeriesDataSetTableAdapters.SeriesTableAdapter
    <span style="color:#0000FF">Dim</span> taGeneroSerie <span style="color:#0000FF">As</span> <span style="color:#0000FF">New</span> SeriesDataSetTableAdapters.GeneroSerieTableAdapter
    <span style="color:#0000FF">Using</span> cnConexion <span style="color:#0000FF">As</span> <span style="color:#0000FF">New</span> SqlServerCe.SqlCeConnection( _
                                <span style="color:#0000FF">My</span>.Settings.DefaultConnectionString)
        <span style="color:#0000FF">Try</span>
            <span style="color:#008000">'Le pasamos los TableAdapters al TableAdapterManager</span>
            tamSeries.SeriesTableAdapter = taSeries
            tamSeries.GeneroSerieTableAdapter = taGeneroSerie
            <span style="color:#008000">'Le pasamos la conexión que vamos a usar</span>
            <span style="color:#008000">'(De no hacerlo, se crearía la suya propia)</span>
            tamSeries.Connection = cnConexion
            <span style="color:#008000">'Abrimos la conexión. En VB2008 sin SP1 daría error</span>
            cnConexion.Open()
            Result = tamSeries.UpdateAll(dsSeries)
        <span style="color:#0000FF">Catch</span> ex <span style="color:#0000FF">As</span> Exception
            <span style="color:#0000FF">Throw</span> ex
        <span style="color:#0000FF">Finally</span>
            cnConexion.Close()
        <span style="color:#0000FF">End</span> <span style="color:#0000FF">Try</span>
    <span style="color:#0000FF">End</span> <span style="color:#0000FF">Using</span>
    <span style="color:#0000FF">Return</span> Result
<span style="color:#0000FF">End</span> <span style="color:#0000FF">Function</span></pre>
</td>
</tr>
</tbody>
</table>
<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">Resumiendo: hemos creado una instancia del </span><span style="font-family:Courier New">TableAdapterManager</span><span style="font-family:Verdana"> que VB2008 ha creado junto con los tableadapters del dataset tipado </span><span style="font-family:Courier New">SeriesDataSet</span><span style="font-family:Verdana">. El </span><span style="font-family:Courier New">TableAdapterManager</span><span style="font-family:Verdana"> necesita que le pasemos los tableadapters con los que va a trabajar, y eso es lo que hemos hecho. Como está tan tipado como el dataset, las datatables y los tableadapters, tiene tres propiedades distintas, cada una esperando su tableadapter. Habíamos dicho que sólo íbamos a modificar valores en la tabla </span><span style="font-family:Courier New">SeriesDataTable</span><span style="font-family:Verdana"> y en la tabla </span><span style="font-family:Courier New">GeneroSerieDataTable</span><span style="font-family:Verdana">, por lo que sólo necesitamos sus dos tableadapters correspondientes. De </span><span style="font-family:Courier New">GeneroTableAdapter</span><span style="font-family:Verdana"> aquí nos olvidamos.<br />
</span></span></p>
<p style="text-align: justify"><span style="font-size:10pt"><span style="font-family:Verdana">Ya sólo nos queda llamar al método </span><span style="font-family:Courier New"><strong>UpdateAll</strong></span><span style="font-family:Verdana"> pasándole el dataset de Series y listo. El </span><span style="font-family:Courier New">TableAdapterManager</span><span style="font-family:Verdana"> no tiene mucho más misterio. Básicamente trabajaremos únicamente con las propiedades de los respectivos tableadapters, el objeto </span><span style="font-family:Courier New">Connection</span><span style="font-family:Verdana">, el método </span><span style="font-family:Courier New">UpdateAll</span><span style="font-family:Verdana"> y la propiedad </span><span style="font-family:Courier New"><strong>UpdateOrder</strong></span><span style="font-family:Verdana">. UpdateOrder tiene dos valores posibles (y autoexplicativos): </span><span style="font-family:Courier New"><strong>UpdateInsertDelete</strong></span><span style="font-family:Verdana"><strong><br />
</strong>(primero hace los update, luego los insert y por último los delete) e </span><span style="font-family:Courier New"><strong>InsertUpdateDelete</strong></span><span style="font-family:Verdana">, que hace lo obvio. Podemos ver lo que hace el método UpdateAll, en todo caso, con el habitual &#8220;Ir a definición&#8221;.<br />
</span></span></p>
<p style="text-align: justify"><span style="font-family:Verdana; font-size:10pt">Y no hay más. Está bien para situaciones sencillitas, y se queda corto en otras. No lo he usado mucho, pero las veces que lo he metido me ha permitido ahorrarme un tiempecito que me ha venido bien para otras cosas. </span></p>
]]></content:encoded>
			<wfw:commentRss>http://cromo.cda-ie.es/2009/11/10/usando-un-tableadaptermanager/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

