Sie sind auf Seite 1von 47

Ejemplo

Iniciar un nuevo proyecto, aadir un Windows Form, y colocar los siguientes controles :

Un control Listview llamado ListView1

Un ComboBox llamado ComboBox1

Un Control Button llamado Button1

Configurar la cadena de conexin

Formulario

( Espacio de nombres usado en el ejemplo : Imports System.Data.SqlClient )


cdigo fuente
Texto planoImprimir
1.

Option Explicit On

2.

Option Strict On

3.
4.

Imports System.Data.SqlClient

5.
6.

Public Class Form1

7.
8.

Private name_bd As String = String.Empty

9.
10.

' Cadena de conexin para sql server express en modo local

11.

Private Const cs As String = "Data Source=(local)\SQLEXPRESS;" & _

12.

"Integrated Security=True;" & _

13.

"Initial Catalog="

14.
15.

' funcin para llenar el LV

16.

Public Sub cargar_ListView( _

17.

ByRef ListView As ListView, _

18.

ByVal sql As String, _

19.

ByVal db As String)

20.
21.

Try

22.

' Crea y abre una nueva conexin

23.

Using cn As New SqlConnection(cs & db)

24.
25.

cn.Open()

26.
27.

' propiedades del SqlCommand

28.

Dim comando As New SqlCommand

29.
30.

With comando

31.

.CommandType = CommandType.Text

32.

.CommandText = sql

33.
34.
35.

.Connection = cn
End With

36.
37.

Dim da As New SqlDataAdapter ' Crear nuevo SqlDataAdapter

38.

Dim dataset As New DataSet ' Crear nuevo dataset

39.
40.

da.SelectCommand = comando

41.
42.

' llenar el dataset

43.

da.Fill(dataset, "Tabla")

44.
45.

' Propiedades del ListView

46.

With ListView

47.

.Items.Clear()

48.

.Columns.Clear()

49.

.View = View.Details

50.

.GridLines = True

51.

.FullRowSelect = True

52.

' aadir los nombres de columnas

53.

For c As Integer = 0 To dataset.Tables("tabla").Columns


.Count - 1

54.

.Columns.Add(dataset.Tables("tabla").Columns(c).Cap
tion)

55.

Next

56.

End With

57.
58.

' Aadir los registros de la tabla

59.

With dataset.Tables("tabla")

60.
61.

For f As Integer = 0 To .Rows.Count - 1

62.

Dim dato As New ListViewItem(.Rows(f).Item(0).ToStr


ing)

63.

' recorrer las columnas

64.

For c As Integer = 1 To .Columns.Count - 1

65.

dato.SubItems.Add(.Rows(f).Item(c).ToString())

66.

Next

67.

ListView.Items.Add(dato)

68.

Next

69.

End With

70.

End Using

71.

' errores

72.

Catch ex As Exception

73.
74.
75.

MsgBox(ex.Message.ToString)
End Try
End Sub

76.
77.

' Funcin que retorna un objeto DataTable para

78.

'enlazarlo con el combobox y visualizar las tablas

79.

Private Function get_Tablas( _

80.

ByVal la_base_de_datos As String) As DataTable

81.
82.

Try

83.
84.

' nueva conexin a sql

85.

Using cn As New SqlConnection(cs)

86.

' nuevo comando

87.

Dim comando As SqlCommand = New SqlCommand()

88.

' Nuevo DataAdapter

89.

Dim da As SqlDataAdapter = New SqlDataAdapter()

90.
91.

'Nuevo DataTable

92.

Dim dt As DataTable = New DataTable()

93.
94.

'Asignacin de propiedades para el comando

95.

With comando

96.

.Connection = cn

97.

.CommandType = CommandType.Text

98.

' instruccin T-SQL para obtener las tablas

99.

.CommandText = "Use [" & la_base_de_datos & "] " & _

100.
ON_SCHEMA.TABLES " & _

"Select * From INFORMATI

101.
PE = 'BASE TABLE'"

"Where TABLE_TY

102.
103.
104.

da.SelectCommand = comando
End With

105.
106.

da.Fill(dt)

107.
108.

' retornar el dataTable

109.

Return dt

110.

End Using

111.
112.
113.

' errores
Catch ex As Exception

114.

MsgBox(ex.Message.ToString)

115.

End Try

116.

Return Nothing

