About the Closest facility solver Sample
[C#]
frmClosestFacilitySolver.cs
//************************************************************************************* // Network Analyst - Closest Facility Demonstration // // This simple code shows how to : // 1) Open a workspace and open a Network DataSet // 2) Create a NAContext and its NASolver // 3) Load Incidents/Facilites from Feature Classes and create Network Locations // 4) Set the Solver parameters // 5) Solve a Closest Facility problem // 6) Read the CFRoutes output to display the total facilities // and the list of the routes found //************************************************************************************ using System; using System.Windows.Forms; using ESRI.ArcGIS.Carto; using ESRI.ArcGIS.esriSystem; using ESRI.ArcGIS.Geodatabase; using ESRI.ArcGIS.Geometry; using ESRI.ArcGIS.NetworkAnalyst; namespace ClosestFacilitySolver { public partial class frmClosestFacilitySolver : Form { private INAContext m_NAContext; public frmClosestFacilitySolver() { InitializeComponent(); Initialize(); } private void Initialize() { IFeatureWorkspace featureWorkspace = null; INetworkDataset networkDataset = null; try { // Open Geodatabase and network dataset IWorkspace workspace = OpenWorkspace(Application.StartupPath + @"\..\..\..\..\..\Data\SanFrancisco\SanFrancisco.gdb"); networkDataset = OpenNetworkDataset(workspace, "Transportation", "Streets_ND"); featureWorkspace = workspace as IFeatureWorkspace; } catch (Exception ex) { System.Windows.Forms.MessageBox.Show("Unable to open dataset. Error Message: " + ex.Message); this.Close(); return; } // Create NAContext and NASolver m_NAContext = CreateSolverContext(networkDataset); // Get Cost Attributes INetworkAttribute networkAttribute; for (int i = 0; i < networkDataset.AttributeCount - 1; i++) { networkAttribute = networkDataset.get_Attribute(i); if (networkAttribute.UsageType == esriNetworkAttributeUsageType.esriNAUTCost) { cboCostAttribute.Items.Add(networkAttribute.Name); cboCostAttribute.SelectedIndex = 0; } } txtTargetFacility.Text = "1"; txtCutOff.Text = ""; // Load locations from a feature class IFeatureClass inputFClass = featureWorkspace.OpenFeatureClass("Stores"); LoadNANetworkLocations("Incidents", inputFClass, 100); inputFClass = featureWorkspace.OpenFeatureClass("FireStations"); LoadNANetworkLocations("Facilities", inputFClass, 100); //Create Layer for Network Dataset and add to ArcMap ILayer layer; INetworkLayer networkLayer; networkLayer = new NetworkLayerClass(); networkLayer.NetworkDataset = networkDataset; layer = networkLayer as ILayer; layer.Name = "Network Dataset"; axMapControl.AddLayer(layer, 0); //Create a Network Analysis Layer and add to ArcMap INALayer naLayer = m_NAContext.Solver.CreateLayer(m_NAContext); layer = naLayer as ILayer; layer.Name = m_NAContext.Solver.DisplayName; axMapControl.AddLayer(layer, 0); } private void cmdSolve_Click(object sender, System.EventArgs e) { try { lstOutput.Items.Clear(); lstOutput.Items.Add("Solving..."); SetSolverSettings(); // Solve IGPMessages gpMessages = new GPMessagesClass(); if (!m_NAContext.Solver.Solve(m_NAContext, gpMessages, null)) GetCFOutput("CFRoutes"); else lstOutput.Items.Add("Partial Result"); // Display Error/Warning/Informative Messages if (gpMessages != null) { for (int i = 0; i < gpMessages.Count; i++) { switch (gpMessages.GetMessage(i).Type) { case esriGPMessageType.esriGPMessageTypeError: lstOutput.Items.Add("Error " + gpMessages.GetMessage(i).ErrorCode.ToString() + " " + gpMessages.GetMessage(i).Description); break; case esriGPMessageType.esriGPMessageTypeWarning: lstOutput.Items.Add("Warning " + gpMessages.GetMessage(i).Description); break; default: lstOutput.Items.Add("Information " + gpMessages.GetMessage(i).Description); break; } } } //Zoom to the extent of the route IGeoDataset geoDataset; IEnvelope envelope; geoDataset = m_NAContext.NAClasses.get_ItemByName("CFRoutes") as IGeoDataset; envelope = geoDataset.Extent; if (!envelope.IsEmpty) envelope.Expand(1.1, 1.1, true); axMapControl.Extent = envelope; axMapControl.Refresh(); } catch (Exception ee) { MessageBox.Show(ee.Message); cmdSolve.Text = "Find Closest Facilities"; } } // Get the Impedance Cost form the CFRoute Class Output public void GetCFOutput(string strNAClass) { ITable table = m_NAContext.NAClasses.get_ItemByName(strNAClass) as ITable; if (table == null) { lstOutput.Items.Add("Impossible to get the " + strNAClass + " table"); } lstOutput.Items.Add("Number facilities found " + table.RowCount(null).ToString()); lstOutput.Items.Add(""); if (table.RowCount(null) > 0) { lstOutput.Items.Add("IncidentID, FacilityID,FacilityRank,Total_" + cboCostAttribute.Text); double total_impedance; long incidentID; long facilityID; long facilityRank; ICursor cursor; IRow row; cursor = table.Search(null, false); row = cursor.NextRow(); while (row != null) { incidentID = long.Parse(row.get_Value(table.FindField("IncidentID")).ToString()); facilityID = long.Parse(row.get_Value(table.FindField("FacilityID")).ToString()); facilityRank = long.Parse(row.get_Value(table.FindField("FacilityRank")).ToString()); total_impedance = double.Parse(row.get_Value(table.FindField("Total_" + cboCostAttribute.Text)).ToString()); lstOutput.Items.Add(incidentID.ToString() + ",\t" + facilityID.ToString() + ",\t" + facilityRank.ToString() + ",\t" + total_impedance.ToString("F2")); row = cursor.NextRow(); } } lstOutput.Refresh(); } //********************************************************************************* // Network Analyst functions // ******************************************************************************** //********************************************************************************* // Create NASolver and NAContext //********************************************************************************* public INAContext CreateSolverContext(INetworkDataset networkDataset) { //Get the Data Element IDENetworkDataset deNDS = GetDENetworkDataset(networkDataset); INASolver naSolver; naSolver = new NAClosestFacilitySolver(); INAContextEdit contextEdit = naSolver.CreateContext(deNDS, naSolver.Name) as INAContextEdit; contextEdit.Bind(networkDataset, new GPMessagesClass()); return contextEdit as INAContext; } //********************************************************************************* // Set Solver Settings //********************************************************************************* public void LoadNANetworkLocations(string strNAClassName, IFeatureClass inputFC, double snapTolerance) { INAClass naClass; INamedSet classes; classes = m_NAContext.NAClasses; naClass = classes.get_ItemByName(strNAClassName) as INAClass; // delete existing Locations except if that a barriers naClass.DeleteAllRows(); // Create a NAClassLoader and set the snap tolerance (meters unit) INAClassLoader classLoader = new NAClassLoader(); classLoader.Locator = m_NAContext.Locator; if (snapTolerance > 0) classLoader.Locator.SnapTolerance = snapTolerance; classLoader.NAClass = naClass; //Create field map to automatically map fields from input class to NAClass INAClassFieldMap fieldMap; fieldMap = new NAClassFieldMap(); fieldMap.CreateMapping(naClass.ClassDefinition, inputFC.Fields); classLoader.FieldMap = fieldMap; // Avoid loading network locations onto non-traversable portions of elements INALocator3 locator = m_NAContext.Locator as INALocator3; locator.ExcludeRestrictedElements = true; locator.CacheRestrictedElements(m_NAContext); //Load Network Locations int rowsIn = 0; int rowsLocated = 0; IFeatureCursor featureCursor = inputFC.Search(null, true); classLoader.Load((ICursor)featureCursor, null, ref rowsIn, ref rowsLocated); //Message all of the network analysis agents that the analysis context has changed ((INAContextEdit)m_NAContext).ContextChanged(); } //********************************************************************************* // Set Solver Settings //********************************************************************************* public void SetSolverSettings() { //Set Route specific Settings INASolver naSolver = m_NAContext.Solver; INAClosestFacilitySolver cfSolver = naSolver as INAClosestFacilitySolver; if (txtCutOff.Text.Length > 0 && IsNumeric(txtCutOff.Text.Trim())) cfSolver.DefaultCutoff = txtCutOff.Text; else cfSolver.DefaultCutoff = null; if (txtTargetFacility.Text.Length > 0 && IsNumeric(txtTargetFacility.Text)) cfSolver.DefaultTargetFacilityCount = int.Parse(txtTargetFacility.Text); else cfSolver.DefaultTargetFacilityCount = 1; cfSolver.OutputLines = esriNAOutputLineType.esriNAOutputLineTrueShapeWithMeasure; cfSolver.TravelDirection = esriNATravelDirection.esriNATravelDirectionToFacility; // Set generic solver settings // Set the impedance attribute INASolverSettings naSolverSettings; naSolverSettings = naSolver as INASolverSettings; naSolverSettings.ImpedanceAttributeName = cboCostAttribute.Text; // Set the OneWay Restriction if necessary IStringArray restrictions; restrictions = naSolverSettings.RestrictionAttributeNames; restrictions.RemoveAll(); if (chkUseRestriction.Checked) restrictions.Add("oneway"); naSolverSettings.RestrictionAttributeNames = restrictions; //Restrict UTurns naSolverSettings.RestrictUTurns = esriNetworkForwardStarBacktrack.esriNFSBNoBacktrack; naSolverSettings.IgnoreInvalidLocations = true; // Set the Hierarchy attribute naSolverSettings.UseHierarchy = chkUseHierarchy.Checked; if (naSolverSettings.UseHierarchy) naSolverSettings.HierarchyAttributeName = "hierarchy"; // Do not forget to update the context after you set your impedance naSolver.UpdateContext(m_NAContext, GetDENetworkDataset(m_NAContext.NetworkDataset), new GPMessagesClass()); } //********************************************************************************* // Geodatabase functions // ******************************************************************************** public IWorkspace OpenWorkspace(string strGDBName) { // As Workspace Factories are Singleton objects, they must be instantiated with the Activator var workspaceFactory = System.Activator.CreateInstance(System.Type.GetTypeFromProgID("esriDataSourcesGDB.FileGDBWorkspaceFactory")) as ESRI.ArcGIS.Geodatabase.IWorkspaceFactory; return workspaceFactory.OpenFromFile(strGDBName, 0); } //********************************************************************************* // Open the network dataset //********************************************************************************* public INetworkDataset OpenNetworkDataset(IWorkspace workspace, string featureDatasetName, string strNDSName) { // Obtain the dataset container from the workspace var featureWorkspace = workspace as IFeatureWorkspace; ESRI.ArcGIS.Geodatabase.IFeatureDataset featureDataset = featureWorkspace.OpenFeatureDataset(featureDatasetName); var featureDatasetExtensionContainer = featureDataset as ESRI.ArcGIS.Geodatabase.IFeatureDatasetExtensionContainer; ESRI.ArcGIS.Geodatabase.IFeatureDatasetExtension featureDatasetExtension = featureDatasetExtensionContainer.FindExtension(ESRI.ArcGIS.Geodatabase.esriDatasetType.esriDTNetworkDataset); var datasetContainer3 = featureDatasetExtension as ESRI.ArcGIS.Geodatabase.IDatasetContainer3; // Use the container to open the network dataset. ESRI.ArcGIS.Geodatabase.IDataset dataset = datasetContainer3.get_DatasetByName(ESRI.ArcGIS.Geodatabase.esriDatasetType.esriDTNetworkDataset, strNDSName); return dataset as ESRI.ArcGIS.Geodatabase.INetworkDataset; } public IDENetworkDataset GetDENetworkDataset(INetworkDataset networkDataset) { //Cast from the Network Dataset to the DatasetComponent IDatasetComponent dsComponent; dsComponent = networkDataset as IDatasetComponent; //Get the Data Element return dsComponent.DataElement as IDENetworkDataset; } private bool IsNumeric(string str) { try { double.Parse(str.Trim()); } catch (Exception) { return false; } return true; } } }
[Visual Basic .NET]
frmClosestFacilitySolver.vb
'************************************************************************************* ' Network Analyst - Closest Facility Demonstration ' ' This simple code shows how to : ' 1) Open an shapefile workspace and open a Network DataSet ' 2) Create a NAContext and its NASolver ' 3) Load Incidents/Facilites from Feature Classes and create Network Locations ' 4) Set the Solver parameters ' 5) Solve a Closest Facility problem ' 6) Read the CFRoutes output to display the total facilities ' and the list of the routes found '************************************************************************************ Imports System Imports System.Windows.Forms Imports ESRI.ArcGIS.Carto Imports ESRI.ArcGIS.esriSystem Imports ESRI.ArcGIS.Geodatabase Imports ESRI.ArcGIS.Geometry Imports ESRI.ArcGIS.NetworkAnalyst Public Class frmClosestFacilitySolver Private m_NAContext As INAContext Public Sub New() 'This call is required by the Windows Form Designer. InitializeComponent() 'Add any initialization after the InitializeComponent() call Initialize() End Sub Private Sub Initialize() ' Open geodatabase and network dataset Dim featureWorkspace As IFeatureWorkspace = Nothing Dim networkDataset As INetworkDataset = Nothing Try Dim workspace As IWorkspace = OpenWorkspace(Application.StartupPath & "\..\..\..\..\..\Data\SanFrancisco\SanFrancisco.gdb") networkDataset = OpenNetworkDataset(workspace, "Transportation", "Streets_ND") featureWorkspace = TryCast(workspace, IFeatureWorkspace) Catch ex As Exception Windows.Forms.MessageBox.Show("Unable to open dataset. Error Message: " + ex.Message) Me.Close() Return End Try ' Create NAContext and NASolver m_NAContext = CreateSolverContext(networkDataset) ' Get Cost Attributes Dim networkAttribute As INetworkAttribute, i As Integer For i = 0 To networkDataset.AttributeCount - 2 networkAttribute = networkDataset.Attribute(i) If networkAttribute.UsageType = esriNetworkAttributeUsageType.esriNAUTCost Then cboCostAttribute.Items.Add(networkAttribute.Name) cboCostAttribute.SelectedIndex = 0 End If Next txtTargetFacility.Text = "1" txtCutOff.Text = "" ' Load locations from FC Dim inputFClass As IFeatureClass = featureWorkspace.OpenFeatureClass("Stores") LoadNANetworkLocations("Incidents", inputFClass, 100) inputFClass = featureWorkspace.OpenFeatureClass("FireStations") LoadNANetworkLocations("Facilities", inputFClass, 100) ' Create Layer for Network Dataset and add to ArcMap Dim networkLayer As INetworkLayer = New NetworkLayerClass networkLayer.NetworkDataset = networkDataset Dim layer As ILayer = networkLayer layer.Name = "Network Dataset" axMapControl.AddLayer(layer, 0) ' Create a Network Analysis Layer and add to ArcMap Dim naLayer As INALayer = m_NAContext.Solver.CreateLayer(m_NAContext) layer = naLayer layer.Name = m_NAContext.Solver.DisplayName axMapControl.AddLayer(layer, 0) End Sub Private Sub cmdSolve_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdSolve.Click Try lstOutput.Items.Clear() lstOutput.Items.Add("Solving...") SetSolverSettings() ' Solve Dim gpMessages As IGPMessages = New GPMessagesClass If Not m_NAContext.Solver.Solve(m_NAContext, gpMessages, Nothing) Then GetCFOutput("CFRoutes") Else lstOutput.Items.Add("Partial Result") End If ' Display Error/Warning/Informative Messages If Not gpMessages Is Nothing Then Dim i As Integer For i = 0 To gpMessages.Count - 1 Select Case gpMessages.GetMessage(i).Type Case esriGPMessageType.esriGPMessageTypeError lstOutput.Items.Add("Error " + gpMessages.GetMessage(i).ErrorCode.ToString() + " " + gpMessages.GetMessage(i).Description) Case esriGPMessageType.esriGPMessageTypeWarning lstOutput.Items.Add("Warning " + gpMessages.GetMessage(i).Description) Case Else lstOutput.Items.Add("Information " + gpMessages.GetMessage(i).Description) End Select Next End If ' Zoom to the extent of the route Dim geoDataset As IGeoDataset = m_NAContext.NAClasses.ItemByName("CFRoutes") Dim envelope As IEnvelope = geoDataset.Extent If Not envelope.IsEmpty Then envelope.Expand(1.1, 1.1, True) axMapControl.Extent = envelope axMapControl.Refresh() End If Catch ee As Exception MessageBox.Show(ee.Message) cmdSolve.Text = "Find Closest Facilities" End Try End Sub ' Get the Impedance Cost form the CFRoute Class Output Public Sub GetCFOutput(ByVal strNAClass As String) Dim table As ITable = m_NAContext.NAClasses.ItemByName(strNAClass) If table Is Nothing Then lstOutput.Items.Add("Impossible to get the " + strNAClass + " table") End If lstOutput.Items.Add("Number facilities found " + table.RowCount(Nothing).ToString()) lstOutput.Items.Add("") If table.RowCount(Nothing) > 0 Then lstOutput.Items.Add("IncidentID, FacilityID, FacilityRank, Total_" + cboCostAttribute.Text) Dim total_impedance As Double Dim incidentID As Long Dim facilityID As Long Dim facilityRank As Long Dim cursor As ICursor Dim row As IRow cursor = table.Search(Nothing, False) row = cursor.NextRow() While Not row Is Nothing incidentID = Long.Parse(row.Value(table.FindField("IncidentID")).ToString()) facilityID = Long.Parse(row.Value(table.FindField("FacilityID")).ToString()) facilityRank = Long.Parse(row.Value(table.FindField("FacilityRank")).ToString()) total_impedance = Double.Parse(row.Value(table.FindField("Total_" + cboCostAttribute.Text)).ToString()) lstOutput.Items.Add(incidentID.ToString() + "," + vbTab + facilityID.ToString() + "," + vbTab + facilityRank.ToString() + "," + vbTab + total_impedance.ToString("F2")) row = cursor.NextRow() End While End If lstOutput.Refresh() End Sub '********************************************************************************* ' Network Analyst functions ' ******************************************************************************** '********************************************************************************* ' Create NASolver and NAContext '********************************************************************************* Public Function CreateSolverContext(ByVal networkDataset As INetworkDataset) As INAContext ' Get the Data Element Dim deNDS As IDENetworkDataset = GetDENetworkDataset(networkDataset) Dim naSolver As INASolver = New NAClosestFacilitySolver Dim contextEdit As INAContextEdit = naSolver.CreateContext(deNDS, naSolver.Name) contextEdit.Bind(networkDataset, New GPMessagesClass) Return contextEdit End Function '********************************************************************************* ' Load network locations '********************************************************************************* Public Sub LoadNANetworkLocations(ByVal strNAClassName As String, ByVal inputFC As IFeatureClass, ByVal snapTolerance As Double) Dim classes As INamedSet = m_NAContext.NAClasses Dim naClass As INAClass = classes.ItemByName(strNAClassName) ' delete existing Locations except if that a barriers naClass.DeleteAllRows() ' Create a NAClassLoader and set the snap tolerance (meters unit) Dim classLoader As INAClassLoader = New NAClassLoader classLoader.Locator = m_NAContext.Locator If snapTolerance > 0 Then classLoader.Locator.SnapTolerance = snapTolerance End If classLoader.NAClass = naClass ' Create field map to automatically map fields from input class to naclass Dim fieldMap As INAClassFieldMap = New NAClassFieldMap fieldMap.CreateMapping(naClass.ClassDefinition, inputFC.Fields) classLoader.FieldMap = fieldMap ' Avoid loading network locations onto non-traversable portions of elements Dim locator As INALocator3 = TryCast(m_NAContext.Locator, INALocator3) locator.ExcludeRestrictedElements = True locator.CacheRestrictedElements(m_NAContext) ' Load Network Locations Dim rowsIn As Integer = 0 Dim rowsLocated As Integer = 0 Dim featureCursor As IFeatureCursor = inputFC.Search(Nothing, True) classLoader.Load(featureCursor, Nothing, rowsIn, rowsLocated) ' Message all of the network analysis agents that the analysis context has changed Dim naContextEdit As INAContextEdit = m_NAContext naContextEdit.ContextChanged() End Sub '********************************************************************************* ' Set Solver Settings '********************************************************************************* Public Sub SetSolverSettings() 'Set Route specific Settings Dim naSolver As INASolver = m_NAContext.Solver Dim cfSolver As INAClosestFacilitySolver = naSolver If txtCutOff.Text.Length > 0 And IsNumeric(txtCutOff.Text.Trim()) Then cfSolver.DefaultCutoff = txtCutOff.Text Else cfSolver.DefaultCutoff = Nothing End If If txtTargetFacility.Text.Length > 0 And IsNumeric(txtTargetFacility.Text) Then cfSolver.DefaultTargetFacilityCount = Integer.Parse(txtTargetFacility.Text) Else cfSolver.DefaultTargetFacilityCount = 1 End If cfSolver.OutputLines = esriNAOutputLineType.esriNAOutputLineTrueShapeWithMeasure cfSolver.TravelDirection = esriNATravelDirection.esriNATravelDirectionToFacility 'Set generic Solver settings ' set the impedance attribute Dim naSolverSettings As INASolverSettings = naSolver naSolverSettings.ImpedanceAttributeName = cboCostAttribute.Text ' Set the OneWay Restriction if necessary Dim restrictions As IStringArray = naSolverSettings.RestrictionAttributeNames restrictions.RemoveAll() If chkUseRestriction.Checked Then restrictions.Add("oneway") End If naSolverSettings.RestrictionAttributeNames = restrictions 'Restrict UTurns naSolverSettings.RestrictUTurns = esriNetworkForwardStarBacktrack.esriNFSBNoBacktrack naSolverSettings.IgnoreInvalidLocations = True ' Set the Hierarchy attribute naSolverSettings.UseHierarchy = chkUseHierarchy.Checked If naSolverSettings.UseHierarchy Then naSolverSettings.HierarchyAttributeName = "hierarchy" End If ' Do not forget to update the context after you set your impedance naSolver.UpdateContext(m_NAContext, GetDENetworkDataset(m_NAContext.NetworkDataset), New GPMessagesClass) End Sub '********************************************************************************* ' Solve the problem '********************************************************************************* Public Function Solve(ByVal naContext As INAContext, ByVal gpMessages As IGPMessages) As String Dim errStr As String = "" Try ' Solving the Problem errStr = "Error when solving" Dim isPartialSolution As Boolean = naContext.Solver.Solve(naContext, gpMessages, Nothing) If Not isPartialSolution Then errStr = "OK" Else errStr = "Partial Solution" End If Catch e As Exception errStr += " Error Description " + e.Message End Try Return errStr End Function '********************************************************************************* ' Geodatabase functions '********************************************************************************* Public Function OpenWorkspace(ByVal strGDBName As String) As IWorkspace ' As Workspace Factories are Singleton objects, they must be instantiated with the Activator Dim workspaceFactory As IWorkspaceFactory = TryCast(Activator.CreateInstance(Type.GetTypeFromProgID("esriDataSourcesGDB.FileGDBWorkspaceFactory")), IWorkspaceFactory) Return workspaceFactory.OpenFromFile(strGDBName, 0) End Function '********************************************************************************* ' Open the network dataset '********************************************************************************* Public Function OpenNetworkDataset(ByVal workspace As IWorkspace, ByVal featureDatasetName As String, ByVal strNDSName As String) As INetworkDataset ' Obtain the dataset container from the workspace Dim featureWorkspace As IFeatureWorkspace = TryCast(workspace, IFeatureWorkspace) Dim featureDataset As IFeatureDataset = featureWorkspace.OpenFeatureDataset(featureDatasetName) Dim featureDatasetExtensionContainer As IFeatureDatasetExtensionContainer = TryCast(featureDataset, IFeatureDatasetExtensionContainer) Dim featureDatasetExtension As IFeatureDatasetExtension = featureDatasetExtensionContainer.FindExtension(ESRI.ArcGIS.Geodatabase.esriDatasetType.esriDTNetworkDataset) Dim datasetContainer3 As IDatasetContainer3 = TryCast(featureDatasetExtension, IDatasetContainer3) ' Use the container to open the network dataset Dim dataset As Object = datasetContainer3.DatasetByName(ESRI.ArcGIS.Geodatabase.esriDatasetType.esriDTNetworkDataset, strNDSName) Return TryCast(dataset, INetworkDataset) End Function Public Function GetDENetworkDataset(ByVal pNetDataset As INetworkDataset) As IDENetworkDataset 'Cast from the Network Dataset to the DatasetComponent Dim dsComponent As IDatasetComponent = pNetDataset 'Get the Data Element Return dsComponent.DataElement End Function Private Function IsNumeric(ByVal str As String) As Boolean Try Double.Parse(str.Trim()) Catch Return False End Try Return True End Function End Class