|
Validating Input with the Windows Forms DataGrid Control
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vbcon/html/vbtskHandlingErrorsWithWindowsFormsDataGridControl.asp?frame=true
|
|
|
|
|
I tried this code but having a tiny problem:
I get the error @ AddHandler customersDataSet1.Tables("Customers").ColumnChanging, AddressOf Customers_ColumnChanging
any idea?
Imports System.Data
Imports System.Data.OleDb
Inherits System.Windows.Forms.Form
Dim da As New OleDbDataAdapter()
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim ds As New Data.DataSet()
' Visual Basic
' Assumes the grid is bound to a dataset called customersDataSet1
' with a table called Customers.
' Put this code in the form's Load event or its constructor.
Dim conn As OleDbConnection = New OleDbConnection("Provider = Microsoft.jet.oledb.4.0;Data source = D:\Program files\Microsoft Visual Studio .NET\Crystal Reports\Samples\Database\Xtreme.mdb")
If conn.State <> ConnectionState.Open Then conn.Open()
da.SelectCommand.CommandText = "Select * from customer"
da.SelectCommand.CommandType = CommandType.Text
da.Fill(ds, "customersDataSet1")
DataGrid1.DataSource = ds
DataGrid1.DataMember = "customersDataSet1"
AddHandler customersDataSet1.Tables("Customers").ColumnChanging, AddressOf Customers_ColumnChanging
End Sub
Private Sub Customers_ColumnChanging(ByVal sender As Object, _
ByVal e As System.Data.DataColumnChangeEventArgs)
' Only check for errors in the Product column
If (e.Column.ColumnName.Equals("Product")) Then
' Do not allow "Automobile" as a product.
If CType(e.ProposedValue, String) = "Automobile" Then
Dim badValue As Object = e.ProposedValue
e.ProposedValue = "Bad Data"
e.Row.RowError = "The Product column contians an error"
e.Row.SetColumnError(e.Column, "Product cannot be " & _
CType(badValue, String))
End If
End If
End Sub
|
|
|
|
|
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim ds As DataSet = New DataSet
Dim strSQL As String
Dim cmSQL As OleDbCommand
Dim conn As OleDbConnection = New OleDbConnection( _
"Provider = Microsoft.jet.oledb.4.0;Data source = C:\Program files\Microsoft Visual Studio .NET\Crystal Reports\Samples\Database\Xtreme.mdb")
If conn.State <> ConnectionState.Open Then conn.Open()
strSQL = "SELECT * FROM Customer"
cmSQL = New OleDbCommand(strSQL, conn)
da.SelectCommand = cmSQL
da.Fill(ds, "Customer")
DataGrid1.DataSource = ds.Tables(0)
AddHandler ds.Tables(0).ColumnChanging, AddressOf Customers_ColumnChanging
End Sub
|
|
|
|
|
I forgot to change your drive letter back to D: in the last post, sorry.
Here is the rest of the code.
Private Sub Customers_ColumnChanging(ByVal sender As Object, _
ByVal e As System.Data.DataColumnChangeEventArgs)
' Only check for errors in the Contact Title column
If (e.Column.ColumnName.Equals("Contact Title")) Then
' Do not allow it to be empty
If CType(e.ProposedValue, String) = String.Empty Then
Dim badValue As Object = e.ProposedValue
e.ProposedValue = "Bad Data"
e.Row.RowError = "The Contact Title column contians an error"
e.Row.SetColumnError(e.Column, "Contact Title cannot be Empty")
Else
e.Row.ClearErrors()
End If
End If
End Sub
|
|
|
|
|
Thanks a bunch.
Its working fine.
|
|
|
|
|
There is one tiny problem here.
I want to check if the value entered is 'Mr' or 'Mrs'
ElseIf CType(e.ProposedValue, String) <> "Mr" or CType(e.ProposedValue, String) <> "Mrs" Then
But when I enter Mr or Mrs it displays the error mssg.
But when I check the value individually it works fine:
ElseIf CType(e.ProposedValue, String) <> "Mr" Then
If (e.Column.ColumnName.Equals("Contact Title")) Then
' Do not allow it to be empty
If CType(e.ProposedValue, String) = String.Empty Then
Dim badValue As Object = e.ProposedValue
e.ProposedValue = "Bad Data"
e.Row.RowError = "The Contact Title column contians an error"
e.Row.SetColumnError(e.Column, "Contact Title cannot be Empty")
' Do not allow if the value is not 'Mr' or Mrs
ElseIf CType(e.ProposedValue, String) <> "Mr" Or CType(e.ProposedValue, String) <> "Mrs" Then
Dim badValue As Object = e.ProposedValue
e.ProposedValue = "This field is limited to'Mr','Mrs'"
e.Row.RowError = "The Contact Title column contians an error"
e.Row.SetColumnError(e.Column, "Contact Title cannot be Empty")
Else
e.Row.ClearErrors()
End If
End If
|
|
|
|
|
Yes, that is a strange problem, but I it works good this way.
Private Sub Customers_ColumnChanging(ByVal sender As Object, _
ByVal e As System.Data.DataColumnChangeEventArgs)
' Only check for errors in the Contact Title column
If (e.Column.ColumnName.Equals("Contact Title")) Then
Select Case CType(e.ProposedValue, String)
Case String.Empty
Dim badValue As Object = e.ProposedValue
e.ProposedValue = "Bad Data"
e.Row.RowError = "The Contact Title column contians an error"
e.Row.SetColumnError(e.Column, "Contact Title cannot be Empty")
Case "Mr."
e.Row.ClearErrors()
Case "Mrs."
e.Row.ClearErrors()
Case "Miss"
e.Row.ClearErrors()
Case "Dr."
e.Row.ClearErrors()
Case Else
Dim badValue As Object = e.ProposedValue
e.ProposedValue = "Bad Data"
e.Row.RowError = "This field is limited to'Mr','Mrs'"
e.Row.SetColumnError(e.Column, "Contact Title cannot be Empty")
End Select
End If
End Sub
|
|
|
|
|
|
Yes, that is very strang it can't seem to evaluate "And" "Or" either. humm, guess it's a glitch.
I don't know if you noticed it or not but if you hover over the red Icon's when it's in error it produces a popup balloon to tell you the error.
It will not set "Changes" to the dataset either, until all the errors are cleared. You can trap that also.
also I forgot to finish the errors in the last two line, here they are.
Dim badValue As Object = e.ProposedValue
e.ProposedValue = "Bad Data"
e.Row.RowError = "This field is limited to 'Mr.','Mrs.', 'Miss', or 'Dr.'"
e.Row.SetColumnError(e.Column, "Contact Title field is limited to 'Mr.','Mrs.', 'Miss', or 'Dr.'")
|
|
|
|
|
If I try to check that the value is either 'Mr' or 'Mrs' it wont work..
What am I missing?
ElseIf (CType(e.ProposedValue, String) <> "Mr") or (CType(e.ProposedValue, String) <> "Mr") Then
It works fine if I check for only "Mr"
code:
If CType(e.ProposedValue, String) = String.Empty Then
Dim badValue As Object = e.ProposedValue
e.ProposedValue = "Bad Data"
e.Row.RowError = "The Contact Title column contians an error"
e.Row.SetColumnError(e.Column, "Contact Title cannot be Empty")
' Do not allow if the value is not 'Mr'
ElseIf (CType(e.ProposedValue, String) <> "Mr") Then
Dim badValue As Object = e.ProposedValue
e.ProposedValue = ""
e.Row.RowError = "The Contact Title column contians an error"
e.Row.SetColumnError(e.Column, "Contact Title cannot be Empty")
|
|
|
|
|
Private Sub Customers_ColumnChanging(ByVal sender As Object, _
ByVal e As System.Data.DataColumnChangeEventArgs)
' Only check for errors in the Contact Title column
If (e.Column.ColumnName.Equals("Contact Title")) Then
Select Case CType(e.ProposedValue, String)
Case String.Empty
Dim badValue As Object = e.ProposedValue
e.ProposedValue = "Bad Data"
e.Row.RowError = "The Contact Title column contians an error"
e.Row.SetColumnError(e.Column, "Contact Title cannot be Empty")
Case "Mr."
e.Row.ClearErrors()
Case "Mrs."
e.Row.ClearErrors()
Case "Miss"
e.Row.ClearErrors()
Case "Dr."
e.Row.ClearErrors()
Case Else
Dim badValue As Object = e.ProposedValue
e.ProposedValue = "Bad Data"
e.Row.RowError = "The Contact Title column contians an error"
e.Row.SetColumnError(e.Column, "Contact Title is limited to 'Mr.','Mrs.', 'Miss', or 'Dr.'")
End Select
End If
End Sub
|
|
|
|
|
This may be of intrest to you.
Dim da As New OleDbDataAdapter
Dim da2 As New OleDbDataAdapter
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Dim ds As DataSet = New DataSet
Dim ds2 As DataSet = New DataSet
Dim strSQL As String
Dim strSQL2 As String
Dim cmSQL As OleDbCommand
Dim cmSQL2 As OleDbCommand
Dim conn As OleDbConnection = New OleDbConnection( _
"Provider = Microsoft.jet.oledb.4.0;Data source = D:\Program files\Microsoft Visual Studio .NET\Crystal Reports\Samples\Database\Xtreme.mdb")
If conn.State <> ConnectionState.Open Then conn.Open()
strSQL = "SELECT * FROM Customer"
cmSQL = New OleDbCommand(strSQL, conn)
da.SelectCommand = cmSQL
da.Fill(ds, "Customer")
DataGrid1.DataSource = ds.Tables(0)
strSQL2 = "SELECT * FROM [Orders Detail]"
cmSQL2 = New OleDbCommand(strSQL2, conn)
da2.SelectCommand = cmSQL2
da2.Fill(ds2, "[Orders Detail]")
DataGrid2.DataSource = ds2.Tables(0)
' add a new DataColumn Called "Total" to DataGrid2
Dim myColumn As DataColumn = New DataColumn
myColumn.DataType = System.Type.GetType("System.Decimal")
myColumn.AllowDBNull = False
myColumn.Caption = "Total"
myColumn.ColumnName = "Total"
myColumn.DefaultValue = 0
myColumn.Expression = "[Quantity] * [Unit Price]"
' Add the new DataColumn to the table.
ds2.Tables(0).Columns.Add(myColumn)
AddHandler ds.Tables(0).ColumnChanging, AddressOf Customers_ColumnChanging
End Sub
Private Sub ComputeByOrderID(ByVal myDataSet As DataSet)
' Presumes a DataTable named "Order Details" that
' has a new DataColumn named "Total".
Dim myTable As DataTable
myTable = myDataSet.Tables(0)
Dim objSum As Object
objSum = myTable.Compute("Sum(Total)", "[Order ID] > 0")
End Sub
Private Sub Customers_ColumnChanging(ByVal sender As Object, _
ByVal e As System.Data.DataColumnChangeEventArgs)
' Only check for errors in the Contact Title column
If (e.Column.ColumnName.Equals("Contact Title")) Then
Select Case CType(e.ProposedValue, String)
Case String.Empty
Dim badValue As Object = e.ProposedValue
e.ProposedValue = "Bad Data"
e.Row.RowError = "The Contact Title column contians an error"
e.Row.SetColumnError(e.Column, "Contact Title cannot be Empty")
Case "Mr."
e.Row.ClearErrors()
Case "Mrs."
e.Row.ClearErrors()
Case "Miss"
e.Row.ClearErrors()
Case "Dr."
e.Row.ClearErrors()
Case Else
Dim badValue As Object = e.ProposedValue
e.ProposedValue = "Bad Data"
e.Row.RowError = "The Contact Title column contians an error"
e.Row.SetColumnError(e.Column, "Contact Title is limited to 'Mr.','Mrs.', 'Miss', or 'Dr.'")
End Select
End If
End Sub
|
|
|
|
|
Hi all,
I have the following problem: i got an ip (such as 209.89.34.23) which associated with a computer current online. How can I get all available information of this computer by using VB and which API should be used?
I know the function gethostbyaddr when i implemented it to get hostname of local network, working correctly. But when use it to get hostname from an internet IP, is not working and raise following error "Valid name, no data record of requested type."
What should i do to get all available information of computer i got the IP
|
|
|
|
|
What do you mean by "all available information"? Are you talking about getting any DNS record for the IP address?
RageInTheMachine9532
"...a pungent, ghastly, stinky piece of cheese!" -- The Roaming Gnome
|
|
|
|
|
all available information here is can be:
- Opened ports
- Domain
- NetBIOS name
- Groups
- Users
- Transport
- Share
I have a question for scan algorithm.
Scenarion:
1) User input IPs range (from IP -> to IP).
2) Application scan the range and return live host associated with IP in range
3) Gather information about IP scanned
Can you show me the way to do this? I mean how to scan and how to find the live host associated with its (with code for scaning process is very helpful)
|
|
|
|
|
THen you going to need to use WMI and the System.Management namespace to get at the information. There is no one function call you can make to do this. It's going to be alot of code to write because you looking to get all kinds of dissimilar information.
RageInTheMachine9532
"...a pungent, ghastly, stinky piece of cheese!" -- The Roaming Gnome
|
|
|
|
|
When use WMI, the target computer must have WMI installed too rite? I want to check every possible getted information. You know what i mean. If there anything available to get, just get other wise do nothing. Therefore, i think should not use WMI.
How about your opinion?
|
|
|
|
|
In that case, most of the information you want is not available without writing a client inventory application that must be running on each client machines you want to scan. For that app, you'll have to write LOTS, and I mean A LOT, of code to get each little piece you want and put it together into a form that you can transfer to the server machine collecting this data.
WMI is the ONLY way to go for this if you want to keep the code down to a minimum... Granted, WMI is not available without installing software for it on WIndows NT SP3, Me, 98, 95 and below. Windows 2000 and above come with WMI standard.
RageInTheMachine9532
"...a pungent, ghastly, stinky piece of cheese!" -- The Roaming Gnome
|
|
|
|
|
Thank Dave for remine me, sound good!
I agree that we must use WMI to get information. Now i wonder how can i do that. I mean, i'm a newbie to do that. Can you show me the way to check for all available port and other information by using WMI (working on Win2K SP4) and programming in VB6
|
|
|
|
|
Actually, I'm working on such a solution now. The code is pretty ruff, but it gets the point acrossed. In order to see the SWbem* classes, you'll need to add a Reference to the Microsoft WMI Scripting Library.
Dim wmiLocator As New SWbemLocator
Dim wmiService As SWbemServices
Dim wmiObjectSet As SWbemObjectSet
Dim wmiObject As SWbemObject
Dim intIndex As Long
On Error Resume Next
Set wmiService = wmiLocator.ConnectServer(strNameOrAddress, "root\CIMV2")
If Err.Number = 0 Then
Set wmiObjectSet = wmiService.ExecQuery("SELECT * FROM Win32_ComputerSystem")
For Each wmiObject In wmiObjectSet
With objWMIDataBlock
.strMachineName = Trim(wmiObject.Properties_("Name"))
.strDomain = Trim(wmiObject.Properties_("Domain"))
.strManufacturer = Trim(wmiObject.Properties_("Manufacturer"))
.strModel = Trim(wmiObject.Properties_("Model"))
.intRAMSize = Round((wmiObject.Properties_("TotalPhysicalMemory") \ 1048576) / 32, 0) * 32
End With
Next
Set wmiObjectSet = wmiService.ExecQuery("SELECT * FROM Win32_OperatingSystem")
For Each wmiObject In wmiObjectSet
With objWMIDataBlock
.strOSName = Trim(wmiObject.Properties_("Caption"))
.strOSServicePack = Trim(wmiObject.Properties_("CSDVersion"))
.strOSVersion = Trim(wmiObject.Properties_("Version"))
End With
Next
Set wmiObjectSet = wmiService.ExecQuery("SELECT * FROM Win32_BIOS")
For Each wmiObject In wmiObjectSet
With objWMIDataBlock
.strBIOSManufacturer = Trim(wmiObject.Properties_("Version"))
.strBIOSVersion = Trim(wmiObject.Properties_("SMBIOSMajorVersion")) & _
"." & Trim(wmiObject.Properties_("SMBIOSMinorVersion"))
.strBIOSRevision = Trim(wmiObject.Properties_("SMBIOSBIOSVersion"))
.strSerialNumber = Trim(wmiObject.Properties_("SerialNumber"))
End With
Next
Set wmiObjectSet = wmiService.ExecQuery("SELECT * FROM Win32_NetworkAdapter WHERE AdapterType='Ethernet 802.3'")
ReDim objWMIDataBlock.arrNICs(wmiObjectSet.Count - 1)
objWMIDataBlock.NICCount = wmiObjectSet.Count
intIndex = 0
For Each wmiObject In wmiObjectSet
With objWMIDataBlock.arrNICs(intIndex)
.strMAC = MACCleanup(wmiObject.Properties_("MACAddress"))
.strDescription = Trim(wmiObject.Properties_("Caption"))
End With
intIndex = intIndex + 1
Next
Set wmiObjectSet = wmiService.ExecQuery("SELECT * FROM Win32_QuickFixEngineering")
intIndex = 0
For Each wmiObject In wmiObjectSet
If Left(wmiObject.Properties_("HotFixID"), 4) <> "File" Then
ReDim Preserve objWMIDataBlock.arrHotFixes(intIndex)
With objWMIDataBlock.arrHotFixes(intIndex)
.strHoxFixID = Trim(wmiObject.Properties_("HotFixID"))
.strDescription = Trim(wmiObject.Properties_("Description"))
End With
intIndex = intIndex + 1
End If
Next
objWMIDataBlock.HotFixCount = intIndex
Else
objWMIDataBlock.strMachineName = strNameOrAddress
status strNameOrAddress & " is alive but WMI Service could not be reached."
LogError strNameOrAddress, "", Err.Number, Err.Description
End If
On Error GoTo 0
RageInTheMachine9532
"...a pungent, ghastly, stinky piece of cheese!" -- The Roaming Gnome
|
|
|
|
|
Thank for posting, nice post. I have an example to follow:
I want to display the following information of a remote computer. The following information displayed when i input IP/Hostname then click a function to enumarate the information. I don't know how it process and how can i do that? (i mean the solution, the code and many more, 'cause i'm starting to research it). The result is display while using SuperScan to scan it (for more information please refer to it, i just want to develop an application like that).
The Enumaration Type of display is:
- NetBIOS Name Table
- NULL Session
- MAC Address
- Workstation Type
- Users
- Groups
- Account Policies
- Shares
- Domains
- Remote Time of Day
- Logon Sessions
- Drives
- Trusted Domains
- Services
=====================================================================
NetBIOS information on 192.168.2.13
5 names in table
HIEULN 00 UNIQUE Workstation service name
HIEULN 20 UNIQUE Server services name
WORKGROUP 00 GROUP Workstation service name
WORKGROUP 1E GROUP Group name
HIEULN 03 UNIQUE Messenger name
MAC address 0: 00:0D:60:35:B2:11
Attempting a NULL session connection on 192.168.2.13
NULL session successful to \\192.168.2.13\IPC$
MAC addresses on 192.168.2.13
MAC address 0: 00:00:00:00:00:00
\Device\NetbiosSmb
MAC address 1: 00:0D:60:35:B2:11
\Device\NetBT_Tcpip_{2D3783AA-F4A7-44BF-9776-3CE86BC1890C}
Workstation/server type on 192.168.2.13
Windows 2000
Workstation/Server Name : "192.168.2.13"
Platform ID : 500
Version : 5.0
Comment : ""
Type : 00011003
LAN Manager Workstation
LAN Manager Server
NT/2000 Workstation
Users on 192.168.2.13
Total Users: 8
--- 1 ---
User "a"
Full Name: "a"
System Comment: "a"
User Comment: ""
Last logon: Never
Password expires: Never
Password changed: Never
Locked out: No
Disabled: No
Number of logons: 0
Bad password count: 0
--- 2 ---
User "ACTUser"
Full Name: "Application Center Test Account"
System Comment: "Account used to launch the Application Center Test Broker and Controller services"
User Comment: ""
Last logon: Thu Sep 30 15:26:58 2004 (10 days ago)
Password expires: Never
Password changed: 27 days ago
Locked out: No
Disabled: No
Number of logons: 57
Bad password count: 0
--- 3 ---
Admin "Administrator"
Full Name: ""
System Comment: "Built-in account for administering the computer/domain"
User Comment: ""
Last logon: Mon Oct 11 08:37:23 2004 (0 days ago)
Password expires: Never
Password changed: 28 days ago
Locked out: No
Disabled: No
Number of logons: 56
Bad password count: 26
--- 4 ---
User "ASPNET"
Full Name: "ASP.NET Machine Account"
System Comment: "Account used for running the ASP.NET worker process (aspnet_wp.exe)"
User Comment: "Account used for running the ASP.NET worker process (aspnet_wp.exe)"
Last logon: Never
Password expires: Never
Password changed: 27 days ago
Locked out: No
Disabled: No
Number of logons: 0
Bad password count: 0
--- 5 ---
User "Guest"
Full Name: ""
System Comment: "Built-in account for guest access to the computer/domain"
User Comment: ""
Last logon: Never
Password expires: Never
Password changed: 27 days ago
Locked out: No
Disabled: Yes
Number of logons: 0
Bad password count: 0
--- 6 ---
Admin "hieuln"
Full Name: "hieuln"
System Comment: ""
User Comment: ""
Last logon: Never
Password expires: Never
Password changed: 27 days ago
Locked out: No
Disabled: No
Number of logons: 0
Bad password count: 0
--- 7 ---
User "isphere"
Full Name: "isphere"
System Comment: ""
User Comment: ""
Last logon: Mon Oct 11 08:51:33 2004 (0 days ago)
Password expires: Never
Password changed: 27 days ago
Locked out: No
Disabled: No
Number of logons: 0
Bad password count: 0
--- 8 ---
User "SQLDebugger"
Full Name: "SQLDebugger"
System Comment: "This user account is used by the Visual Studio .NET Debugger"
User Comment: ""
Last logon: Never
Password expires: Never
Password changed: 27 days ago
Locked out: No
Disabled: No
Number of logons: 0
Bad password count: 0
Groups on 192.168.2.13
Group: Administrators
HIEULN\Administrator
HIEULN\hieuln
Group: Backup Operators
Group: Guests
HIEULN\Guest
Group: Power Users
Group: Replicator
Group: Users
NT AUTHORITY\INTERACTIVE
NT AUTHORITY\Authenticated Users
HIEULN\ASPNET
HIEULN\ACTUser
HIEULN\SQLDebugger
HIEULN\Guest
HIEULN\hieuln
HIEULN\isphere
HIEULN\a
Group: Debugger Users
HIEULN\Administrator
Group: NC_S_ISLCK
Group: VS Developers
Password and account policies on 192.168.2.13
Account lockout threshold is 0
Account lockout duration is 30 mins
Minimum password length is 0
Maximum password age is 42 days
Shares on 192.168.2.13
IPC: IPC$ (Remote IPC)
Disk: D$ (Default share)
Disk: virus ()
Disk: books ()
Disk: setup ()
Disk: share ()
Disk: ERP_PCS ()
Disk: hieulnwork_atnight_29 ()
Disk: Windows Script Host 2.0 ()
Disk: ADMIN$ (Remote Admin)
Disk: C$ (Default share)
Domains on 192.168.2.13
Remote time of day on 192.168.2.13
Date: 10/11/2004
Time: 05:06:16
Timezone: GMT +07:00
Uptime: 0 days, 3 hours, 30 minutes
Logon sessions on 192.168.2.13
Total Sessions: 3
\\THIENHD ISPHERE Uptime: 3:14:43 Idle: 0:02:33
\\DEV007 ISPHERE Uptime: 2:50:14 Idle: 0:13:45
\\192.168.2.12 Uptime: 0:00:01 Idle: 0:00:00
Trusted Domains on 192.168.2.13
Remote services on 192.168.2.13
Enumeration complete
==============================================================================
|
|
|
|
|
http://www.microsoft.com/technet/community/scriptcenter/compmgmt/default.mspx
|
|
|
|
|
Hi, I use open dialog control in my VB.NET project to get the file name to the label control on form. It is working fine when I choose the file then it return the value to the label, but How could I control the user if they choose the CANCEL button?
A thousand mile of journey, begin with the first step.
APO-CEDC
Save Children Norway-Cambodia Office
|
|
|
|
|
If he clicks the Cancel Button the ShowDialog method of OpenFileDialog button will return DialogResult.Cancel and you can check for this to do what you want. Hope I understand your question.
|
|
|
|
|
Thank you very much, now it is working.
A thousand mile of journey, begin with the first step.
APO-CEDC
Save Children Norway-Cambodia Office
|
|
|
|
|