117.
118.

End Function

119.

Private Sub Form1_Load( _

120.

ByVal sender As System.Object, _

121.

ByVal e As System.EventArgs) Handles MyBase.Load

122.

Button1.Text = "Obtener tablas"

123.
124.

' Propiedades del ComboBox

125.

With ComboBox1

126.

.ValueMember = "TABLE_CATALOG"

127.

.DisplayMember = "TABLE_NAME"

128.

End With

129.
130.

End Sub

131.
132.

Private Sub ComboBox1_SelectedIndexChanged( _

133.

ByVal sender As System.Object, _

134.
Changed

ByVal e As System.EventArgs) Handles ComboBox1.SelectedIndex

135.
136.

' cargar los registros de la tabla indicada por el combobox

137.
138.
139.

cargar_ListView( _
ListView1, _

140.

"Select * from [" & ComboBox1.Text.ToString & "]", _

141.

name_bd)

142.
143.

End Sub

144.
145.

Private Sub Button1_Click( _

146.

ByVal sender As System.Object, _

147.

ByVal e As System.EventArgs) Handles Button1.Click

148.
149.
150.

name_bd = InputBox("Escribir el nombre de la base de datos")

151.
152.

If name_bd <> String.Empty Then

153.

Try

154.

' Indicar la base de datos para recuperar

155.

'y cargar las tablas en la lista

156.

ComboBox1.DataSource = get_Tablas(name_bd.Trim)

157.

Catch ex As Exception

158.

MsgBox(ex.Message.ToString)

159.
160.
161.
162.

End Try
End If
End Sub
End Class

Abrimos el Visual Studio '12


Archivo >> Nuevo >> Proyecto >> Aplicacin de Windows Forms
Aceptar
Se nos genera el proyecto con la solucin y quedara as:

Perfecto, ah tenemos el form el cual podemos redisear agregando/modificando/quitando


componentes desde el cuadro de herramientas, que es la pestaa que se encuentra a la
<------

Por ahora, vamos a agregar un DataGridView al Form (ventana), agrandamos el form y nos
dirigimos al cuadro de herramientas y arrastramos esos 2 componentes a la ventana.
Al agregarlo nos saldr una ventanita, dejaremos tododestildado (Quedar como ReadOnly):

Ahora lo que vamos a hacer es cambiar el nombre de dataGridView a DGV, para que quede
ms simple. Teniendo seleccionada la dataGridView vamos a la derecha, a propiedades >>
diseo >> (Name) y lo nombramos DGV

Joya, ahora... qu hacemos? A codear.


Vamos abajo a la derecha a la pestaa que dice "Explorador de soluciones" o simplemente
tocamos F7 para el cdigo (Shift + F7 para el diseo).
Nos encontramos con lo siguiente:

Ahora como vamos a usar cosas que forman parte de una librera que no estamos usando, la
agregamos arriba de todo.

using System.Data.SqlClient;

Ahora fuera de "public Form1" creamos las siguientes variables privadas:


private SqlConnection sqlCon;
private SqlCommand sqlCmd;
private String strCmd;
private DataTable dt;
private SqlDataAdapter sqlDA;

ese "public Form1" es el constructor de la clase, es lo primero que se ejecuta al iniciar el


programa (De lo que nos interesa)
As que agarramos y dentro de ese bloque inicializamos la conexin de la base. Cmo? De la
siguiente manera:

String instancia = "SQLExpress";


String bd = "Agenda";
try
{
sqlCon = new SqlConnection("Server=" + System.Environment.MachineName + "" +
instancia + ";Database= " + bd + ";Trusted_Connection=True" ) ;
sqlCon.Open();
}
catch (SqlException)
{
MessageBox.Show("No se pudo establecer la conexin con la Base de datos." ) ;
Application.Exit();
}
bloque try-catch
en el try:
se se pasa un String por parmetro al instanciar el sqlCon como un nuevo SqlConnection
Internamente al ejecutar el programa quedara, en mi caso, as:
Server=Ludebe-PCSQLExpress;Database=Agenda;Trusted_Connection= True

