WinForms: рекурсивный контроль доступа в управлении [дубликат]

Вот более краткий ответ для людей, которые ищут быструю ссылку, а также некоторые примеры с использованием обещаний и асинхронных / ожидающих.

Начните с наивного подхода (который не работает) для функция, вызывающая асинхронный метод (в данном случае setTimeout) и возвращает сообщение:

function getMessage() {
  var outerScopeVar;
  setTimeout(function() {
    outerScopeVar = 'Hello asynchronous world!';
  }, 0);
  return outerScopeVar;
}
console.log(getMessage());

undefined регистрируется в этом случае, поскольку getMessage возвращается до вызова setTimeout и обновления outerScopeVar.

Двумя основными способами его решения являются обратные вызовы и обещания:

Обратные вызовы

Здесь изменение состоит в том, что getMessage принимает параметр callback, который будет вызываться для доставки результатов обратно к вызывающему коду после его доступности.

function getMessage(callback) {
  setTimeout(function() {
    callback('Hello asynchronous world!');
  }, 0);
}
getMessage(function(message) {
  console.log(message);
});

Обещает

Обещания предоставляют альтернативу который является более гибким, чем обратные вызовы, потому что их можно комбинировать естественно для координации нескольких асинхронных операций. Стандартная реализация Promises / A + изначально представлена ​​в node.js (0.12+) и многих текущих браузерах, но также реализована в таких библиотеках, как Bluebird и Q .

function getMessage() {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      resolve('Hello asynchronous world!');
    }, 0);
  });
}

getMessage().then(function(message) {
  console.log(message);  
});

jQuery Отложенные

jQuery предоставляет функциональные возможности, аналогичные обещаниям с его отсрочкой.

function getMessage() {
  var deferred = $.Deferred();
  setTimeout(function() {
    deferred.resolve('Hello asynchronous world!');
  }, 0);
  return deferred.promise();
}

getMessage().done(function(message) {
  console.log(message);  
});

async / await

Если ваша среда JavaScript включает поддержку async и await (например, Node.js 7.6+), то вы может использовать обещания синхронно в пределах функций async:

function getMessage () {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            resolve('Hello asynchronous world!');
        }, 0);
    });
}

async function main() {
    let message = await getMessage();
    console.log(message);
}

main();

1
задан jww 7 September 2014 в 22:15
поделиться

6 ответов

Когда вы вызываете Control.Controls, он возвращает только элементы управления на самом удаленном уровне. Он не будет рекурсивно спускаться в любые элементы управления контейнерами, в которых содержатся другие элементы управления.

Если ваши элементы управления находятся в другом контейнере, вам нужно будет использовать свойство .Controls этого контейнера.

В качестве альтернативы вы можете обобщить его, написав метод рекурсивного возврата всех элементов управления из родительского и всех его дочерних элементов, например:

public IEnumerable<Control> AllControls(Control container)
{
    foreach (Control control in container.Controls)
    {
        yield return control;

        foreach (var innerControl in AllControls(control))
            yield return innerControl;
    }
}

Затем вы можете использовать это вместо Control.Controls следующим образом:

private void test() // Assuming this is a member of a Form other class derived from Control
{
    var textboxesWithFilledTag = 
        AllControls(this).OfType<TextBox>()
        .Where(tb => (string) tb.Tag == "Filled");

    foreach (var textbox in textboxesWithFilledTag)
        Debug.WriteLine(textbox.Text);
}

Как говорится в комментарии, я предполагаю, что метод test() является членом вашей Формы или другого класса, производного от Control. Если это не так, вам нужно передать ему родительский элемент управления:

private void test(Control container)
{
    var textboxesWithFilledTag = 
        AllControls(container).OfType<TextBox>()
        .Where(tb => (string) tb.Tag == "Filled");

    foreach (var textbox in textboxesWithFilledTag)
        Debug.WriteLine(textbox.Text);
}

Следующий метод имеет идентичные результаты с приведенным выше, для справки (и более читаемым IMHO):

private void test(Control container)
{
    foreach (var textbox in AllControls(container).OfType<TextBox>())
        if ((string)textbox.Tag == "Filled")
            Debug.WriteLine(textbox.Text);
}

Для вашего кода обработчик щелчка на кнопке может выглядеть примерно так:

void button1_Click(object sender, EventArgs e)
{
    foreach (var c in AllControls(this).OfType<TextBox>())
    {
        if ((string) c.Tag == "Filled")
        {
            // Here is where you put your code to do something with Textbox 'c'
        }
    }
}

Обратите внимание, что вам также нужен метод AllControls(), конечно.

2
ответ дан Matthew Watson 24 August 2018 в 05:38
поделиться

Вы устанавливаете свойство tag для себя. Это свойство свойства string. Вы можете попробовать следующее:

if (c.Tag == "Filled")
{
    Console.WriteLine(c.Name);
}

, если вы хотите проверить, что текстовое поле не пустое, вы можете просто попробовать следующее:

if (c.Text.Trim().Length == 0)
{
 Console.WriteLine(c.Name);
}
0
ответ дан Arshad 24 August 2018 в 05:38
поделиться

Чтобы получить все элементы управления (а не только прямые дочерние элементы формы), вы можете использовать этот рекурсивный Linq

Func<Control, IEnumerable<Control>> allControls = null;
allControls = c => new Control[] { c }
                   .Concat(c.Controls.Cast<Control>()
                                     .SelectMany(x=>allControls(x)));

. Теперь вы можете фильтровать TextBoxes

var tbs = allControls(this).OfType<TextBox>()
            .Where(t=>(string)t.Tag=="Filled")
            .ToList();
2
ответ дан I4V 24 August 2018 в 05:38
поделиться

Лучше использовать if (c is TextBox). Кроме того, если вы хотите знать, почему ваш код разбивается, используйте try / catch

1
ответ дан JeffRSon 24 August 2018 в 05:38
поделиться

Это может быть проще:

foreach ( TextBox tb in this.Controls.OfType<TextBox>()) 
{
    if ((string)tb.Tag == "Filled")
    // .....
}
3
ответ дан Roger Rowland 24 August 2018 в 05:38
поделиться

Я бы рекомендовал использовать следующий синтаксис:

foreach (Control c in Controls)
    if (c is TextBox)
0
ответ дан Sinatr 24 August 2018 в 05:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: