I am writing a C# script to do a ping test on a network. I have it mostly working, except the part to write to richtextbox. I need to write results as the ping test is happening. I know it needs multithreading, but I have spent the past week trying to figure it out, and cannot. Here is the code I have, minus the unnecessary parts. The part I am having trouble with is the RunPingTest function. I will also include my sample xml for the IP addresses. All the buttons and other functions otherwise work as intended.
using System.Diagnostics;
using System.Windows;
using System.Xml.Linq;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System;
using System.Threading;
namespace NetTestingTool
{
public partial class NetTest : Window
{
static string dataFolder = "D:";
static readonly string configPath = dataFolder + "\\ConfigFiles";
//get HostIPAddresses.xml
XElement GroupNames = XElement.Load(configPath + "\\HostIPAddresses.xml");
bool testIsRunning = false;
//Start the initial GUI
public NetTest()
{
InitializeComponent();
//populate the dropdown
foreach (var groupElement in GroupNames.Elements("Group"))
{
if (groupElement.Attribute("name").Value != null)
{
dropDown.Items.Add(groupElement.Attribute("name").Value);
}
}
}
//********** Button Actions **********
//run the network test
public void BtnStart_Click(object sender, RoutedEventArgs e)
{
//clear richtextbox first
resultsBox.Document.Blocks.Clear();
//first, make sure the user has selected a group
if (dropDown.SelectedItem != null)
{
string HostSelection = (string)dropDown.SelectedItem;
//create a list of IP addresses based on group chosen
List<string> pingList = new List<string>();
foreach (var groupElement in GroupNames.Elements("Group"))
{
//resultsBox.AppendText(groupElement.Attribute("name").Value);
if (groupElement.Attribute("name").Value == HostSelection)
{
// resultsBox.AppendText(groupElement);
foreach (var hostSystem in groupElement.Elements("HostSystem"))
{
if (hostSystem.Attribute("IPAddress").Value != null)
{
pingList.Add(hostSystem.Attribute("IPAddress").Value);
}
}
}
}
/*Should this be outside BtnStart_Click?
currently cannot because it uses pingList*/
void RunPingTest(object? obj)
{
//do the ping test
resultsBox.AppendText("Doing the ping test \r"); //Just kidding, it isn't
foreach (var pingElement in pingList)
{
//this needs to be able to write to resultsBox as the ping happens (richtextbox)
Ping myPing = new Ping();
resultsBox.AppendText("Pinging " + pingElement + ". . . ");
PingReply reply = myPing.Send(pingElement, 2000); //ip and timeout
string results = reply.Status.ToString();
resultsBox.AppendText(results + "\r");
}
resultsBox.AppendText("\r\n Finished ping testing");
}
}
}
//other buttons here
}
}
xml for IPAddresses:
<?xml version="1.0"?>
<Systems>
<Group name="Group1">
<HostSystem name="System1" IPAddress="1.1.1.1"> </HostSystem>
<HostSystem name="System2" IPAddress="2.2.2.2"> </HostSystem>
<HostSystem name="System3" IPAddress="3.3.3.3"> </HostSystem>
</Group>
<Group name="Group2">
<HostSystem name="System4" IPAddress="4.4.4.4"> </HostSystem>
<HostSystem name="System5" IPAddress="5.5.5.5"> </HostSystem>
<HostSystem name="System6" IPAddress="6.6.6.6"> </HostSystem>
</Group>
Well, in the code you posted you never call the method RunPingTest
which is probably why your RichTextBox
never gets updated.
However, if you want to do this asynchronously, the Ping
class
contains a method called SendPingAsync
with a number of overloads that returns a Task<PingReply>
and allows you to just use async/await
. No need to introduce any kind of Thread
or BackgroundWorker
or manually deal with the Dispatcher
.
private async void BtnStart_Click(object sender, RoutedEventArgs e)
{
var pingList = new List<string>
{
"1.1.1.1",
"2.2.2.2",
"3.3.3.3",
"4.4.4.4"
};
resultsBox.Document.Blocks.Clear();
resultsBox.AppendText("Doing the ping test\r");
using (var ping = new Ping())
{
foreach (var ip in pingList)
{
resultsBox.AppendText($"Pinging {ip} . . . ");
PingReply reply = await ping.SendPingAsync(ip, 2000);
resultsBox.AppendText($"{reply.Status} \r");
}
}
}
The important thing is to make your event handler async void
and to await
the calls to SendPingAsync
. You might also want to consider disabling 'BtnStart' until the method completes, otherwise users will be able to click on it while a ping test is still running.