y al final del try abre la conexin para ya dejarla abierta durante todo le uso del programa.
A m me ensearon que lo ideal es abrir y cerrar al momento de enviar una consulta a la base
de datos, pero en SQL Server, segn le, existe algo que se llama connection pooling, que es
una funcin de Sql que se encarga de administrar las conexiones. Al ser un programita simple,
lo dejamos as.
El catch se ejecuta si se genera una excepcin en el try, en el cual nos mostrara un mensaje y
al aceptar se cerrara el programa.
Quedara as:

El mtodo InitializateComponent() lo que hace es cargar todos los componentes que cargamos
visualmente en el Form en la clase Designer.cs, pero no nos vamos a meter en eso porque no
hace falta
Perfecto, ya inicializamos la conexin. Pero... ahora?
Lo que vamos a hacer es ahora conseguir o traer todos los registros de la tabla Persona para
mostrarlos en una tabla. Cmo? De la siguiente manera con las variables que declaramos
antes:
Vamos a crear un mtodo privado que se va a llamar actualizarTabla() y que se va a encargar
de llenar la tabla con los valores de la BDD.
private void actualizarTabla()
{
dt = new DataTable();
strCmd = "Select * from Personas";
sqlCmd = new SqlCommand(strCmd,sqlCon);
sqlDA = new SqlDataAdapter(sqlCmd);
sqlDA.Fill(dt);
DGV.DataSource = dt;
}
Se vaca la datatable, se crea la consulta (strCmd), se crea el comando pasndole el string y
la conexin ya abierta, el dataAdapter lo que hace es lo que parece, es un adaptador entre
una datatable y SQL. Como ven, al crearlo se le pasa el comando, por lo que al llamar su

mtodo Fill() est llenando la dt con los registros trados desde SQL.
al final, se iguala la DGV al dt. Simple, no? La lgica es fcil, capaz que cuesta aprenderse la
sintxis

En VS quedara as:

Bueno, ahora invocamos el mtodo en el constructor de la clase, justo debajo de donde


instanciamos la conexon de la base as al cargar el Form ya llena la tabla :3 like this:

Listop, ahora si ejecutamos va a explotar todo a la mi*rda porque no tenemos la base creada,
obviamente.

Vamos a proceder a aadir unos textBoxes locos para poder agregar nuevas personas. As
que Shift + F7 o explorador de soluciones >> Form1 doble click
Vamos a agregar un textBox para el nombre, otro para el apellido, uno para el DNI, otro para el
telfono y uno final para la direccin, as bien simple.
Cuadro de herramientas >> TextBox
Arrastran 5 al form como hicieron con el DGV o arrastran uno y copypastean el control:

ahora para reconocerlos arrastramos 5 Labels a la izquierda de cada TextBox:

Va quedando... ahora a cambiar las propiedades de los 10 controles que acabamos de


agregar.
Tenemos que cambiar de los labeles el texto que se muestra y de los textBoxs el nombre en el
diseo.
Vamos con los labeles...
Seleccionamos uno >> propiedades >> apariencia >> text >> "Nombre"
as:

Y despus con los dems, quedara as:

Perfecto, ahora lo mismo con los textBoxes nada ms que en vez de ir a apariencia, vamos a
diseo y cambiamos el (name) a txtBoxNombre, txtBoxApellido, y as con los 5, as podremos
usarlos ms fcil despus al momento de getear o conseguir su Text (lo que est escrito).
(No recomiendo poner acentos en variables, como se presenta en el caso del Telfono y de la
Direccin).
Luego de esto, agregamos un botn debajo de estos controles:

Jueguen con las propiedades, les pueden cambiar la apariencia a casi todo, por ejemplo, a m
en estos tipos de programa me gustan los botones as:

Para ponerlo as vayan a propiedades teniendo el botn seleccionado, apariencia, FlatStyle, y


elijan Flat :3
Bueno, ahora cambienle el nombre en diseo a "btnAgregar" y el texto en apariencia a
Agregar, quedara as:

Por ltimo agregen un botn igual pero que se llame en apariencia Actualizar y en diseo
bntActualizar
Ahora, si hacen doble click en el btnAgregar se va a generar en el cdigo un evento click, que
luego al ejecutar el programa y presionar el botn, se ejecutar lo que est dentrode ese
bloque que se auto-gener. Tambin pueden jugar con este tipo de cosas si van al rayito
teniendo el control seleccionado (sea un botn, un textBox, un label, lo que sea, cualquiera ! )
Ese "rayito" es una forma de declarar eventos muy fcilmente, en otro post hablar de esto
sino nos vamos a la mier e.e

