Исключение InvalidOperaionException при работе с ComboBox (C# WinForms)

В моей программе на форме присутствуют три элемента ComboBox. Я хочу передать значения из них в код:

string vhost = VirtualHostPick.GetItemText(VirtualHostPick.SelectedItem);
string exchange_name = ExchangePicker.GetItemText(ExchangePicker.SelectedItem);
string routingKey = RoutingKeyPicker.GetItemText(RoutingKeyPicker.SelectedItem);

Но во время работы программы возникает исключение InvalidOperationException:

System.InvalidOperationException: "Недопустимая операция в нескольких потоках: попытка доступа к элементу управления 'VirtualHostPick' не из того потока, в котором он был создан."

Я пробовал решить проблему через отдельный метод с Invoke, но не смог придумать, как возвращать string, а не void:

private void VhostPicker(object selectedItem) 
{
    if (InvokeRequired)
        Invoke((Action<object>)VhostPicker, selectedItem);
    else
        VirtualHostPick.GetItemText(selectedItem);
}

Ответы (1 шт):

Автор решения: aepot

Для .NET 6 и новее - Invoke<T>(Func<T>)

private string VhostPicker(object selectedItem) 
{
    if (InvokeRequired)
        return Invoke(() => VhostPicker(selectedItem));
    else
        return VirtualHostPick.GetItemText(selectedItem);
}

Или для старых версий WinForms

private string VhostPicker(object selectedItem) 
{
    if (InvokeRequired)
    {
        string result = null;
        Invoke((Action)(() => result = VhostPicker(selectedItem)));
        return result;
    }
    else
        return VirtualHostPick.GetItemText(selectedItem);
}

Чтобы не схватывать исключение с VirtualHostPick.SelectedItem, можно сделать более универсальный метод.

private T InvokeFunction<T>(Func<T> func) 
{
    if (InvokeRequired)
        return Invoke(func);
    else
        return func();
}

Для старых форм

private T InvokeFunction<T>(Func<T> func) 
{
    if (InvokeRequired)
    {
        T result = default(T);
        Invoke((Action)(() => result = func()));
        return result;
    }
    else
        return func();
}

И вызывать вот так

string vhost = InvokeFunction(() => VirtualHostPick.GetItemText(VirtualHostPick.SelectedItem));
→ Ссылка