Arquitectura - Definición de un Data Access Component (con un ejemplo) Parte 3

Hola Qué Tal?

En esta ocasión, no he dejado pasar tanto tiempo para terminar la trilogía del uso de Data Access Component con un ejemplo.

Bien, pues en este artículo veremos el uso del componente ya creado, cómo extenderemos la funcionalidad del componente y cómo lo aplicamos en la interfaz de usuario.

Primeramente, debemos crear un proyecto de Windows, agregar la referencia al DataHelper y al componente de acceso a datos. Realizar algunas modificaciones a los espacios de nombre y a los nombres de los archivos para que queden congruentes.

Explicaré brevemente el diseño de la interfaz de usuario; para este ejemplo utilizaré WPF (VS2008 ó VS2010), en un proyecto de Windows WPF. Será una ventana que tenga dos comportamientos, uno será para mostrar los registros que están en la base de datos y el otro comportamiento será para registrar o editar los registros. Es la misma venta con dos comportamientos, según se construya la ventana, será su comportamiento, es por eso que tendremos de antemano dos constructores. Tendremos algunos eventos, bindings y otras cosas que ya iremos viendo en el transcurso de este artículo.

Aquí empiezo dejando el XAML de la ventana que utilizaremos:

VB

<Window x:Class="TipoProducto"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Tipo de Producto" Height="200" Width="400">

    <Grid>

        <DockPanel x:Name="docPanMenu" Height="20"

                   VerticalAlignment="Top">

            <Menu x:Name="mnuMain" Height="Auto" Width="Auto" >

                <MenuItem Header="Archivo" x:Name="mnuArchivo">

                    <MenuItem Header="Eliminar registro actual"

                              Name="mnuEliminar"

                              Click="mnuEliminar_Click" />

                    <MenuItem  Header="Salir"

                               Name="mnuSalir"

                               Click="mnuSalir_Click" />

                </MenuItem>

            </Menu>

        </DockPanel>

        <DockPanel Margin="0,20,0,55" x:Name="docPanRegistro">

            <Grid x:Name="grid1" Width="Auto">

                <StackPanel Height="25" Margin="6,15,0,0"

                            x:Name="skpClaveTipoProducto"

                            Orientation="Horizontal"

                            VerticalAlignment="Top"

                            HorizontalAlignment="Left" Width="365">

                    <TextBlock Height="16" Width="95"

                               TextAlignment="Right"

                               FontWeight="Bold" FontStretch="Normal"

                               FontStyle="Oblique" Text="Clave:" />

                    <TextBox Name="txtClaveTipoProducto" Width="250"

                             MaxLength="20" Margin="5,0,0,0"

                             Text="{Binding Path=ClaveTipoProducto}" />

                </StackPanel>

                <StackPanel Height="25" Margin="6,45,0,0"

                            x:Name="skpDescripcion"

                            Orientation="Horizontal"

                            VerticalAlignment="Top"

                            HorizontalAlignment="Left" Width="365">

                    <TextBlock Height="16" Width="95" 

                               TextAlignment="Right"

                               FontWeight="Bold" FontStyle="Oblique"

                               Text="Descripción:" />

                    <TextBox Name="txtDescripcion"  Width="250"

                             MaxLength="150" Margin="5,0,0,0"

                             Text="{Binding Path=Descripcion}" />                   

                </StackPanel>

            </Grid>

        </DockPanel>

        <DockPanel Margin="0,0,0,20" x:Name="docPanBotones"

                   Height="35" VerticalAlignment="Bottom">

            <Grid Width="Auto">

                <Button Margin="0,6,90,6" Name="btnSalvar" 

                        Content="Salvar" Click="btnSalvar_Click"

                        HorizontalAlignment="Right" Width="73" />

                <Button Margin="0,6,8,6" Content="Salir"

                        IsCancel="True" HorizontalAlignment="Right"

                        Width="73" />

            </Grid>

        </DockPanel>

        <DockPanel Height="20" x:Name="docPanStatus"

                   VerticalAlignment="Bottom">

            <StatusBar Height="Auto" x:Name="statusBar1"

                       Width="Auto" Foreground="White">

                <StatusBarItem x:Name="sbiTitle"

                       Content="Ejemplo DAC - .NET Chronicles"/>

            </StatusBar>

        </DockPanel>

        <DockPanel Name="docPanSeleccion" Margin="420,25,-230,20" >

            <Grid>

                <DockPanel Margin="0,0,0,40" Width="Auto">

                    <ListView Name="lvwSeleccion" Height="Auto"

                          Width="Auto"

                          MouseDoubleClick=

                              "lvwSeleccion_MouseDoubleClick"

                          SelectionChanged=

                              "lvwSeleccion_SelectionChanged"

                          SelectionMode="Single" 

                          KeyUp="lvwSeleccion_KeyUp">

                        <ListView.View>

                            <GridView AllowsColumnReorder="True" >

                                <GridViewColumn

                                    DisplayMemberBinding=

                                    "{Binding Path=ClaveTipoProducto}"

                                    Width="Auto"

                                    Header="Clave" />

                                <GridViewColumn

                                    DisplayMemberBinding=

                                        "{Binding Path=Descripcion}"

                                    Width="Auto"

                                    Header="Descripcion" />                           

                            </GridView>

                        </ListView.View>

                    </ListView>

                </DockPanel>

                <DockPanel Height="40" VerticalAlignment="Bottom"

                           Width="Auto">

                    <Grid Height="Auto" Width="Auto">

                        <Button HorizontalAlignment="Right"

                                Width="73" Height="22"

                                Margin="0,0,170,6" 

                                x:Name="btnNuevo" Content="Nuevo"

                                Click="btnNuevo_Click"/>

                        <Button HorizontalAlignment="Right"

                                Width="73"

                                Height="22" Margin="0,0,90,6" 

                                x:Name="btnAceptar"

                                Content="Aceptar"

                                Click="btnAceptar_Click"/>

                        <Button HorizontalAlignment="Right"

                                Width="73"

                                Height="22" Margin="0,0,9,6"

                                x:Name="btnCancelar"

                                Content="Salir" IsCancel="True" />

                    </Grid>

                </DockPanel>

            </Grid>

        </DockPanel>

    </Grid>

