¡Esta es una revisión vieja del documento!
Abrimos un nuevo proyecto vacío, añadimos un archivo de código también vacío (debemos señalar que el resultado será una aplicación para windows) y empezamos:
Para usar aplicaciones con formularios de windows debemos añadir en la carpeta references del explorador de proyectos las dll System.Windows.Forms (para trabajar con los formularios de windows) y System.dll, que son funciones base del sistema. Y luego incluirlas con using, de esa forma no será necesario a la hora (p.ej en una aplicación de consola) de escribir el writeline escribir la parrafada: System.Console.WriteLine(“..”); sino Console.WriteLine(“…”); también podemos incluir System.Console(“…”);.
Ahora añadimos el código dentro del espacio de nombres.
Introducimos la clase. Ésta será pública y que herede de la clase Form. Ahora podemos variar sus propiedades, p.ej. al introducir Text = “Tal”; cambiamos el caption del formulario, o Width = 5; la anchura. Aún así, en este caso podemos usar this.
En el ejemplo la función Main de la clase Ejemplo se crea a sí misma (mediante la clase application) en un objeto, al hacer esto se llama al constructor y este define el caption del formulario como “hola world”:
using System; using System.Windows.Forms; public class Ejemplo : Form { public static void Main() { Application.Run(new Ejemplo()); } public Ejemplo() { Text = "Hola World"; this.Width = 25; } }
El editor de formularios del Visual Studio usa la clase System.Drawing así que si lo usarmos habrá que referenciarla si no queremos que nos pete el código que él añada.
Ensamblados son las línias que definen la versión del ejecutable. Para usarlas has de hacer referencia a la clase System.Reflection y añadirlas en el código.
Yo las añado en otro archivo, en C# cualquier archivo que añadas al proyecto será compilado.
Se usan entre corchetes y con la cláusula assembly antes:
[assembly: Ensamblado(parámetros)]
Tenemos las siguientes: AssemblyTitle, AsseblyDescription, AssemblyConfiguration, AssemblyCompany, AssemblyProduct, AssemblyCopyright, AssembyTrademark, AssemblyCulture (Que especifica el lenguaje y el país del archivo. Peeero no se puede poner en un ejecutable. Para encontrar las siglas que corresponden al país que queramos las encontraremos en: www.ietf.org/rfc), AssemblyVersion (La versión que es una cifra compuesta de cuatro números separados por puntos 1.0.0.0 (version principal . v secundaria . revision . compilación). Podemos indicar que c# las introduzca automáticamente añadiendo un asterisco en el número de compilación (sólo para que introduzca la compilación) o en la revisión (y añadirá la compilación y la revisión): 1.0.*).
Es un objeto con métodos estáticos que controla la aplicación y los mensajes de windows sobre esta.
ApplicationContext es una clase que se nos permite pasarla al método Application.Run para iniciar una aplicación, de esa forma no es necesario un formulario. La aplicación no acabará hasta que no se llame al método ExitThread.
La estructura de lo que genera es muy similar al primer ejemplo de programa:
Una vez hemos definido los elementos que compondrán el formulario llega el momento de definir sus propiedades. Para esto debemos llamar a la función SuspendLayout() antes, una vez esté todo definido llamaremos a ResumeLayout para que los cambios se hagan efectivos, los parámetros para esta última (si se los queremos poner) son true y false, true para que realice los cambios pendientes YA, y false para que no.
Incluimos las librerías y creamos una clase que herede de Windows.Forms.
Creamos un nuevo objeto Form1 y lo lanzamos con Application.Run
Al crearse el formulario se definen todos sus cmponentes. Para indicar la función para un evento en concreto, cogemos el control, el evento tal y añadimos el evento con += y new EventHandler (porque será un evento de windows ya declarado anteriormente) y entre parentesis el nombre la función del evento, esta función deberá recibir dos parámetros: (object sender, EventArgs Arguments).
A medida que vamos añadiendo controles los iremos incluyendo con Controls.Add pasandole el control. Aunque también podemos pasarselos dentro de un array llamando a la función Controls.AddRange (new Control [] { control1, control2….}).
using System; using System.Drawing; using System.Windows.Forms; namespace Formulario { class Form1 : System.Windows.Forms.Form { private Button boton1 = new Button(); private TextBox tb = new TextBox(); public static void Main () { Form1 form = new Form1(); Application.Run (form); } public Form1 () { boton1.Location = new Point(25,30); boton1.Text = "Apretame"; boton1.Click += new EventHandler (Boton1Click); Controls.Add (boton1); tb.Text = "hola nena"; tb.Location = new Point(10,10); tb.Size = new Size(100,10); Controls.Add (tb); this.Size = new Size(150,100); } private void Boton1Click (object sender, EventArgs Arguments) { MessageBox.Show ("hola nena"); } } }
Lo primero que debemos hacer es indicar al compilador que la aplicación compilada será una biblioteca dinámica (Propiedades del proyecto à Resultado). El espacio de nombres será lo que luego usaremos en el using del otro archivo y, muy importante, la clase que crearemos deberá de ser pública.
namespace biblioteca { public class Contador { public static void MiraPar (int a) { if (a % 2 == 0) { System.Console.WriteLine("{0} es par",a); } } } }
Para usarla no hay que hacer nada realmente especial, sólo inculir en el using el namespace y hacer referencia a la .dll. También podemos usar perfectamente clases y objetos, aunque siempre deberán ser clases públicas.
using biblioteca; class MainClass { public static void Main () { for (int i =0; i <=100; i++) { Contador.MiraPar(i); } System.Console.ReadLine(); } }
Hay tres capas en el diseño de una aplicación:
Acciones de ayuda para el programador
Todos los controles tiene una propiedad llamada Tag, aquí podemos almecenar cualquier tipo de objeto relacionado al control al que le pertenece.
Los controles contienen eventos que saltan cuando se les añaden\quitan otros controles a estos: ControlAdded y ControlRemoved.
Parent Es el control que contiene (formulario, panel, groupbox…) al control de la propiedad.FindForm Recupera el formulario donde se encuentra el control.TopLevelControl Recupera el primer control que contiene a todos los demás.GetChildAtPoin Recupera el control hijo situado en estas coordenadas.TabStop Permite a un control acceder a él mediante la tecla TAB.Focused Retorna true si la propiedad tiene el foco.ContainsFocus Retorna true si él o uno de los controles hijos tiene el foco.Focus Da el foco al control.SelectNextControl Da el foco al siguiente control que le tocaGetNextControl Devuelve el siguiente control que le toca el foco.Existe una clase para pasar de colores html a colores .net, la encontramos en System.Drawing y se llama ColorTranslator.
ctrl.ForeColor = ColorTranslator.FromOle(OxFF00);
Podemos cojer colores predefinidos de la clase Color o de SystemColors.
La estructura control tiene los siguientes métodos: GetBrightness(), GetHue(), y GetSaturation().
Si en un botón ponemos como texto &Button1 cuando apretemos al ALT en tiempo de ejecución, la B del texto se subrayará y si, entonces, apretamos a la tecla B será como si se hubiese hecho click sobre el botón.
ImageFromFile Te devuelve una imágen desde un fichero.RotateFlip Rota la imágen en los grados indicados.GetThumbnailImage Te devuelve una imágen con los tamaños cambiados según el Size indicado.Handle Provides an IntPtr structure (a 32-bit integer on 32-bit operating systems) that represents the current control's window handle.RecreatingHandle Set to true while the control is being re-created with a new handle.GetStyle() and SetStyle() Sets or gets a control style bit. Generally you will use higher-level properties to accomplish the same thing.PreProcessMessage(), ProcessCmdKey(), ProcessKeyMessage(), ProcessKeyPreview(), and WndProc() These methods are involved in processing a Windows message, which is represented as a Message structure. You can override these methods and use them to process special messages Anchor (en Top, Left, Bottom y Right) de los componentes hace que un objeto cambie de tamaño con su formulario contenedor, si este cambia. Y Dock bloquea el control. ClientRectangle obtiene el tamaño total del formulario.Validate se produce cuando se sale del control, justo antes del lost focus. Podemos hacer que el cursor quede atrapado en ese control hasta que el usuario no haya dado un valor correcto indicando el valor de su CancelEventArgs.Cancel a true.KeyPressEventArgs, si su propiedad Handled la ponemos a true no surgirá efecto el presionado de la tecla.Cursor el cambio de cursor que se hará al estar sobre ese control del formulario.TopMost(bool) Si el formulario está sobre las demás ventanas del sistema.Sorted (bool) Si se guardan los datos ordenados.DataSource (array de strings) Los elementos del array pasarán a ser los del listbox.DrawMode (enum. DrawMode) Para indicar si se introducirá en formato texto o imagen.
Para que un string con \n puede introducirse en un textbox multiline no sólo ha de ser \n sino también \r\n. sabes?
DrawItem (el momento en el que se introduce el elemento), MesureItem (antes de introducir el elemento, calcula el espacio que éste necesita).DrawMode en OwnerDrawVariable.e.ItemHeight= 22;
Primero necesitamos llamar a los métodos del elemento introducido DrawBackGround y DrawFocusRectangle, para controlar cuando un elemento se selecciona, como se ha de dibujar… Y luego mediante Graphics.DrawString insertaremos la imagen, que será un string pero dibujado en formato imagen:
private void listBox1_DrawItem(object sender, System.Windows.Forms.DrawItemEventArgs e) { if (e.Index >= 0) { e.DrawBackground(); e.DrawFocusRectangle(); e.Graphics.DrawString(textBox1.Text, new Font(FontFamily.GenericSansSerif, 14, FontStyle.Bold), new SolidBrush(color[e.Index]), e.Bounds); } }
Si esto mismo lo quisiesemos hacer con un combo, o simplemente cambiar de color los fondos deberíamos el FillRectangle:
e.Graphics.FillRectangle(new SolidBrush(color[e.Index]), e.Bounds); e.Graphics.DrawString(nombre[e.Index], e.Font, new SolidBrush(color[e.Index + 1]),e.Bounds);
Los Bounds son los límites del elemento que se va a dibujar
En el evento mousedown de un listbox:
ListBox lb = ((ListBox)sender);
MouseEventArgs.int idx = lb.IndexFromPoint (VarPoint);
Primero vamos a crear un array de strings con los nombres de las fuentes existentes:
nombre = new string [FontFamily.Families.Length]; int i = 0; foreach (FontFamily FF in FontFamily.Families) { nombre[i] = FF.Name; i++; }
‘nombre’ ya lo teníamos definido. La clase estática FontFamily representa las fuentes del sistema. FontFamily.Families es una matriz con todas las fuentes del sistema.
Esta forma ya está bien, aún así, podríamos informarnos si la fuente está disponible en el modo (negrita, cursiva…(dentro de la enumeración FontStyle)) que queramos:
if(FF.IsStyleAvailable(FontStyle.Regular))
En cambio, para establecer una fuente usaremos el objeto Font. Y podemos mirar qué tamaño va a tener un string dibujado por pantalla con la fuente elegida, para ello usaremos el método de Graphics: MesureString, éste no es estático, por lo que dependerá de un objeto Graphics, se le pasa la cadena que medirá y el objeto Font que se le aplica y devuelve un Size. (El height puede ser el height del elemento a dibujar, lo podemos indicar en el método MeasureItem del combo).
En el evento DrawItem colocaríamos esto:
e.Graphics.DrawString(nombre[e.Index], new Font(nombre[e.Index], 10), new SolidBrush(SystemColors.WindowText), e.Bounds);
Y luego, cuando el elemento principal cambie (evento: SelectedIndexChanged) cambiamos la fuente del combo por la que ahora tiene el nombre que está en su propiedad texto.
Para agregar una nueva fila a una DataTable usaremos el método NewRow, que nos devuelve una DataRow que será la nueva y que añadiremos a su colección de filas que encontramos en su propiedad Rows.
Podemos escribir la DataTable en un archivo XML mediante el método WriteXML, este requiere que se haya asignado el TableName.
El DefaultDataView de una DataTable es el DataView por defecto enlazado a la tabla, con DataView podemos indicar como se mostrarán los datos. Contiene una propiedad llamada RowFilter que te filtra la tabla, a esta le pasamos un string que podría ser así: “ID > 4”.
Una DataTable contiene un método llamado Select, con este podemos hacer algo parecido que con el DataView, seleccionar un cierto número de filas, pero este te las devuelve.
Para usar un Timer necesitamos definir cuantos milisegundos por segundo tendrá el control. Esto lo haremos con la propiedad .Interval que le adjudicaremos un int.
Los métodos Start y Stop empezarán y encenderán el Timer.
El evento Tick (o elapsed) es el que se ejecutará a cada intervalo definido.
El primer paso para crear un control es, al abrir proyecto, hacer “nueva biblioteca de controles”. Ahí le añadimos todo lo que necesitemos. El control tendrá métodos, para añadirselos iremos a la vista de clases y desde ahí, haciendo botón derecho y “agregar método”, configurarlo es muy intuitivo, de la misma forma también podremos agregar una propiedad.
En el ejemplo realizado se crea un control con un label y un timer. Este tiene dos métodos públicos, start y stop, y una propiedad: Segundos. El programador deberá inicializar los segundos, en el set de éstos se copiará ese valor a una variable privada que irá decrementandose a cada intervalo del timer, este se pondrá en funcionamiento con el método start y se parará con el stop. Dentro del “tiempo” del timer, éste mirará si la variable no es 0, y si lo es llamará a un evento que habremos declarado antes, pero que será codificado por el usuario. Recordemos….
public event EventHandler Expired;set { m_Seconds = value; }Expired(this, e);
Al compilarlo se creará una .dll, deberemos hacer referencia a esta (sin el using) y para incluir nuestro control en el panel de componentes iremos a él y haremos click derecho à Personalizar cuadro de herramientas y en la pestaña de Componentes .Net Framework examinaremos para buscar el nuestro, lo añadiremos y seleccionaremos, a partir de entonces ya tará en el panel y lo incluiremos en el formulario (el explicado antes se verá como un label).
En el ejemplo se ponen dos botones, uno que inicializa la propidad Segundos y llamará al método Start del control. El otro al Stop. Ahora vamos a codificar el evento “Expired” (el del ejemplo). En el modo diseño, hacemos click sobre éste y vamos al panel de las propiedades, allí hay un botón para ver los métodos (parecido a un rayo), y con nuestro control seleccionado buscamos entre estos métodos el llamado Expired declarado dentro de él, doble click y empezamos a codificar. Yo he puesto un message box diciendo que ya ha finalizado.
Los treeViews nos sirven para agrupar los objetos mediante nodos (como en el regedit). Y a medida que vayamos entrando en uno éste se abrirá y podrá contener más.
Para añadir un nodo principal a un TreeView sólo tendremos que declarar un objeto TreeNode. A su constructor se le pasa un string que es el título que este tendrá, y dos números enteros, el primero el índice de la imagen del nodo que se mostrará cuando este esté seleccionado y el segundo cuando no. Entonces se lo pasamos al método Add del TreeView.Nodes.
Podemos referenciar un conjunto de nodos al TreeNodeView, entonces, cada vez que añadamos nodos a este conjunto se añadirán al TreeNodeView. Este objeto es el llamado TreeNodeCollection, al definirlo lo que haremos será igualarlo a un objeto TreeNode.Nodes, y cada vez que llamemos a su método Add pasandole un TreeNode se añadirán.
Podemos formatear un treeView mediante métodos como HotTracing, que remarca los elementos al pasar sobre ellos, o ShowPlusMinus, que hace que se muestren o no los signos de más y de menos en sus nodos…
Al constructor también podemos pasarle dos enteros, estos representan a los índices (si tá asignado) de un imagelist, cuando está seleccionado y cuando no respectivamente.
Los listView son unos componentes que nos permiten ver los datos en una lista. Para dar formato a un listView debemos recordar que podemos añadir columnas si su propiedad View tiene el valor Details de la enumeración View.
Para añadir una columna al listView sólo deberemos hacer: <nombre del listview>.Colums.Add (string nombre, int ancho, valor de la enum HorizontalAligment)
Para añadir un registro a un listView necesitamos tener un objeto ListViewItem, su constructor recibe, principalmente, un string o array de strings, si fuese un array de strings cada uno de ellos pertenecería a una columna del listView. Y luego para añadir este ListViewItem haríamos: <objeto listview>.Items.Add ( <objeto listviewitem> )
Podemos añadir de otra forma las columnas y los datos al listview. Si tenemos un ListView y queremos añadir columnas necesitamos declarar tantos objetos “columnheader” como columnas vayamos a añadir, a los constructores de estos objetos no son necesarios pasarle parámetros. Luego, mediante las propiedades TextAlign, Text, Width… podemos ir modificándolo.
Para añadirlas:
listView1.Columns.AddRange(new System.Windows.Forms.ColumnHeader[]) { this.columnHeader1, this.columnHeader2, this.columnHeader3, this.columnHeader4});
Estas columnas representarán a un SubItem de un ListViewItem. Es decir, para añadir elementos al listView montaremos un objeto ListViewItem, en el constructor podemos pasarle el elemento de la primera columna, y mediante el método .SubItems.Add(<string>) iremos pasándole los elementos a las columnas pertinentes. Luego deberemos llamar al método .Items.Add(<ListViewItem>) de un listView1 para añadirlo.
Un statusBar es un componente que se encuentra en el pie del formulario. No tiene gran misterio ya que su principal propiedad es Text, mediante esta podemos indicar lo que nos aparece escrito. También existen los Panels, que para que puedan verse debemos tener ShowPanels a trae.
Un splitter es un separador de controles que permite modificar el tamaño de estos siempre manteniendo su relación. És algo complejo de encuadrar, sólo decir que los dos controles deben de estar pegados a él, la MSDN dice esto:
Por ejemplo, para crear una ventana similar a la del Explorador de Windows, agregue un control TreeView a un formulario y establezca su propiedad Dock en DockStyle.Left. Agregue un control Splitter al formulario y establezca también su propiedad Dock en DockStyle.Left. Para terminar el diseño del formulario, agregue un control ListView y establezca su propiedad Dock en DockStyle.Fill para que ListView ocupe el resto del espacio del formulario.
En tiempo de ejecución, el usuario podrá cambiar el ancho del control TreeView (y del control ListView) moviendo el control Splitter.
Este objeto estático muestra un mensaje al usuario. Su método más útil es Show y podemos llamarlo de varias formas ya que está sobrecargado. Sus parámetros son:
string).string).MessageBoxButtons)MessageBoxIcon)
La tecla apretada por el usuario será devuelta en una enumeración DialogResult.
El objeto ImageList, en el modo de desarrollo de Visual Studio .NET no es difícil de manejar, se añade al formulario con un doble clic, y mediante la propiedad .images vamos añadiendo imágenes, a cada una de estas imágenes se le asigna un índice.
Luego, hay controles que aceptan la propiedad imageList, podemos elegir la imagelist creada para ellos, entonces en sus métodos que pedian una imagen le pasaríamos el número de indice que le ha dado a la imagen correspondiente.
A un botón también podemos asignarle un imagelist y un imageindex, pero tiene otras propiedades para el control de imágenes: ImageAlign que indica la posición de esta, Image que se le pasa un string que es la dirección del archivo…
this.imageList.Images.Add(Img.getImg("Clientes.Menu.arbol_newclient.bmp"));
Los tooltips son las etiquetitas que aparecen al dejar el ratón sobre algún componente, en C# estas etiquetas corresponden a la clase ToolTip, una vez tengamos un objeto de ésta podemos asignarle un elemento del formulario.
El constructor de ésta clase no recibe ningún parámetro. Y sus propiedades más usadas son Active y InitialDelay, la primera, Active, es una booleana, si está a true el tooltip podrá usarse. InitialDelay recibe un int, este corresponde al tiempo (en milisegundos) que esperará el tooltip para mostrarse.
Para asignar el tooltip a un componente del formulario llamaremos al método de este objeto SetToolTip, éste recibe dos parámetros, el primero el nombre del control y el segundo un string que sería el texto que se mostraría.
Existen varias clases que heredan de esta clase, estas son las referentes a los objetos correspondientes a guardar archivo, abrir archivo, imprimir… las comunes en todos los programas para el dialogo del usuario con el programa y genéricos de windows. Éstos son:
Un método heredado es .ShowDialog(), con él se muestra el cuadro de dialogo correspondiente. Luego existen propiedades propias de cada clase, por ejemplo, en ColorDialog, para permitir que el usuario además de coger los colores predefinidos por el sistema coja otros personalizados hay que poner .AllowFullOpen a true.
Existen otras propiedades que son comunes para cada uno, por ejemplo Title, en el que se indica el título del cuadro de diálogo. Para saber qué ha sido presionado por el usuario ShowDialog devuelve un DialogResult, si este es DialogResult.OK significará que el usuario ha presionado “aceptar”.
Luego, tanto en PageSetupDialog como en PrintDialog, antes de llamar al .ShowDialog debemos de pasarle a la propiedad Document un objeto de la clase System.Drawing.Printing.PrintDocument.
OpenFileDialog y SaveFileDialog tienen propiedades muy parecidas. La propiedad Filter es un string correspondiente a lo que se mostrará en el filtro para visualizar los objetos, ésta irá compuesta por una cadena (la que se mostrará en el combo), una barra vertical ( | ) y el filtro que se aplicará, si queremos añadir otro componente añadiríamos otra barra vertical. InicialDirectory será en el directorio en el que se inicie el cuadro de diálogo.
OpenFileDialog OpenFile = new OpenFileDialog(); OpenFile.Filter = "Jpeg Files (.jpg) |*.jpg| Bitmap Files (*.bmp) |*.bmp| All files (*.*) |*.*"; OpenFile.FilterIndex = 3; OpenFile.InitialDirectory = Directory.GetCurrentDirectory(); OpenFile.Multiselect = true; if (OpenFile.ShowDialog() == DialogResult.OK) { textBox1.Text = "Ficheros elegidos: "; foreach (string str in OpenFile.FileNames) textBox1.Text += str + " "; FileInfo FInf = new FileInfo(OpenFile.FileName); textBox1.Text += "\tDirectorio: " + FInf.DirectoryName; }
Clase usada para el control de animaciones en archivos tipo gif.
Representa el cursor que se muestra. Por ejemplo, si queremos cambiar el cursor un momento mientras se ejecuta una rutina, guardaríamos el cursor actual en un objeto cursor, luego el ‘Cursor.Current’ lo cambiamos por el elemento deseado de la enumeración Cursors y al acabar a ‘Cursor.Current’ le asignamos el que habíamos guardado.
Es una clase que encontramos en System.Windows.Forms, es un extensor de propiedades que, al colocarse en un contenedor nos permite que al estar sobre un control y clicar F1 nos aparezca un pequeño pop-up donde nos muestra un texto introducido en la Propiedad añadida HelpEstring.
Existe un componente COM llamado SQLDMO, este puede ser usado para ver las Bases de Datos activas, los procedimientos almacenos de una DB, o sus tablas.
La clase System.Data.Common.DataTableMapping permite al DataAdapter que una DataTable use distintos nombres de columna que en una DB.
OleDbDataAdapter da = new OleDbDataAdapter(strSQL, strConn); da.TableMappings.Add("Table", "Customers"); DataSet ds = new DataSet(); da.Fill(ds);
En un DataSet puedes refrescar tablas por separado:
da1.Fill(ds.Tables["Table1"]); da2.Fill(ds.Tables["Table2"]);
Podemos crear una columna que se vaya incrementando a una tabla del dataset, ahí pondremos los números de fila para luego mostrarlos por páginas:
DataTable tbl = ds.Tables["Customers"]; DataColumn col = tbl.Columns.Add("RowID", typeof(int)); col.AutoIncrement = true; col.AutoIncrementSeed = 1; col.AutoIncrementStep = 1; da.Fill(ds, "Customers"); DataView vue = new DataView(tbl); int intPageSize = 10; int intPageNum = 3; vue.RowFilter = "RowID > " + (intPageNum - 1) * intPageSize + " AND RowID <= " + intPageNum * intPageSize; DataRowView row; for (int intCounter = 0; intCounter < vue.Count; intCounter++) { row = vue[intCounter]; Console.WriteLine(row["RowID"] + "\t" + row["CustomerID"] + "\t" + row["CompanyName"]); }
Podemos crear una columna a una tabla mediante una expresión, es decir, que contenga un valor relacionado con otros datos de otras columnas y que se actualice automáticamente:
DataSet ds = new DataSet(); DataTable tbl = ds.Tables.Add("Order Details"); ... tbl.Columns.Add("Quantity", typeof(int)); tbl.Columns.Add("UnitPrice", typeof(Decimal)); tbl.Columns.Add("ItemTotal", typeof(Decimal), "Quantity * UnitPrice");
Guardar en una tabla un array de objetos:
object[] aValues = {"ALFKI", "Alfreds Futterkiste", "Maria Anders", "030-0074321"}; ds.Tables["Customers"].LoadDataRow(aValues, false);
Podemos buscar una fila que contenga un valor concreto:
rowCustomer = ds.Tables["Customers"].Rows.Find("ANTON");
Una DataRow tiene tres métodos interesantes: BeginEdit, EndEdit o CancelEdit. Al llamar a BeginEdit se crea un buffer en el que se van guardando los cambios que se realizan en la fila, si se llama a EndEdit los cambios se guardan, pero si se llama a CancelEdit la fila se queda tal y como taba.
El método de una DataRow IsNull, al que se le pasa un string con nombre de columna sería el que te indica si ese campo es nulo en la DB. Para asignar un valor nulo a un campo de una datarow lo haremos asignandole System.DBNull.Value.
Podemos relacionar dos columnas de las tablas de un DataSet mediante el objeto System.Data.DataRelation, una vez tenemos los campos relacionados podemos acceder a sus relacionados de forma fácil. Por ejemplo, relacionamos los campos A con los de B, mediante el método GetChildRows de una DataRow de A podemos ver sus elementos de B relacionados y mediante el método GetParentRow de una DataRow de B podemos ver al registro que esta está relacionada.
Puedes desactivar el control de índices en una DataTable relacionada mediante el método BeginLoadData mientras esta se carga. No olvidar de llamar luego al EndLoadData.
Para las busquedas existen ciertos trucos:
strFilter = "State LIKE 'New %'"
strFilter = "State LIKE '% Dakota'"
Ordenar fechas:
strCriteria = "OrderDate >= #01/01/2002# AND OrderDate < #02/01/2002#"
DataTable tbl = new DataTable(); da.Fill(tbl); DataRow[] aRows = tbl.Select("Country = 'USA' AND City <> 'Seattle'");
DataRow[] aRows = tbl.Select("Country = 'USA' AND City <> 'Seattle', "City DESC"");
Puedes moverte por una DataView mediante los DataRowView, aún así, un objeto de esta clase no tiene el suficiente poder para acceder a la fila con los datos verdaderos, para ello usaremos la propiedad Row.
Métodos como Find o FindRows te permiten encontrar DataRows con ciertas características en una DataView.
La propiedad Count de una DataView te indica el número de filas visibles que contiene.
Con un select no sólo podemos llenar DataSets sino también DataTables:
string strConn, strSQL; strConn = "Provider=SQLOLEDB;Data Source=(local)\\NetSDK;" + "Initial Catalog=Northwind;Trusted_Connection=Yes;"; strSQL = "SELECT OrderID, ProductID, Quantity, UnitPrice " + "FROM [Order Details] WHERE OrderID = 10503 " + "ORDER BY ProductID"; OleDbDataAdapter da = new OleDbDataAdapter(strSQL, strConn); DataTable tbl = new DataTable("Order Details"); da.Fill(tbl);
Mediante comando parametrizados. Primero crea métodos que te montan los comandos (inserts, updates, deletes), luego crea métodos que llaman a dichos comandos y que se les pasa una row donde están los datos y el comando, ellos irán colocando los datos en los parámetros. (Mira el archivo: Creación de comandos parametrizados)
Teniendo una tabla con dos campos:
Un procedimiento almacenado para insertar imágenes:
use Alfred go alter procedure insertImg @img as image as insert into images (img) values (@img) go
Creamos un método insertar imágenes:
System.Windows.Forms.OpenFileDialog dialog = new OpenFileDialog(); if (dialog.ShowDialog() == DialogResult.OK) { System.IO.FileStream stream = new System.IO.FileStream(dialog.FileName, System.IO.FileMode.Open, System.IO.FileAccess.Read); byte[] buffer = new byte[stream.Length]; stream.Read(buffer, 0, (int)stream.Length); stream.Close(); deco.Components.decoSQLConexion con = new deco.Components.decoSQLConexion("Alfred", "S_GROUPDECO"); if (con.Open()) { deco.Components.SQLParametros[] pars = new deco.Components.SQLParametros[1]; pars[0] = new deco.Components.SQLParametros("@img", buffer); pars[0].Tipo = deco.Components.decoDBVarTipo.imagen; con.executeProcedure("insertImg", pars); } }
Método leer imágenes:
public byte[] getImage (string select) { try { SqlCommand sqlCommand = new SqlCommand(select); sqlCommand.Connection = this.sqlCon; return ((byte[])sqlCommand.ExecuteScalar()); } catch (Exception e) { System.Windows.Forms.MessageBox.Show(e.Message, "Error en decoSQLConexion", System.Windows.Forms.MessageBoxButtons.AbortRetryIgnore , System.Windows.Forms.MessageBoxIcon.Error); return null; } }
Método leer método leer imágenes
deco.Components.decoSQLConexion con = new deco.Components.decoSQLConexion("Alfred", "S_GROUPDECO"); if (con.Open()) { System.IO.MemoryStream stream = new System.IO.MemoryStream(con.getImage("select img from images where id=1"), true); System.Drawing.Bitmap bmp = new Bitmap(stream); this.pictureBox1.Image = bmp; stream.Close(); }
Para crear un formulario MDI padre con formularios hijos debemos declarar un formulario como padre, indicando, poniendo a true, su propiedad isMdiContainer.
En el padre cargamos los nuevos formularios, pero a estos se les indica que tienen un padre:
Form2 frm = new Form2(); frm.MdiParent = this; frm.Show();
No podemos hacer dock con los formularios hijos, pero sí que podemos arrastrar controles al formulario padre y hacer con ellos dicho dock.
Podemos usar ciertas propiedades para hacer dichos formularios más atractivos:
frm.Text = ""; frm.ControlBox = false; frm.WindowState = FormWindowState.Maximized;
Para saber los formulários hijos que tiene estan en el array de formularios mdiChildren.
Para llenar toda la parte rellenable con el formulario hijo:
private void FillActiveChildFormToClient() { Form child = this.ActiveMdiChild; Rectangle mdiClientArea = Rectangle.Empty; foreach(Control c in this.Controls) { if(c is MdiClient) mdiClientArea = c.ClientRectangle; } child.Bounds = mdiClientArea; }
Son aplicaciones ejecutables de larga duración, pueden iniciarse al iniciarse el sistema o manualmente.
Para empezar a programar debemos referenciar los siguientes componentes: System, System.Configuration.Install y System.ServiceProcess. Luego, debemos incluir, usando using: System.ServiceProcess, System.Configuration.Install, System.ComponentModel. Ahora ya podemos usar todas las clases necesarias. La configuración del proyecto no es nada especial, únicamente debemos indicar que es una aplicación para windows.
La clase principal ha de heredar de System.ServiceProcess.ServiceBase y ser pública. Un servicio tiene una serie de métodos que representan la vida de éste:
Normalmente no se les pasan parámetros, pero a los que se les pasa esta es su definición:
protected virtual void OnCustomCommand(int command);protected virtual void OnStart(string[] args);
Para poder llamar a los métodos en el servicio debemos poner algunas propiedades a true, por ejemplo a CanStop, CanPauseandContinue…
Es importante indicar el nombre del servicio mediante la propiedad .ServiceName a la que se le pasa un string.
Otra propiedad, AutoLog hace que el servicio vaya escribiendo en el registro de sucesos. Aún así podríamos escribir en el registro de sucesos con la siguiente función: System.Diagnostics.EventLog.WriteEntry (<nombre del servicio>, <string que escribe>). Pero para usarlo debemos recordar incluir System.Diagnostics.
En el punto de entrada de la clase principal del programa, el método estático Main, ha de hacer correr el proceso (no iniciarlo, por lo tanto no se llamará al método OnStart), para ello debemos llamar al método Run de la clase ServiceBase, si sólo quisiesemos añadir uno nos bastaría con: ServiceBase.Run (new <nombre de la clase del servicio>());
Aún así podemos hacer correr a una serie de servicios con:
System.ServiceProcess.ServiceBase[] ServicesToRun; ServicesToRun = new System.ServiceProcess.ServiceBase[] { new <servicio1>(), new <servicio2>(), … }; System.ServiceProcess.ServiceBase.Run(ServicesToRun);
Ejemplo…
public class AlfServ : System.ServiceProcess.ServiceBase { public AlfServ() { this.ServiceName = "AlfServ"; this.CanStop = true; } public static void Main () { System.ServiceProcess.ServiceBase[] ServicesToRun; ServicesToRun = new System.ServiceProcess.ServiceBase[] { new AlfServ() }; System.ServiceProcess.ServiceBase.Run(ServicesToRun); } protected override void OnStart(string[] args) { System.Diagnostics.EventLog.WriteEntry(this.ServiceName, this.ServiceName + " iniciando"); } protected override void OnStop() {} protected override void OnCustomCommand(int command) { switch (command) { case 0: System.Diagnostics.EventLog.WriteEntry(this.ServiceName, "Se ha llamado a un comando personalizado"); break; default: break; } }
Un instalador es una clase que indicará como se deberá instalar el servicio, para poder instalar este servicio debe existir un instalador obligatoriamente.
Para instalar y desinstalar un servicio manualmente usamos la herramienta de .NET installutil. Si sintaxis es la siguiente: installutil <nombre del archivo del servicio> para instalar y installutil /u <nombre del archivo del servicio> para desinstalar.
Un instalador es una clase que hereda de System.Configuration.Install.Installer y ha de ser, por huevos, pública. Debemos aplicarle el atributo RunInstaller, poniendo el parámetro de su constructor a true.
El instalador ha de tener dos objetos “instaladores”: un ServiceProcessInstaller y un ServiceInstaller. Con ellos podemos definir…
Luego debemos añadir estos objetos “instaladores”, para ello llamaremos al método Installers.AddRange() si lo que le vamos a pasar es un array de instaladores (como en este caso son dos será al que llamemos).
[RunInstaller(true)] public class AlfServIns : System.Configuration.Install.Installer { System.ServiceProcess.ServiceProcessInstaller ServProcIns; System.ServiceProcess.ServiceInstaller ServIns; public AlfServIns () { ServProcIns = new ServiceProcessInstaller(); ServIns = new ServiceInstaller(); ServProcIns.Account = ServiceAccount.LocalSystem; ServIns.StartType = ServiceStartMode.Manual; ServIns.DisplayName = "Servicio del Alfred"; ServIns.ServiceName = "AlfServ"; this.Installers.AddRange(new System.Configuration.Install.Installer[] { this.ServIns, this.ServProcIns}); } }
Mediante el objeto podemos ServiceController controlar los servicios del sistema. Una vez pasemos a su constructor el nombre del servicio o el nombre que se muestra podemos pararlo (método Stop), iniciarlo (método Start), mandarle un comando personalizado (método ExecuteCommand, pasandole entre paréntesis el número de comando), mirar su estado (propiedad status)…
Para crear estas aplicaciones debemos hacer uso de las directivas:
Un socket cliente es aquel que conecta con uno servidor y luego manda datos. Para conectar con un servidor necesita un punto final (éste está formado por una dirección IP y por un puerto). La implementación no es difícil:
Para crear un socket, el constructor recibe varios parámetros: que tipo de redes usaremos (dentro de la enumeración AddressFamily), el tipo de socket (enumerción SocketType) y el protocolo (enumeración ProtocolType).
Para conectarlo usamos el método .Connect al que hay que pasar un IPEndPoint, a este tipo de objeto se le pasa un IPAddreess y un entero que sería el puerto, para obtener la IPAddress llamaríamos al método estático IPAddress.Parse pasandole un string.
Para conseguir los bytes que son un string cogeríamos dicho string y mediante el método estático Ecoding.ASCII.GetBytes pasandole el string, nos devolvería el array de bytes.
using System; using System.Text; using System.Net; using System.Net.Sockets; namespace Client { class Client { private Socket sock; public Client () { IPEndPoint EP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1981); sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); sock.Connect(EP); } public void Manda (string text) { byte[] txt = Encoding.ASCII.GetBytes(text); if (sock.Connected) sock.Send(txt); } static void Main(string[] args) { string temp = ""; Client C = new Client(); do { C.Manda(Console.ReadLine()); } while (temp != "exit"); } } }
La aplicación servidor debe crear un socket que espera las conexiones entrantes, luego cuando llega una le pasa la responsabilidad a otro.
Tras crear el socket debemos asociarlo con un punto final local, esto lo haríamos con el método Bind, y luego dejarlo escuchando mediante el método Listen y pasandole como parámetros el máximo de conexiones que puede recibir. También debemos indicar la función que será llamada cuando entre una conexión (con un AsyncCallback, dicha función deberá recibir como parámetro un IASyncResult) y el socket que cargaremos con los datos de ésta.
En la función encargada de recibir los datos, lo primero que haremos será cerrar la conexión con el método del socket que ha estado escuchando EndAccept pasándole como parámetros los parámetros de la función y asignando lo devuelto al socket encargado de las conexiones.
Para recibir los datos debemos crear una estructura u objeto que ser donde se guarden, éste debe contener un array de bytes (ya que es lo que te enviarán), y mediante el método BeginReceive empezamos a recibir los datos. Luego debemos pasarlos a string. Y si pensamos recibir más datos de esa conexión deberíamos volver a asignar la función que acometerá con dicha acción.
using System; using System.Net; using System.Net.Sockets; namespace Server { struct StateObject { public byte[] buffer; } class Server { private Socket sock; private Socket receptor; public Server() { sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP); IPEndPoint EP = new IPEndPoint(System.Net.IPAddress.Any, 1981); sock.Bind(EP); sock.Listen(10); sock.BeginAccept(new AsyncCallback(Recibe), receptor); } private void Recibe(IAsyncResult ar) { StateObject SO; Console.WriteLine("Está recibiendo datos"); receptor = sock.EndAccept(ar); Console.WriteLine("IP remota: " + receptor.RemoteEndPoint.ToString()); SO.buffer = new byte [10]; Preparado(SO); } private void Preparado (StateObject SO) { SO.buffer = new byte [SO.buffer.Length]; receptor.BeginReceive(SO.buffer, 0, SO.buffer.Length, SocketFlags.None, new AsyncCallback(Lee), SO); } private void Lee (IAsyncResult ar) { StateObject SO = (StateObject)ar.AsyncState; Console.WriteLine(System.Text.Encoding.ASCII.GetString(SO.buffer)); Preparado(SO); } static void Main(string[] args) { Server s = new Server(); Console.ReadLine(); } } }
private void PreparaRecepcion (Socket socket) { socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(Recibe), socket); } private void Recibe (IAsyncResult ar) { Socket tmpSock = (Socket)ar.AsyncState; int nBytesRec = tmpSock.EndReceive( ar ); if( nBytesRec > 0 ) { textBox2.Text += Encoding.ASCII.GetString(buffer, 0, nBytesRec); PreparaRecepcion(tmpSock); } }
Aun así hay otra forma para recibir datos. Es De esta forma no necesitamos ninguna clase StateObject.
Es un objeto que se encarga de escuchar por el puerto que indiquemos como parámetro al construirlo, llamando al método Start comienza a escuchar y cuando el método Pending devuelva true éste habrá recibido una petición de conexión. AcceptSocket aceptará esta petición y la podremos asignar a un socket.
worker = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); TcpListener listener = new TcpListener(1981); listener.Start(); do { if (listener.Pending()) { worker = listener.AcceptSocket(); Byte[] byteDateLine = System.Text.Encoding.ASCII.GetBytes( strDateLine.ToCharArray() ); worker.Send( “bienvenido!”); PreparaRecepcion (worker); } else { Thread.Sleep(100); } } while (true);
Para que una clase sea, por defecto, visible en la barra de herramientas de visual studio ha de ser un componente o un control (Objeto que hereda de System.ComponentModel.IControl o System.ComponentModel.Control).
Si una propiedad de un control coge o devuelve una enumeración, en la ventana de propiedades aparecerá como combo.
Atributos para propiedades / eventos
[RefreshProperties(RefreshProperties.All)]Atributos para clases
[Browsable(true), Description("Es una prueba"), Category("Pruebas")]
Atributos:
Esto obliga a una propiedad a mostrarse
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
Esto hace que todas las propiedades de una clase se puedan mostrar (como en el size)
[TypeConverterAttribute(typeof(System.ComponentModel.ExpandableObjectConverter))]
Pero para que esto vaya, en la propiedad que es la clase anterior de la clase en la que está:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
Impide que se vea en el editor
EditorBrowsable
Podemos quitar propiedades de un control en diseño:
public class decoTitulo_Design : System.Windows.Forms.Design.ControlDesigner { protected override void PostFilterProperties(IDictionary properties) { properties.Remove("Cursor"); properties.Remove("Tag"); properties.Remove("BackgroundImage"); base.PostFilterProperties (properties); } }
Indicando, claro está el diseñador antes:
[System.ComponentModel.Designer(typeof(decoTitulo.decoTitulo_Design))]
Para trabajar con GDI+ usaremos el objeto Graphics. Iremos dibujando y pintando sobre el formulario, pero debemos hacerlo dentro de una función que se llame cada vez que se repinte el formulario, por ejemplo: OnPaint.
Crearemos un objeto grafics que será igual al que el formulario devuelva de su método CreateGraphics y sobre este iremos añadiendo los trazos.
Para trabajar con ‘pens’ necesitaremos hacer uso de System.Drawing.Drawing2d;
El objeto Pen recibe como parámetros en su constructor el color y el tamaño (o podemos pasarle una brocha que defina estos parámetros) con su color y un entero que sea su ancho. Los colores los encontramos dentro de una enumeración llamada ‘Color’.
Para dibujar una línia (p.ej.) llamaremos al método DrawLine del objeto Graphics, pasándole el pincel y dos puntos (x,y).
protected override void OnPaint(PaintEventArgs e) { Graphics g = this.CreateGraphics(); Pen p = new Pen(Color.Red, 50); g.DrawLine(p, new Point(0,0), new Point(this.Size.Width, this.Size.Height)); }
También podremos dibujar una elipse llamando al método DrawEllipse… Y así.
O elegir el trazo con el método DashStyle del objeto Pen, los trazos están en una enumeración llamada DashStyle.
En una línia podemos cambiar los estremos. Para cambiar el primer extremo llamaremos al método del pincel StartCap y al último EndCap, antes de llamar a DrawLine, pasandole uno de los miembros de la enumeración LineCap.
p.EndCap = LineCap.RoundAnchor; p.DashStyle = DashStyle.DashDot;
C# puede leer archivos de imágenes jpg, bmp o ico, el objeto encargado de esto es Image, sólo tenemos que crear un objeto Image como nuevo Bitmap pasandole la ruta. Luego, en el OnPaint, la pasaremos a un objeto Graphics mediante el método DrawImage.
Declaramos:
Image img;
Inicializamos:
public Form1() { img = new Bitmap(@"c:\gollum.jpg"); InitializeComponent(); }
Llamamos a DrawImage
protected override void OnPaint(PaintEventArgs e) { Graphics g = this.CreateGraphics(); g.DrawImage(img,0,0); }
Podemos, en vez de pasarle dos enteros pasarle ‘ClientRectangle’ y se nos adaptará al tamaño del formulario.
Podemos acceder a los datos de la imagen mediante las propiedades de este objeto: Height, Width…
O modificarla mediante algunos eventos: RotateFlip (pasandole elementos de la enumeración RotateFlipType)…
Existe el MeasureString que es muy útil para ubicar el texto.
Cogemos el objeto Graphics del picturebox y luego usamos el método DrawString. Este ejemplo se hace con el evento Paint de un picturebox.
private void pictureBox1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; g.DrawString("asfkjslf", new Font("Verdana", 10.0f), new SolidBrush(Color.Black), 0.0f, 0.10f); }
Para ello usaremos la function setBounds, con esta, el control cambia de posición o de tamaño sin producir el efecto de parpadeo.
Debido a que el bucle colapsa todos los recursos del sistema, ciertos controles y componentes no llegan a dibujarse si la acción que realizan está dentro de este bucle. Para que la aplicación vuelva a dibujarse y a realizar las acciones pendientes debemos llamar al método Application.DoEvents().
System.Windows.Forms.Cursor.Position
<control>.PointToClient(<point>)
<control>.PointToScreen(<point>)
A partir de la versión 2005 acceder a un control desde un thread distinto al que se creó se controla los errores de concurrencia que se puedan producir, las distintas soluciones pueden ser:
CheckForIllegalCrossThreadCalls de la clase Control a false. Esta es la solución más guarra, ya que el error puede seguir produciendose.RunWorkerAsync del BackgroundWorker. Cuando acaba la tarea llama al evento RunWorkerCompleted.Invoke del control. InvokeRequired.true llamaremos a Invoke con un delegado que hace la llamada al control.false accederemos al control directamente.