Buem, hagan doble-click en btnagregar para generar el evento y los llevar al cdigo:

Este evento lo que har es ejecutar una consulta a SQL para que se guarde una nueva
persona al tener todos los campos llenos.
Una buena prctica y ac empiezan temas de tica y seguridad mnima, es hacer un mtodo
que compruebe que estn los campos llenos, as no manda un DNI vaco, o un apellido, o lo
que sea que fuese que est sin completar :3
Entoonces, debajo de actualizarTabla() creamos un mtodo privado que devuelva un Boolean
y que se llame camposCompletos()

private Boolean camposCompletos()


{
if (textBoxNombre.Text.Equals( "" ) || textBoxApellido.Text.Equals( "" ) ||
textBoxDNI.Text.Equals( "" ) || textBoxTelefono.Text.Equals( "" ) ||
textBoxDireccion.Text.Equals( "" ))
{
return false;
}
else

{
return true;
}}

Lo que hace este mtodo es comprobar que ningn textBox tenga un Text "", osea, est vaco.
Es similar a poner String.Empty en los parmetros del Equals.

Si se cumple el if es porque hay un campo vaco y devuelve un false.


camposCompletos() == false
sino, devuelve un true
camposCompletos() == true

Quedara as:

Hermoso. Ahora... invocamos el mtodo que acabamos de crear en un if dentro del evento del
botn Agregar

if (camposCompletos()) lo que hace es ver si camposCompletos devuelve un true o un false,


ya que el mtodo devuelve un Boolean.
Si devuelve un true va a llamar a un mtodo privado que devuelve un Boolean guardar() que
vamos a crear a continuacin, sino, muestra un mensaje por pantalla avisando que el lince no
llen los campos.

debajo de camposCompletos() creamos un private void guardar()

private Boolean guardar()


{
try
{
strCmd = "insert into Personas (Nombre,Apellido,DNI,Telefono,Direccion)" +
"values ('" + textBoxNombre.Text + "','" + textBoxApellido.Text + "','"
+ textBoxDNI.Text
+ "','" + textBoxTelefono.Text + "','" + textBoxDireccion.Text + "')";
sqlCmd = new SqlCommand(strCmd,sqlCon);
sqlCmd.ExecuteNonQuery();
MessageBox.Show( "Hecho!" , "Aviso" ) ;

return true;
}
catch(SqlException)
{
MessageBox.Show("No se ha podido guardar en la base de datos","Advertencia" ) ;
return false;
}
}

Queda as:

