Что вы ДОЛЖНЫ сделать:
Дайте некоторым событиям CmdExec, чтобы они могли быть запущены, когда что-то меняется, например:
public delegate void CombinationFoundDelegate(object sender, int port, string ip);
//note that it would be better to create class derived from EventArgs instead of passing bare arguments in this delegate
public class CmdExec
{
public event CombinationFoundDelegate CombinationFound;
public void Runcmd()
{
//do your work and call event when necessary:
if(CanConnect(ip, port))
CombinationFound?.Invoke(this, port, ip);
}
}
А потом в вашей форме просто послушайте это событие:
cmdobj.CombinationFound += new CombinationFoundCallback;
И все обновления GUI могут происходить при этом обратном вызове. Но вы должны знать, что этот обратный вызов вызывается из другого потока (поскольку CmdExec работает в другом потоке), поэтому вам нужно синхронизировать элементы управления. Вы можете использовать его Dispatcher для этого случая:
Action a = new Action(() => textBox.Text = ip);
if(!Dispatcher.CheckAccess())
Dispatcher.Invoke(a);
else
a();
Итак, обратите внимание, что CheckAccess по какой-то причине скрыт в Intellisense, но он действительно там. CheckAccess просто проверяет, требуется ли синхронизация или нет (в Winforms это будет InvokeRequired).
Далее, если требуется синхронизация, вы можете запустить свой код обновления пользовательского интерфейса в методе вызова диспетчера. Однако, если не требуется синхронизация, вы можете просто запустить свой код пользовательского интерфейса здесь. Вот почему я создал Action для этого, чтобы не дублировать код.
И это все.
(Код написан из головы, поэтому с реальным кодом может быть какая-то разница, но так он должен выглядеть и работать).