</Window>

C#

<Window x:Class="UI.TipoProducto"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="Tipo de Producto" Height="200" Width="400">

    <Grid>

        <DockPanel x:Name="docPanMenu" Height="20"

                   VerticalAlignment="Top">

            <Menu x:Name="mnuMain" Height="Auto" Width="Auto" >

                <MenuItem Header="Archivo" x:Name="mnuArchivo">

                    <MenuItem Header="Eliminar registro actual"

                              Name="mnuEliminar"

                              Click="mnuEliminar_Click" />

                    <MenuItem  Header="Salir"

                               Name="mnuSalir"

                               Click="mnuSalir_Click" />

                </MenuItem>

            </Menu>

        </DockPanel>

        <DockPanel Margin="0,20,0,55" x:Name="docPanRegistro">

            <Grid x:Name="grid1" Width="Auto">

                <StackPanel Height="25" Margin="6,15,0,0"

                            x:Name="skpClaveTipoProducto"

                            Orientation="Horizontal"

                            VerticalAlignment="Top"

                            HorizontalAlignment="Left" Width="365">

                    <TextBlock Height="16" Width="95"

                               TextAlignment="Right"

                               FontWeight="Bold" FontStretch="Normal"

                               FontStyle="Oblique" Text="Clave:" />

                    <TextBox Name="txtClaveTipoProducto" Width="250"

                             MaxLength="20" Margin="5,0,0,0"

                             Text="{Binding Path=ClaveTipoProducto}" />

                </StackPanel>

                <StackPanel Height="25" Margin="6,45,0,0"

                            x:Name="skpDescripcion"

                            Orientation="Horizontal"

                            VerticalAlignment="Top"

                            HorizontalAlignment="Left" Width="365">

                    <TextBlock Height="16" Width="95" 

                               TextAlignment="Right"

                               FontWeight="Bold" FontStyle="Oblique"

                               Text="Descripción:" />

                    <TextBox Name="txtDescripcion"  Width="250"

                             MaxLength="150" Margin="5,0,0,0"

                             Text="{Binding Path=Descripcion}" />                   

                </StackPanel>

            </Grid>

        </DockPanel>

        <DockPanel Margin="0,0,0,20" x:Name="docPanBotones"

                   Height="35" VerticalAlignment="Bottom">

            <Grid Width="Auto">

                <Button Margin="0,6,90,6" Name="btnSalvar" 

                        Content="Salvar" Click="btnSalvar_Click"

                        HorizontalAlignment="Right" Width="73" />

                <Button Margin="0,6,8,6" Content="Salir"

                        IsCancel="True" HorizontalAlignment="Right"

                        Width="73" />

            </Grid>

        </DockPanel>

        <DockPanel Height="20" x:Name="docPanStatus"

                   VerticalAlignment="Bottom">

            <StatusBar Height="Auto" x:Name="statusBar1"

                       Width="Auto" Foreground="White">

                <StatusBarItem x:Name="sbiTitle"

                       Content="Ejemplo DAC - .NET Chronicles"/>

            </StatusBar>

        </DockPanel>

        <DockPanel Name="docPanSeleccion" Margin="420,25,-230,20" >

            <Grid>

                <DockPanel Margin="0,0,0,40" Width="Auto">

                    <ListView Name="lvwSeleccion" Height="Auto"

                          Width="Auto"

                          MouseDoubleClick=

                              "lvwSeleccion_MouseDoubleClick"

                          SelectionChanged=

                              "lvwSeleccion_SelectionChanged"

                          SelectionMode="Single" 

                          KeyUp="lvwSeleccion_KeyUp">

                        <ListView.View>

                            <GridView AllowsColumnReorder="True" >

                                <GridViewColumn

                                    DisplayMemberBinding=

                                    "{Binding Path=ClaveTipoProducto}"

                                    Width="Auto"

                                    Header="Clave" />

                                <GridViewColumn

                                    DisplayMemberBinding=

                                        "{Binding Path=Descripcion}"

                                    Width="Auto"

                                    Header="Descripcion" />                           

                            </GridView>

                        </ListView.View>

                    </ListView>

                </DockPanel>

                <DockPanel Height="40" VerticalAlignment="Bottom"

                           Width="Auto">

                    <Grid Height="Auto" Width="Auto">

                        <Button HorizontalAlignment="Right"

                                Width="73" Height="22"

                                Margin="0,0,170,6" 

                                x:Name="btnNuevo" Content="Nuevo"

                                Click="btnNuevo_Click"/>

                        <Button HorizontalAlignment="Right"

                                Width="73"

                                Height="22" Margin="0,0,90,6" 

                                x:Name="btnAceptar"

                                Content="Aceptar"

                                Click="btnAceptar_Click"/>

                        <Button HorizontalAlignment="Right"

                                Width="73"

                                Height="22" Margin="0,0,9,6"

                                x:Name="btnCancelar"

                                Content="Salir" IsCancel="True" />

                    </Grid>

                </DockPanel>

            </Grid>

        </DockPanel>

    </Grid>

</Window>

Nótese que las propiedad Text de cada caja de texto (TextBox) ha sido ligada a una propiedad del componente, así, se vincula directamente el componente con la interfaz de usuario. Así mismo las columnas del ListView. Esta propuesta de interfaz de usuario tiene interacción directa con el usuario como se verá más adelante. Además de que son prácticamente idénticos para VB y para C#, solo cambia el origen de la clase, pues en C# se incluye el Namespace.