(Escriban bien Direccion no sean boludos, por dios (? Jajajaja)


Ahora solo queda llamar al mtodo guardar() en el if del evento click del botn
like THIS:

Un espectculo. Ahora generen el evento del btnActualizar haciendo doble-click sobre l y


dentro del bloque llamen al actualizarTabla();
:3 :

Bien maquinola,creo que es hora de pasarnos a Sql.

As que sin cerrar VS abren el SQL Managment Studio, loguan y tocan donde dice new
Query:

y escriben lo siguiente:
create database Agenda
go
use Agenda
go
create table Personas
(
Nombre varchar(30) not null,
Apellido varchar(30) not null,
DNI varchar(12) not null,
Telefono varchar(20) not null,
Direccion varchar(40) not null
)
Lo que hace es crear la base Agenda, usarla y declarar la tabla con sus respectivos campos.
ya con esto podramos ejecutar el programa perfectamente y veremos que funciona.

les dejo la imagen del query, igual despus se las incluyo en el .rar junto al proyecto y al
.exe, pidanlo por MP

Perfecto rufin, a ejecutar el programa!


Vuelvan al Visual Studio y Dleh al play (Compliar y ejecutar [ Iniciar])

Se muestra as:

Si les llega a mostrar la ventana es porque ya conecta bien con la base de datos. De lo
contrario comprueben bien el nombre de la instancia y de la base de datos en el constructor,
como muestro al principio del post, es eso seguro :3
Ahora si tocamos agregar as como viene sin llenar nada, nos tira un errorcito e.:

10 punto'
Ahora llenamos normalmente y tocamos actualizar:

GG WP !
Commend Ludebe.(?
Fjense que qued un poco angostito el DGV, ensanchenlo un toque

Ya tenemos la funcionalidad de agregar un registro a la base, ahora vamos con la editar!

Editar:
Primero cambiemos el nombre del diseo de btnAgregar por btnGuardar y su apariencia a
Guardar, ya que lo usaremos tanto para un nuevo registro como para terminar de editar, ya van
a ver.
(Aument el ancho del DGV ya que estaba)

Si se fijan y hacen doble-click en el botn, el mtodo se seguir llamando btnAgregar_Click,


eso no va a generar conflictos, pero si son caprichosos como yo e.e lo pueden cambiar de la
siguiente forma:
Primero, simplemente donde dice btnAgregar lo cambian por btnGuardar:

Luego, vuelvan al diseo tocando shift + F7, y les dir que hay un error blablabla, esto es
porque en la clase Designer.cs qued el evento click del botn referenciando a btnAgregar,
mientras que ahora es btnGuardar, lo que hacen es ir a donde dice "instancias de este error",
el primre rengln celeste:

y les aparecer esto:

Ahora vern que les marca una lnea en rojo. Lo siguiente es muy fcil, reemplazar btnAgregar
por btnGuardar como hicieron antes, as cambian la referencia del botn.

Fcil, ya no tira error. Pueden cerrar la clase Designer que no la vamos a usar, y volver al
diseo, ahora va a funcionar todo perfecto.
Ahora para poder editar lo que vamos a hacer es configurar el DGV para que se seleccione el
registro o fila completa para poder acceder al index o nmero de fila seleccionada y poder
acceder a los datos de la misma.
Primero, agregamos 2 personas ms para tener un poquito ms de variedad, si es que todava
no lo hicieron:

Ahora configuramos el DGV.


Vamos al constructor del Fom1 (donde inicializamos la conexin) y ah abajo de todo ponemos
lo siguiente:
DGV.SelectionMode = DataGridViewSelectionMode.FullRowSelect ;
DGV.MultiSelect = false ;
Lo que se hace es que se selecicone por fila completa y slo 1, quedara as:

ahora si ejecutamos y vamos al DGV a seleccionar algo, comprobaremos que funca e.e

Ahora lo que hacemos es crear un botn btnEditar con texto Editar:

Vamos al cdigo y como variables arriba de todo declaramos un private Boolean editando, y
en el constructor lo ponemos como editando = false

ahora doble-click en el botn Editar para generar el mtodo, y dentro ponemos lo siguiente:

if (DGV.SelectedRows.Count > 0)
{
editando = true;
textBoxNombre.Text =
DGV.Rows[DGV.SelectedRows[0].Index].Cells["Nombre"].Value.ToString() ;
textBoxApellido.Text =
DGV.Rows[DGV.SelectedRows[0].Index].Cells["Apellido"].Value.ToString() ;
textBoxDNI.Text = DGV.Rows[DGV.SelectedRows[0].Index].Cells["DNI"].Value.ToString() ;
textBoxTelefono.Text =
DGV.Rows[DGV.SelectedRows[0].Index].Cells["Telefono"].Value.ToString() ;
textBoxDireccion.Text =
DGV.Rows[DGV.SelectedRows[0].Index].Cells["Direccion"].Value.ToString() ;
}
Queda as:

lo que se hace es llenar los campos con los valores del registro seleccionado de la tabla.
DGV.SelectedRows[0].Index trae un int index, osea, el nmero del registro o fila seleccionado
del DGV.
textBoxNombre.Text =

DGV.Rows[DGV.SelectedRows[0].Index].Cells["Nombre"].Value.ToString() ;
Ac se setea o asigna al texto del textBoxNombre el valor que est en la fila INDEX y en la
celda NOMBRE del DGV.
Al llenarse los textBox con los valores, los editamos a gusto y al presionar guardar editando va
a ser true, por lo que en vez de insert into va a ser un update where.
Vamos al mtodo guardar y implementamos la edicin de la siguiente manera.
Dentro del try ponemos un if(editando) y dentro del bloque del if, ponemos lo que pasara si se
est editando:
strCmd = "Update Personas set Nombre = '" + textBoxNombre.Text + "', Apellido = '" +
textBoxApellido.Text + "'," +
"DNI = '" + textBoxDNI.Text + "', Telefono = '" + textBoxTelefono.Text + "', Direccion = '" +
textBoxDireccion.Text + "' " +
"where DNI = '" + DGV.Rows[DGV.SelectedRows[0].Index].Cells["DNI"].Value.ToString()+ "'";
sqlCmd = new SqlCommand(strCmd, sqlCon) ;
sqlCmd.ExecuteNonQuery() ;
MessageBox.Show("Hecho!", "Aviso" ) ;
editando = false;
return true ;
y despus, en un else, lo que ya estaba puesto, el guardar o agregar normal.
Queda as:

Comprueben que quede igual en la foto, es difcil poner cdigo en el post por los emoticones y
el formato, se petea todo (?
Bueno... otra aclaracin: El DNI ac funcionara como Primary Key, los que saben van a
entender a qu me refiero, despus voy a hacer un aporte en la comu explicando esto :3
Si hay 2 personas con mismo DNI, va a modificar los 2, lo cual no estara bueno, as que
cuando crean personas, que tengan diferente DNI :3
Probemos:
ejecutamos:

Seleccionamos al Lince y presionamos Editar:

Cambiamos todos los campos para ver si funciona correctamente, guardar y actualizamos:

Y eso es todo para la edicin!

Borrar:
Agregamos un btnBorrar debajo del btnEditar:

doble-click en el botn nuevo para generar el evento y adentro ponemos esto:


if(MessageBox.Show( "Desea realmente borrar este registro?" , "Confirmacin" ,
MessageBoxButtons.YesNo) == DialogResult.Yes)
{
borrar() ;
}
As:

obviamente borrar() nos va a tirar error porque no existe, as que vamos a crearlo.
debajo del mtodo guardar() creamos un private Boolean borrar() con lo siguiente:
try
{
strCmd = "delete from Personas where Nombre = '" +
DGV.Rows[DGV.SelectedRows[0].Index].Cells["Nombre"].Value.ToString() + "' " +
" and DNI = '" + DGV.Rows[DGV.SelectedRows[0].Index].Cells["DNI"].Value.ToString() + "'";
sqlCmd = new SqlCommand(strCmd, sqlCon);
sqlCmd.ExecuteNonQuery();
MessageBox.Show(DGV.Rows[DGV.SelectedRows[0].Index].Cells["Nombre"].Value.ToString()
+ " Borrado correctamente" , "Aviso" ) ;
return true;
}
catch (SqlException)
{
MessageBox.Show("No se ha podido guardar en la base de datos" , "Advertencia" ) ;
return false ;
}
Quedara as:

LISTO LINCE! Si, era slo eso, una boludes


Lo que hace es borrar en Personas donde el DNI sea igual al del registro o fila seleccionado
en el DGV.
Ejecutamos:

Seleccionamos el editado de recin y tocamos borrar

Ponemos que No. na mentira, que s


actualizamos y... VOIL:

Ah tienen, bah.. en realidad ya no lo tienen jajajaj, se borr correctamente. :3


Despus obviamente pueden modificar la interfaz decorandola a gusto para que quede ms
organizada o fachera

As con esta base (de conocimiento) ya hasta lo pueden hacer ustedes, pueden comprobar
que sea un dni vlido, pueden agregar ms campos, un botn cancelar para cancelar la
edicin/borrado de registros y que se limpien los textBox, todo lo que quieran. Lo importante y
lo que quera mostrar es que tan fcil es conectar una base con mi amado C# <3 Lo he hecho
en java y es mucho ms manual, Microsoft al poner mucho ms $$ nos facilita las cosas.

Beautiful.

Listo muchachada, me encant hacer este post, lo disfrut mucho Pienso seguir pero ahora
van a ser aportes en mi comunidad de desarrollo de software Los que quieran
aprender/aportar son todos bienvenidos!!

(No s si este post va en Ciencias y educacin o en Hazlo tu mismo, cre ms correcta


esta categora)
Nos vemos gente ! Felices Fiestas!!

EDIT 1:
Gracias a ustedes ya pude crear la comunidad!

A los que les interese, psense. Ya estoy subiendo algunos aportes que les puede servir!
Voy a empezar tambin a hacer tutoriales para los ms iniciados!
MUCHAS GRACIAS GENTE!

Este es un aporte hecho por m en base a mis conocimientos y experiencias de desarrollo en


C#.
Si quieren ver ms sobre este contenido pueden pasarse por mi comunidad:

Das könnte Ihnen auch gefallen