En resumen tenemos cinco paneles acoplables, el primero, docPanMenu, es para poner los menú que se utilicen en la ventana, también está el docPanRegistro, que se refiere a la información que se mostrará del registro, en la captura o edición. Otro es el docPanBotones, que es donde se colocan los botones de acción del usuario. Un cuarto panel; docPanEstatus, que es el que contiene la barra de estado y por último, docPanSelección, este panel contiene la vista de datos que se tienen registrados en la base de datos, desde donde podremos seleccionar alguno para editar o interactuar con él.

Este tipo de ventanas utilizan la configuración de conexión para poder trabajar, por lo que necesitaremos una ventana principal, solo para inicializar las variables y lanzar la ventana. Una ventana MainWindow bastará, simple y sin complicaciones. Aquí dejo el XAML:

VB

<Window x:Class="MainWindow"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="MainWindow" Height="300" Width="300">

    <Grid>

        <Button

            Margin="10,10"

            Content=" Tipo Producto "

            x:Name="btnTipoProducto"

            HorizontalAlignment="Left"

            VerticalAlignment="Top"

            Click="btnTipoProducto_Click" />

    </Grid>

</Window>

C#

<Window x:Class="UI.MainWindow"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Title="MainWindow" Height="300" Width="300">

    <Grid>

        <Button

            Margin="10,10"

            Content=" Tipo Producto "

            x:Name="btnTipoProducto"

            HorizontalAlignment="Left"

            VerticalAlignment="Top"

            Click="btnTipoProducto_Click" />

    </Grid>

</Window>

Bien, pues empecemos por esta ventana de una vez de aquí nos pasamos a la nuestra y regresamos y vaya que quiero confundirlos. Bueno, mejor ventana por ventana, empecemos con esta entonces, que por supuesto requerirá de algunas cositas más.

Veamos el código, primeramente el uso de namespaces (using o Imports), bueno para esta clase no tenemos nada más allá de los predeterminados, seguido, la declaración de campos.

VB

Private sqlSet As SqlSettings

C#

private SqlSettings sqlSet;

Bien, para aclarar, este campo es del tipo SqlSettings, este tipo implementa la interfaz IDbSettings que se definió en la parte 1 de la definición de un DataHelper (http://msmvps.com/blogs/otelis/archive/2009/03/11/arquitectura-definici-243-n-de-un-datahelper-parte-1.aspx), pero si hay que ser honestos, no he definido la clase SqlSettings como tal, bien, pues tendré que hacerlo, no sin antes comentar lo siguiente: Esta clase debe estar en un componente de proceso de interfaz de usuario o bien (UIPC), para este ejemplo, he agregado una clase más en el proyecto de Windows WPF que estamos utilizando para la interfaz de usuario, sin embargo, podría estar definido en una biblioteca de clases aparte, según les convenga. Bueno, al archivo de la clase lo he definido ProcessUI y contendrá la definición de la clase SqlSettings, además, se tendrá la definición de un delegado genérico, mismo que usaremos para crear nuestros eventos y la declaración de uso del namespace DBInfo:

VB

Imports DataHelper.DBInfo

 

Public Delegate Sub _

    GenericEventHandler(Of T, U) _

    (ByVal sender As T, ByVal e As U)

 

Public Class SqlSettings

    Implements IDbSettings

 

    Private _server As String

    Private _database As String

    Private _user As String

    Private _pwd As String

 

    Public Sub New()

        _server = ""

        _database = ""

        _user = ""

        _pwd = ""

    End Sub

 

    Public Property Server() As String _

    Implements IDbSettings.Server

        Get

            Return _server

        End Get

        Set(ByVal value As String)

            _server = value

        End Set

    End Property

    Public Property DataBase() As String _

    Implements IDbSettings.DataBase

        Get

            Return _database

        End Get

        Set(ByVal value As String)

 

            _database = value

        End Set

    End Property

    Public Property UserId() As String _

    Implements IDbSettings.User

        Get

            Return _user

        End Get

        Set(ByVal value As String)

            _user = value

        End Set

    End Property

    Public Property Password() As String _

    Implements IDbSettings.Password

        Get

            Return _pwd

        End Get

        Set(ByVal value As String)

            _pwd = value

        End Set

    End Property

 

    Public Function GetConnectionString() As String _

    Implements IDbSettings.GetConnectionString

        Dim cnnStr As String = ""

        If _server.Trim() <> "" AndAlso _database.Trim() <> "" Then

            If _user.Trim() = "" Then

                cnnStr = String.Format( _

                "Integrated Security=SSPI; Server={0}; Database={1}", _

                _server, _database)

            Else

                cnnStr = String.Format( _

                "Server={0}; Database={1}; User Id={2}; Password={3}", _

                _server, _database, _user, _pwd)

            End If

        End If

        Return cnnStr

    End Function

End Class

C#

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using DBInfo;

 

namespace UI

{

    public delegate void GenericEventHandler<T, U>(T sender, U e);

    

    public partial class SqlSettings : IDbSettings

    {

        private string server;

        private string database;

        private string user;

        private string pwd;

 

        public SqlSettings()

        {

            server = "";

            database = "";

            user = "";

            pwd = "";

        }

 

        public string Server

        {

            get

            {

                return server;

            }

            set

            {

                server = value;

            }

        }

        public string DataBase

        {

            get

            {

                return database;

            }

            set

            {

                database = value;

            }

        }

        public string UserId

        {

            get

            {

                return user;

            }

            set

            {

                user = value;

            }

        }

        public string Password

        {

            get

            {

                return pwd;

            }

            set

            {

                pwd = value;

            }

        }

        public string GetConnectionString()

        {

            string cnnStr = "";

            if (server.Trim() != "" && database.Trim() != "")

            {

                if (user.Trim() == "")

                    cnnStr = String.Format(

                    "Integrated Security=SSPI; " +

                        "Server={0}; Database={1}",

                    server, database);

                else

                    cnnStr = String.Format(

                    "Server={0}; Database={1}; " +

                        "User Id={2}; Password={3}",

                    server, database, user, pwd);               

            }

            return cnnStr;

        }

        public object Clone()

        {

            return new SqlSettings();

        }

 

        string IDbSettings.Server

        {

            get

            {

                return server;

            }

            set

            {

                server = value;

            }

        }

        string IDbSettings.DataBase

        {

            get

            {

                return database;

            }

            set

            {

                database = value;

            }

        }

        string IDbSettings.User

        {

            get

            {

                return user;

            }

            set

            {

                user = value;

            }

        }

        string IDbSettings.Password

        {

            get

            {

                return pwd;

            }

            set

            {

                pwd = value;

            }

        }

        string IDbSettings.GetConnectionString()

        {

            return GetConnectionString();

        }

    }

}

 

Observemos que se implementa la interfaz IDbSettings explícitamente. Esta clase ya la estamos usando en la MainWindow, en la declaración, sin embargo no hemos visto cómo la utilizaremos. Regresemos pues a nuestra clase MainWindow y veamos el constructor predeterminado que le hemos creado:

VB

Public Sub New()

    InitializeComponent()

 

    sqlSet = New SqlSettings()

    sqlSet.Server = "(LOCAL)"

    sqlSet.DataBase = "EjemploDAC"

    sqlSet.UserId = ""

    sqlSet.Password = ""

End Sub

C#

public MainWindow()

{

    InitializeComponent();

 

    sqlSet = new SqlSettings();

    sqlSet.Server = "(LOCAL)";

    sqlSet.DataBase = "EjemploDAC";

    sqlSet.UserId = "";

    sqlSet.Password = "";           

}

De este constructor, vemos que llama al método interno InitializeComponent(), que como sabrán es el que inicializa la ventana y sus controles, seguido de esto, inicializamos la variable sqlSet, misma que nos servirá más adelante para crear la ventana de captura. Notamos aquí que estamos pasando los valores de conexión para la base de datos, aunque no es buena práctica dejarlos código duro para caso práctico en este ejemplo los usaré así. No olviden poner los valores propios para el equipo donde realicen la prueba.

 Para crear la ventana de captura, hemos puesto un botón que será el responsable de mostrarnos la ventana con la información de la base de datos, el evento click de dicho botón contendrá lo siguiente:

VB

Private Sub btnTipoProducto_Click( _

    ByVal sender As System.Object, _

    ByVal e As System.Windows.RoutedEventArgs)

 

    Dim tipoProducto As TipoProducto = _

        New UI.TipoProducto(sqlSet, False)

    tipoProducto.Owner = Me

    tipoProducto.WindowStartupLocation = _

        Windows.WindowStartupLocation.CenterScreen

    tipoProducto.ShowDialog()

End Sub

C#

private void btnTipoProducto_Click(

    object sender,

    RoutedEventArgs e)

{

    TipoProducto tipoProducto =

        new TipoProducto(sqlSet, false);

    tipoProducto.Owner = this;

    tipoProducto.WindowStartupLocation =

        WindowStartupLocation.CenterScreen;

    tipoProducto.ShowDialog();

}

Es interesante ver cómo estamos instanciando a la ventana de captura, al ver que estamos pasando dos parámetros en el constructor, uno de ellos es la variable sqlSet que contiene la información de conexión a la base de datos y la otra es un valor booleano. En breve estaré explicando este constructor, pero antes definamos las particularidades de la clase  de la venta TipoProducto.

Primeramente la declaración de uso de namespaces:

VB

Imports System.Collections.Generic

C#

using System.Collections.Generic;

Este namespace lo necesitamos porque estaremos haciendo uso de listas genéricas para manipular los datos.

Seguido tenemos la declaración de campos:

VB

Private tipoProducto As DAC.TipoProducto

Private isSelection As Boolean = False

Private isSearch As Boolean = False

C#

private DAC.TipoProducto tipoProducto;

private bool isSelection = false;

private bool isSearch = false;

Estos campos, bueno… el tipoProducto es una variable que contiene la información del registro, esto es, es el DAC de TipoProducto, y la requerimos como campo porque la utilizamos en toda la clase, de hecho hay un constructor que la utiliza. Los otros dos campos, isSelection y isSearch son para definir el estado de la vista de datos, esto es, si la vista es para seleccionar un valor basado en los datos mostrados, entonces la variable isSelection será true y la variable isSearch será false, en cambio, si la vista de datos es para tener acceso al registro y poder modificarlo, entonces, se dará el caso inverso, donde isSelection será false e isSearch será true. Estos campos se asignan inicialmente ambos en false, pero cambian su valor en uno de los constructores, como veremos más adelante.

Pues continuando con el código de la ventana, tendremos la declaración de los eventos de esta, serán eventos que lanzaremos cuando se modifiquen los datos, en general, Salvar y Borrar. Para esto, tenemos definido el tipo GenericEventHandler en el archivo ProcessUI, como lo mostré anteriormente en este artículo, siendo así, queda el código siguiente:

VB

Public Event RecordSaved As  _

    GenericEventHandler( _

        Of UI.TipoProducto, DAC.TipoProducto)

Public Event RecordSelected As  _

    GenericEventHandler( _

        Of UI.TipoProducto, DAC.TipoProducto)

C#

public event GenericEventHandler

    <UI.TipoProducto, DAC.TipoProducto> RecordSaved;

public event GenericEventHandler

    <UI.TipoProducto, DAC.TipoProducto> RecordSelected;

Bien, como les mencioné, están los dos eventos, ambos creados a partir del tipo declarado en el ProcessUI. Más adelante veremos su uso.

Continuando, definiremos los constructores de la ventana, teniendo en cuenta que no habrá un constructor sin parámetros, lo anterior para asegurar que la ventana se construya con un enlace a la base de datos y un objeto conectado a la misma, esto es, un DAC con vida. Veamos pues estos constructores:

VB

Public Sub New(ByVal sqlSet As SqlSettings)

    tipoProducto = New DAC.TipoProducto(sqlSet)

    InitForm()

End Sub

 

Public Sub New(ByVal sqlSet As SqlSettings, _

               ByVal IsSelection As Boolean)

    tipoProducto = New DAC.TipoProducto(sqlSet)

    IsSelection = IsSelection

    isSearch = Not IsSelection

    InitForm()

End Sub

 

Public Sub New(ByVal pTipoDeclaracion As DAC.TipoProducto)

    tipoProducto = pTipoDeclaracion

    InitForm()

End Sub

C#

public TipoProducto(SqlSettings sqlSet)

{           

    tipoProducto = new DAC.TipoProducto(sqlSet);

    InitForm();

}

public TipoProducto(SqlSettings sqlSet, bool IsSelection)

{

    tipoProducto = new DAC.TipoProducto(sqlSet);

    isSelection = IsSelection;

    isSearch = !IsSelection;

    InitForm();

}

public TipoProducto(DAC.TipoProducto pTipoDeclaracion)

{

    tipoProducto = pTipoDeclaracion;

    InitForm();

}

 

Como podemos observar, el parámetro sqlSet del tipo SqlSettings se utiliza para construir el objeto tipoProducto, el cual es del tipo DAC.TipoProducto, que es el componente de acceso a datos que programamos.

El primer constructor construirá al objeto tipoProducto como un objeto nuevo, o bien, un registro nuevo. El segundo constructor, será simplemente para mostrar la ventana en su forma de búsqueda o selección. En la ventana MainWindow estamos optando por la primera forma, esto es, como búsqueda para permitir la edición del registro. El tercer constructor recibe el objeto TipoProducto previamente construido y lo asigna a nuestra variable del mismo tipo.

En todos los casos podemos ver que se invoca el método InitForm(), este método se llama para inicializar al formulario por primera vez, este método no sirve para tareas recurrentes, o sea, no es útil para reinicializar al formulario, para eso he creado otro método denominado InitializeForm(), que será de utilidad para reinicializar el formulario en cualquier momento. A continuación, el código de dichos métodos:

VB

Private Sub InitForm()

    InitializeComponent()

    initializeForm()

    AddHandler RecordSaved, _

        AddressOf Record_Saved

    If isSelection Then

        AddHandler RecordSelected, _

            AddressOf Record_Selected

    End If

End Sub

 

Private Sub Record_Saved( _

    ByVal sender As UI.TipoProducto, _

    ByVal e As DAC.TipoProducto)

 

    Return

End Sub

 

Private Sub Record_Selected( _

    ByVal sender As UI.TipoProducto, _

    ByVal e As DAC.TipoProducto)

 

    Return

End Sub

 

Private Sub initializeForm()

    Me.DataContext = tipoProducto

    If tipoProducto.Nuevo Then

        If isSelection OrElse isSearch Then

 

            mnuArchivo.Items.Clear()

            mnuArchivo.Items.Add(mnuSalir)

 

            docPanRegistro.Visibility = Visibility.Hidden

            docPanBotones.Visibility = Visibility.Hidden

            docPanSeleccion.Margin = _

                New System.Windows.Thickness(0, 25, 0, 25)

 

            If isSelection Then

                Title = "Selección de Tipo de Producto"

                btnAceptar.Content = "Seleccionar"

            Else

                Title = "Búsqueda de Tipo de Producto"

                btnAceptar.Content = "Editar"

            End If

            Width = 600

            Height = 600

 

            FillSelection()

            btnAceptar.IsEnabled = False

        Else

            mnuArchivo.Items.Clear()

            mnuArchivo.Items.Add(mnuSalir)

            docPanSeleccion.Visibility = Visibility.Hidden

        End If

    Else

        mnuArchivo.Items.Clear()

        mnuArchivo.Items.Add(mnuEliminar)

        mnuArchivo.Items.Add(mnuSalir)

 

        docPanSeleccion.Visibility = Visibility.Hidden

    End If

End Sub

 

C#

private void InitForm()

{

    InitializeComponent();

    initializeForm();

    RecordSaved +=

        delegate(UI.TipoProducto T, DAC.TipoProducto U)

        { return; };

    if (isSelection)

    {

        RecordSelected +=

            delegate(UI.TipoProducto T, DAC.TipoProducto U)

            { return; };

    }

}

 

private void initializeForm()

{

    this.DataContext = tipoProducto;

    if (tipoProducto.Nuevo)

    {

        if (isSelection || isSearch)

        {

            mnuArchivo.Items.Clear();

            mnuArchivo.Items.Add(mnuSalir);

 

            docPanRegistro.Visibility = Visibility.Hidden;

            docPanBotones.Visibility = Visibility.Hidden;

            docPanSeleccion.Margin =

                new System.Windows.Thickness(0, 25, 0, 25);

 

            if (isSelection)

            {

                this.Title = "Selección de Tipo de Producto";

                btnAceptar.Content = "Seleccionar";

            }

            else

            {

                this.Title = "Búsqueda de Tipo de Producto";

                btnAceptar.Content = "Editar";

            }

            this.Width = 600;

            this.Height = 600;

 

            FillSelection();

            btnAceptar.IsEnabled = false;

 

        }

        else

        {

            mnuArchivo.Items.Clear();

            mnuArchivo.Items.Add(mnuSalir);

            docPanSeleccion.Visibility = Visibility.Hidden;

        }

    }

    else

    {               

        mnuArchivo.Items.Clear();

        mnuArchivo.Items.Add(mnuEliminar);

        mnuArchivo.Items.Add(mnuSalir);

 

        docPanSeleccion.Visibility = Visibility.Hidden;

    }

}

 

Podemos observar que el método InitForm() se encarga de invocar al método interno InitializeComponent(), invoca además el método antes mencionado InitializeForm() y además, inicializa los eventos. Esta inicialización es para evitar la invocación de un delegado sin método, así pues he agregado dos métodos en el caso de VB  y dos métodos anónimos en el caso de C#. En realidad es porque si ejecuto el código y no se generó un manejador para el evento, la aplicación lanza una excepción, por esta razón, digamos que desvío la ejecución a los métodos anónimos para evitar contrariedades.

Con respecto al método InitializeForm(), podemos observar que inicializa las características de la ventana teniendo en cuenta cómo se ha construido la misma, vemos evaluaciones de las variables isSearch, isSelection y de la propiedad nuevo del objeto tipoProducto, con esto podemos construir la ventana en el modo que nos convenga, y esto gracias a que desde el momento de realizar la instancia de la ventana estamos anticipando su comportamiento. No hay mucho que explicar en esta parte, salvo que se oculta, muestran y mueven los paneles para cambiar la apariencia de la ventana según los criterios mencionados.

Continuando con nuestro recorrido en la generación del código de la ventana, llegamos a los métodos comunes, uno de ellos está invocado en el método InitializeForm(), ese y otros más se definen a continuación:

VB

Private Sub FillSelection()

    lvwSeleccion.ItemsSource = Nothing

    lvwSeleccion.ItemsSource = _

        tipoProducto.GetListTiposProductos()

End Sub

 

Private Sub RaiseSelection()

    If lvwSeleccion.SelectedItems.Count = 0 Then

        Return

 

    End If

    tipoProducto = TryCast(lvwSeleccion.SelectedItem,  _

                           DAC.TipoProducto)

    If isSelection Then

        RaiseEvent RecordSelected(Me, tipoProducto)

        Close()

    Else

        Cursor = Cursors.Wait

        Dim tipoDec As UI.TipoProducto = _

            New TipoProducto(tipoProducto)

        tipoDec.Owner = Me

        tipoDec.WindowStartupLocation = _

            WindowStartupLocation.CenterOwner

        AddHandler tipoDec.RecordSaved, _

            AddressOf tipoProd_RecordSaved

        tipoDec.ShowDialog()

        Cursor = Cursors.Arrow

    End If

End Sub

C#

private void FillSelection()

{

    lvwSeleccion.ItemsSource = null;

    lvwSeleccion.ItemsSource =

        tipoProducto.GetListTiposProductos();

}

 

private void RaiseSelection()

{

    if (lvwSeleccion.SelectedItems.Count == 0)

        return;

 

    tipoProducto = lvwSeleccion.SelectedItem as DAC.TipoProducto;

    if (isSelection)

    {

        RecordSelected(this, tipoProducto);

        Close();

    }

    else

    {

        this.Cursor = Cursors.Wait;

        UI.TipoProducto tipoDec =

            new TipoProducto(tipoProducto);

        tipoDec.Owner = this;

        tipoDec.WindowStartupLocation =

            WindowStartupLocation.CenterOwner;

        tipoDec.RecordSaved += tipoProd_RecordSaved;

        tipoDec.ShowDialog();

        this.Cursor = null;

    }

}

 

De estos dos métodos puedo decirles que, el primero FillSelection() se encarga de inicializar el ItemsSource del ListView que utilizamos para mostrar los registros que están en la base de datos. Ahora bien, pueden darse cuenta que se invoca un método del DAC que no tenemos actualmente, sin embargo lo vamos a definir en un momento más.

El otro método, RaiseSelection(), es más interesante, ya que este método se invoca cada vez que se quiere mostrar en un elemento seleccionado en el ListView, bueno, tiene dos comportamientos como pueden observar, si isSelection es true, solo se lanza el evento RecordSelected y se cierra, de lo contrario, se inicializará una ventana nueva con el elemento seleccionado para mostrar los datos en modo de edición.

Continuando, tenemos los dos métodos que operan hacia la base de datos, así es, estos se manda a llamar en varios eventos dependiendo de la acción del usuario. Son los métodos Salvar() y Eliminar(), donde se aprecia en detalle la funcionalidad del DAC. Veamos pues:

VB

Private Sub Salvar()

 

    If tipoProducto.ClaveTipoProducto.Trim() = "" Then

        MessageBox.Show("La Clave es requerida. Rectifique", _

        "Mensaje de Sistema", _

        MessageBoxButton.OK, _

        MessageBoxImage.Information, _

        MessageBoxResult.OK)

        txtClaveTipoProducto.Focus()

        Return

    ElseIf tipoProducto.Descripcion.Trim() = "" Then

        MessageBox.Show("La Descripción es requerida. Rectifique", _

        "Mensaje de Sistema", _

        MessageBoxButton.OK, _

        MessageBoxImage.Information, _

        MessageBoxResult.OK)

        txtDescripcion.Focus()

        Return

    End If

 

    Dim res As Integer = tipoProducto.Salvar()

 

    If res > 0 Then

        MessageBox.Show("El registro se salvó correctamente", _

            "Registro salvado", _

            MessageBoxButton.OK, _

            MessageBoxImage.Information, _

            MessageBoxResult.OK)

    ElseIf (res <= 0) Then

        MessageBox.Show("No se pudo Salvar el Registro", _

        "Mensaje de Sistema", _

        MessageBoxButton.OK, _

        MessageBoxImage.Warning, _

        MessageBoxResult.OK)

        Return

    End If

 

    RaiseEvent RecordSaved(Me, tipoProducto)

    Close()

End Sub

 

Private Sub Eliminar()

 

    If MessageBox.Show( _

            String.Format( _

            "¿Desea Eliminar El Tipo de Producto con Clave {0}?", _

            tipoProducto.ClaveTipoProducto.Trim()), _

         "Mensaje de Sistema", _

         MessageBoxButton.YesNo, _

         MessageBoxImage.Question, _

         MessageBoxResult.No) = System.Windows.MessageBoxResult.Yes Then

 

        Dim res As Integer = tipoProducto.Borrar()

        If res <= 0 Then

            MessageBox.Show("No se pudo Borrar el Registro", _

            "Mensaje de Sistema", _

            MessageBoxButton.OK, _

            MessageBoxImage.Information, _

            MessageBoxResult.OK)

        Else

            RaiseEvent RecordSaved(Me, tipoProducto)

            If isSelection OrElse isSearch Then

                FillSelection()

            Else

                Close()

            End If

        End If

    End If

End Sub

C#

private void Salvar()

{

    if (this.tipoProducto.ClaveTipoProducto.Trim() == "")

    {

        MessageBox.Show("La Clave es requerida. Rectifique",

        "Mensaje de Sistema",

        MessageBoxButton.OK,

        MessageBoxImage.Information,

        MessageBoxResult.OK);

        this.txtClaveTipoProducto.Focus();

        return;

    }

    else if (tipoProducto.Descripcion.Trim() == "")

    {

        MessageBox.Show("La Descripción es requerida. Rectifique",

        "Mensaje de Sistema",

        MessageBoxButton.OK,

        MessageBoxImage.Information,

        MessageBoxResult.OK);

        txtDescripcion.Focus();

        return;

    }

 

    int res = tipoProducto.Salvar();

 

    if (res > 0)

        MessageBox.Show("El registro se salvó correctamente",

            "Registro salvado",

            MessageBoxButton.OK,

            MessageBoxImage.Information,

            MessageBoxResult.OK);

    else if (res <= 0)

    {

        MessageBox.Show("No se pudo Salvar el Registro",

        "Mensaje de Sistema",

        MessageBoxButton.OK,

        MessageBoxImage.Warning,

        MessageBoxResult.OK);

        return;

    }

    RecordSaved(this, tipoProducto);

    Close();

}

 

private void Eliminar()

{

    if (MessageBox.Show(

        string.Format(

        "¿Desea Eliminar El Tipo de Producto con Clave {0}?",

        tipoProducto.ClaveTipoProducto.Trim()),

     "Mensaje de Sistema",

     MessageBoxButton.YesNo,

     MessageBoxImage.Question,

     MessageBoxResult.No) == System.Windows.MessageBoxResult.Yes)

    {

        int res = tipoProducto.Borrar();

        if (res <= 0)

            MessageBox.Show("No se pudo Borrar el Registro",

            "Mensaje de Sistema",

            MessageBoxButton.OK,

            MessageBoxImage.Information,

            MessageBoxResult.OK);

        else

        {

            RecordSaved(this, tipoProducto);

            if (isSelection || isSearch)

                FillSelection();

            else

                Close();

        }

    }

}

 

Veamos en el método salvar, iniciamos con la validación de la información introducida, recordemos que es aquí, en la interfaz de usuario, donde se realizan la validaciones de tipo, formato, etc. Bien, una vez pasada la validación, no nos queda más que invocar el método salvar del objeto TipoProducto, y es tan simple porque hemos establecido de antemano el binding de las propiedades del objeto con los controles de la interfaz. De hecho si se dan cuenta, la validación se hace contra el contenido de las propiedades del objeto y no contra los controles. Después de hacer esto, bueno, se le informa al usuario del éxito o fracaso de la operación y posteriormente se lanza el evento RecordSaved para concluir con el cierre de la ventana.

El evento eliminar, es parecido, solo hay que tener en cuenta que se invoca el método borrar sin parámetros, esto es, se intentará borrar el registro con el identificador contenido en el objeto. También lanza el evento RecordSaved y dependiendo del estado de la ventana llena el ListView o cierra la ventana.

Bueno, es algo simple, y todo se resume a Salvar o Borrar.

Lo que resta son los eventos de los controles, que no serán difíciles de entender, aquí les dejo el código:

VB

Private Sub tipoProd_RecordSaved( _

    ByVal sender As TipoProducto, _

    ByVal e As DAC.TipoProducto)

 

    FillSelection()

End Sub

 

Private Sub mnuEliminar_Click( _

    ByVal sender As Object, _

    ByVal e As RoutedEventArgs)

 

    Eliminar()

End Sub

 

Private Sub mnuSalir_Click( _

    ByVal sender As Object, _

    ByVal e As RoutedEventArgs)

 

    Close()

End Sub

 

Private Sub btnSalvar_Click( _

    ByVal sender As Object, _

    ByVal e As RoutedEventArgs)

 

    Salvar()

End Sub

 

Private Sub btnNuevo_Click( _

    ByVal sender As Object, _

    ByVal e As RoutedEventArgs)

 

    Dim tipoDec As TipoProducto = _

        New TipoProducto( _

            TryCast(tipoProducto.SqlSettings,  _

                SqlSettings))

    tipoDec.Owner = Me

    tipoDec.WindowStartupLocation = _

        WindowStartupLocation.CenterOwner

    AddHandler tipoDec.RecordSaved, _

        AddressOf tipoProd_RecordSaved

    tipoDec.ShowDialog()

End Sub

 

Private Sub btnAceptar_Click( _

    ByVal sender As Object, _

    ByVal e As RoutedEventArgs)

 

    RaiseSelection()

End Sub

 

Private Sub lvwSeleccion_MouseDoubleClick( _

    ByVal sender As Object, _

    ByVal e As MouseButtonEventArgs)

 

    RaiseSelection()

End Sub

 

Private Sub lvwSeleccion_SelectionChanged( _

    ByVal sender As Object, _

    ByVal e As SelectionChangedEventArgs)

 

    btnAceptar.IsEnabled = _

        lvwSeleccion.SelectedItems.Count > 0

End Sub

 

Private Sub lvwSeleccion_KeyUp( _

    ByVal sender As Object, _

    ByVal e As KeyEventArgs)

 

    If e.Key = Key.Enter Then

        RaiseSelection()

    ElseIf e.Key = Key.Delete Then

        If lvwSeleccion.SelectedItems.Count > 0 Then

            tipoProducto = _

                TryCast(lvwSeleccion.SelectedItem,  _

                    DAC.TipoProducto)

            Eliminar()

        End If

    End If

End Sub

C#

 

private void tipoProd_RecordSaved(

    TipoProducto sender, DAC.TipoProducto e)

{

    FillSelection();

}

private void mnuEliminar_Click(

    object sender, RoutedEventArgs e)

{

    Eliminar();

}

private void mnuSalir_Click(

    object sender, RoutedEventArgs e)

{

    this.Close();

}

 

private void btnSalvar_Click(

    object sender, RoutedEventArgs e)

{

    Salvar();

}

private void btnNuevo_Click(

    object sender, RoutedEventArgs e)

{

    TipoProducto tipoDec =

        new TipoProducto(tipoProducto.SqlSettings as SqlSettings);

    tipoDec.Owner = this;

    tipoDec.WindowStartupLocation =

        WindowStartupLocation.CenterOwner;

    tipoDec.RecordSaved += tipoProd_RecordSaved;

    tipoDec.ShowDialog();

}

private void btnAceptar_Click(

    object sender, RoutedEventArgs e)

{

    RaiseSelection();

}

 

private void lvwSeleccion_MouseDoubleClick(

    object sender, MouseButtonEventArgs e)

{

    RaiseSelection();

}

 

private void lvwSeleccion_SelectionChanged(

    object sender, SelectionChangedEventArgs e)

{

    btnAceptar.IsEnabled =

        lvwSeleccion.SelectedItems.Count > 0;

}

 

private void lvwSeleccion_KeyUp(

    object sender, KeyEventArgs e)

{

    if (e.Key == Key.Enter)

        RaiseSelection();

    else if (e.Key == Key.Delete)

    {

        if (lvwSeleccion.SelectedItems.Count > 0)

        {

            tipoProducto =

                lvwSeleccion.SelectedItem as DAC.TipoProducto;

            Eliminar();

        }

    }

}

Veamos, qué tenemos aquí…

En primero el evento tipoProd_RecordSaved que se enlaza cada que construimos una ventana, se usa en el método RiseSelection y en el  método btnNuevo_Click, y bueno, lo que tiene es ya conocido.

Los siguiente son eventos típicos de los controles. En el caso del botón btnNuevo, vemos que lanza una ventana para el registro de un nuevo tipo de producto, esto actualizará la lista mediante el evento RecordSaved.  Otro caso interesante es el método lvwSeleccion_KeyUp que maneja el evento KeyUp del ListView, bueno, en ese se muestran dos opciones del valor de tecla, uno es Enter que invoca el evento RaiseSelection(), el caso de Delete, se ve si hay algún elemento seleccionado para proceder a eliminarlo.

Solo resta la actualización que debemos hacer en el DAC.

Recordando el Stored Procedure que creamos en la primera parte, recordarán la opción 5, bueno, esta opción la utilizaremos para llenar el ListView, son dos métodos, uno llena a un DataTable y con este en el otro método se llena a una lista genérica con los DAC correspondientes.

Recuerden que estos métodos se definen en el DAC, así que completemos el DAC con el siguiente código.

VB

Public Function GetTiposProductos() As DataTable

    cmd.Parameters.Clear()

    cmd.Parameters.Add("@SP_OPCION", SqlDbType.TinyInt).Value = 5

    Return GetQuery(cmd)

End Function

 

Public Function GetListTiposProductos() _

    As List(Of DAC.TipoProducto)

 

    Dim res As List(Of DAC.TipoProducto) = _

        New List(Of TipoProducto)()

    Dim tProd As DAC.TipoProducto

    For Each dr As DataRow In GetTiposProductos().Rows

        tProd = New TipoProducto(MyBase.SqlSettings)

        tProd.IntializeFields(dr)

        res.Add(tProd)

    Next

    Return res

End Function

C#

public DataTable GetTiposProductos()

{

    cmd.Parameters.Clear();

    cmd.Parameters.Add("@SP_OPCION", SqlDbType.TinyInt).Value = 5;

    return GetQuery(cmd);

}

public List<DAC.TipoProducto> GetListTiposProductos()

{

    List<DAC.TipoProducto> res = new List<TipoProducto>();

    DAC.TipoProducto tProd;

    foreach (DataRow dr in GetTiposProductos().Rows)

    {

        tProd = new TipoProducto(base.SqlSettings);

        tProd.InitializeFields(dr);

        res.Add(tProd);

    }

 

    return res;

}

Veamos el método GetTiposProductos(), simplemente asigna los parámetros del SqlCommand para consultar contra la opción mencionada y devuelve lo que tenga en el método GetQuery() como un DataTable. El otro método invoca al primer método para ser recorrido e inicializar y agregar a la lista genérica los elementos TipoProducto que se traen de la base de datos, esto mediante el método InitilizeFields, al cual se le pasa un DataRow e inicializa las propiedades del objeto.

Bien, pues con esto terminamos la trilogía del DAC con ejemplo, espero sea ilustrativo y útil.

Referencias:

Parte 1: http://msmvps.com/blogs/otelis/archive/2009/09/11/arquitectura-definici-243-n-de-un-data-acces-component-con-un-ejemplo.aspx

Parte 2: http://msmvps.com/blogs/otelis/archive/2010/05/12/arquitectura-definici-243-n-de-un-data-access-component-con-un-ejemplo-parte-2.aspx

Saludos…

Octavio Telis

Published Fri, May 14 2010 14:20 by Octavio Telis Aynés

Leave a Comment

(required) 
(required) 
(optional)
(required) 
If you can't read this number refresh your screen
Enter the numbers above:  
Powered by Community Server (Commercial Edition), by Telligent Systems