Ответы пользователя по тегу C#
  • Как сравнить button==button из массива в c#?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Кнопки сравнить просто так не получится, это разные экземпляры и они будут разными, даже если имеют одинаковый набор значений свойств.

    Можно сравнить, например, Text.

    Для этого достаточно сделать переменную на уровне класса, в которую помещать текст первой нажатой кнопки (или ссылку на саму кнопку). Когда будет нажата следующая кнопка, сравнивать её текст с сохраненным значением.

    private Button FirstButton = null;
    
    void S_MouseClick(object sender, MouseEventArgs e)
    {
      var button = (sender as Button);
    
      if (this.FirstButton == null)
      {
        // это первая кнопка в текущей сессии,
        // запоминаем ссылку на кнопку
        this.FirstButton = button;
      }
      else
      {
        // это вторая кнопка в текущей сессии
        // сравниваем текст с первой
        if (this.FirstButton.Text == button.Text)
        {
          Console.WriteLine("Текст совпадает!");
          // меняем свойства кнопок
          this.FirstButton.Text = button.Text = "--";
          this.FirstButton.Enabled = button.Enabled = false;
        }
        else
        {
          Console.WriteLine("Текст не совпадает.");
        }
        // сбрасываем выбор, запуская тем самым новую сессию
        this.FirstButton = null;
      }
    }

    Если понадобится сравнивать более двух кнопок, то придется делать коллекцию.

    Вместо Text можно сравнивать по какому-нибудь другому признаку. У элементов есть свойство Tag, которое можно использовать для хранения любых дополнительных данных.

    Если все будет совсем сложно, то можно расширить Button дополнительными свойствами путем наследования. В таком случае можно будет переопределить Equals и с операторами поиграть.
    Ответ написан
    2 комментария
  • Как определить промежуток времени в (System.Timers.Timer)?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Интервал указывается в миллисекундах. Одна секунда равняется 1 000 миллисекунд.
    Соответственно 25 000 миллисекунд - это 25 секунд.

    // счетчик секунд
    int secondsCounter = 0;
    
    var s500A2 = new System.Timers.Timer();
    
    // интервал - одна секунда
    s500A2.Interval = 1000;
    
    // обработчик истечения интервала
    s500A2.Elapsed += (sender, e) => { // имена sender и e можно поменять, если будут проблемы
      Console.WriteLine("Прошла секунда");
    
      // увеличиваем значение счетчика
      secondsCounter++;
    
      if (secondsCounter >= 10)
      {
        Console.WriteLine("Всего прошло 10 секунд");
        secondsCounter = 0;
      }
    };
    
    s500A2.Start();

    Либо можно установить Interval в значение 10 000, тогда Elapsed будет вызываться каждые 10 секунд.

    Однако с точность все не так просто. Есть незначительная погрешность (в миллисекундах), которая со временем будет расти и в какой-то момент может потеряться секунда. Если точность критична, то лучше использовать меньшие значения.

    Следующий пример демонстрирует погрешность относительно текущего времени:

    // интервал - одна секунда
    s500A2.Interval = 1000;
    
    s500A2.Elapsed += (sender, e) => {
      Console.WriteLine("Прошла секунда: {0:HH:mm:ss.fff}", DateTime.Now);
    };
    
    // На выходе будет примерно это:
    // Прошла секунда: 14:18:20.950
    // Прошла секунда: 14:18:21.964
    // Прошла секунда: 14:18:22.978
    // Прошла секунда: 14:18:23.992 
    // Прошла секунда: 14:18:25.006 // потеряли 14:18:24
    // Прошла секунда: 14:18:26.020
    // Прошла секунда: 14:18:27.034

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

    int lastSecond = 0;
    // интервал - десять миллисекунд
    s500A2.Interval = 10;
    
    s500A2.Elapsed += (sender, e) => {
      int currentSecond = DateTime.Now.Second;
    
      if (currentSecond != lastSecond)
      {
        Console.WriteLine("Прошла секунда: {0:HH:mm:ss.fff}", DateTime.Now);
        lastSecond = currentSecond;
      }
    };
    Ответ написан
    5 комментариев
  • Как найти одинаковые элементы массива объектов (в 1 массиве)?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Собрать коллекцию, примерно так:
    var result = new Dictionary<int, List<object>>();
    
    foreach (var item in arrComputer)
    {
      var c = (Comp)item;
      // проверяем, есть такой год в коллекции или нет
      if (!result.ContainsKey(c.year))
      {
        // такого года еще нет, добавляем
        result.Add(c.year, new List<object> { c });
      }
      else
      {
        // год есть, добавляем запись в него
        result[c.year].Add(c);
      }
    }
    
    // в result будет коллекция: год-компьютеры
    foreach (int year in result.Keys)
    {
      Console.WriteLine
      (
        "В {0} году на Земле вылупилось компьютеров: {1}", 
        year, 
        result[year].Count
      );
    
      if (result[year].Count > 1)
      {
        Console.WriteLine("Да это просто демографический взрыв какой-то!");
        foreach (var item in result[year])
        {
          Console.WriteLine("+ {0}", ((Comp)item).marka);
        }
      }
    }

    Вместо object можно сразу использовать Comp, если использование других типов не предполагается.
    Ответ написан
    Комментировать
  • Как завершить редактирование ячейки DataGrid стрелками на клавиатуре?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Попробуйте обрабатывать событие PreviewKeyDown:
    private void dataGrid_PreviewKeyDown(object sender, KeyEventArgs e)
    {
      if (e.Key == Key.Up || e.Key == Key.Down)
      {
        dataGrid.CancelEdit();
      }
    }

    <DataGrid x:Name="dataGrid" PreviewKeyDown="dataGrid_PreviewKeyDown" ...>
    Ответ написан
  • Как скомпилировать проект на c# в dotnet?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    # клонируем рекурсивно, т.к. там ссылка на другой репозиторий
    git clone --recursive https://github.com/yar229/WebDavMailRuCloud.git
    # тянем последнюю версию nuget, если таковой еще нет
    wget https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -O nuget.exe --no-check-certificate
    # восстанавливаем пакеты nuget
    mono nuget.exe restore WebDavMailRuCloud/WebDAVMailRuCloud.sln
    # пробуем выполнить сборку решения
    MONO_IOMAP=case xbuild WebDavMailRuCloud/WebDAVMailRuCloud.sln

    Если получаем ошибку:

    MailRuCloudApi.cs(1020,51): error CS0019: Operator `>' cannot be applied to operands of type `long' and `object'

    Вот эта строчка:
    https://github.com/yar229/Mail.Ru-.net-cloud-clien...

    Открываем локальную копию указанного файла и добавляем скобки (кстати, можно сделать pull request):

    return (fileStream.Length > 0) as object;

    Пробуем собрать еще раз:

    MONO_IOMAP=case xbuild WebDavMailRuCloud/WebDAVMailRuCloud.sln

    В случае успеха получаем что-то типа этого:

    18 Warning(s)
    0 Error(s)

    Пробуем запустить:

    mono WebDavMailRuCloud/WDMRC.Console/bin/Debug/wdmrc.exe --help

    Получаем:

    log4net:ERROR Could not create Appender [ColoredConsoleAppender] of type [log4net.Appender.ColoredConsoleAppender]. Reported error follows.
    System.EntryPointNotFoundException: GetConsoleOutputCP
    ...

    В суть не буду вникать, но очевидно, что какая-то проблема с log4net. Можно просто отключить log4net. Откройте файл конфигурации:

    nano WebDavMailRuCloud/WDMRC.Console/bin/Debug/wdmrc.exe.config

    Замените ветку <log4net>...</log4net> на:

    <log4net threshold="OFF" />

    У меня с параметром --help запустилось нормально, дальше не стал смотреть:

    mono WebDavMailRuCloud/WDMRC.Console/bin/Debug/wdmrc.exe --help
    WebDAVCloudMailRu 1.0.0.0
    yar229@yandex.ru
    
      -p, --port        Required. WebDAV server port
    
      -l, --login       Required. Login to Mail.ru Cloud
    
      -s, --password    Required. Password to Mail.ru Cloud
    
      --maxthreads      (Default: 5) Maximum concurrent connections to cloud.mail.ru
    
      --user-agent      "browser" user-agent
    
      --help            Display this help screen.
    
      --version         Display version information.

    UPD: Попробовал запустить с login и password, получил ошибку вида:

    Unhandled Exception:
    System.InvalidOperationException: Property 'enabled' not found in configuration element
    at System.Configuration.ConfigurationElement.get_Item (System.String property_name) <0xb50f5608 + 0x000a7> in :0
    at System.Configuration.ConfigurationElement.get_Item (System.Configuration.ConfigurationProperty property) <0xb50f55d8 + 0x0001f> in :0
    at System.Net.Configuration.DefaultProxySection.get_Enabled () <0xb506a270 + 0x0001b> in :0
    at MailRuCloudApi.Account.Login () <0xb50699f8 + 0x000af> in :0

    Проблема в получении значений прокси-сервера по умолчанию:
    https://github.com/yar229/Mail.Ru-.net-cloud-clien...

    По идее, прокси-сервер по умолчанию можно отключить/настроить в файле конфигурации приложения, добавив в секцию configuration нечто вроде этого:

    <system.net>
      <defaultProxy enabled="false" useDefaultCredentials="false">
        <proxy/>
        <bypasslist/>
        <module/>
      </defaultProxy>
    </system.net>

    Но у меня почему-то это не сработало. Можно удалить указанные строки (if (new DefaultProxySection().Enabled)...) из файла и собрать решение заново:

    nano WebDavMailRuCloud/MailRuNetCloudClient/MailRuCloudApi/Account.cs
    MONO_IOMAP=case xbuild WebDavMailRuCloud/WebDAVMailRuCloud.sln

    У меня с login и password запустилось:

    WebDAV server running. Press 'x' to quit.

    Если потребуется журналирование (log4net) и прокси, то придется разбираться в этих проблемах более углубленно.

    -----------------------------------------------------------------------------

    В проверке принимали участие:
    • Debian v8.1
    • Mono v4.2.3
    • XBuild Engine v12.0
    • Nuget v3.4.4.1321
    Ответ написан
    2 комментария
  • Как с dll запустить диалоговое окно с браузером и получить результат?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Слишком общий вопрос :-)

    Манипулирование живым браузером - плохая идея, это ненадежно и можно споткнуться на всяких антивирусах. На уровне приложения лучше использовать компонент WebBrowser (по сути - Internet Explorer) или альтернативу (это будет минимум мегабайт 20-40 к размеру программы).

    Давным-давно писал статейку по работе с ВКонтакте, не знаю насколько сейчас актуальна и работает ли:
    Разработка desktop-приложения для «ВКонтакте» на C#

    Есть также библиотека с открытым исходным кодом:
    https://github.com/alekseynemiro/nemiro.oauth.dll

    Попробуйте выдернуть клиент для ВКонтакте (включая зависимости) или внедрить целиком, если не устроит присутствие лишней библиотеки в проекте (даже двух, если используется Windows Forms) :-)

    Готовые формы для Windows Forms:
    https://github.com/alekseynemiro/Nemiro.OAuth.Logi...

    Статья по использованию библиотеки: Авторизация по протоколу OAuth в проектах .NET Fra...

    Демонстрация ASP.NET MVC: demo-oauth.nemiro.net

    Получение адреса для авторизации делается примерно так:

    string applicationId = "идентификатор вашего приложения";
    string scope = "status,email";
    string authorizeUrl = "https://oauth.vk.com/authorize";
    authorizeUrl += String.Format("?client_id={0}&response_type=code", applicationId);
    // authorizeUrl += String.Format("&state={0}", "все что вам нужно");
    authorizeUrl += String.Format("&scope={0}", scope);

    Адрес authorizeUrl можно открыть в WebBrowser: webBrowser1.Navigate(authorizeUrl). В обработчике события DocumentCompleted можно получать код авторизации или маркер доступа (смотря какой response_type используется).

    На примере обработчика по умолчанию из моей библиотеки:

    protected internal void DefaultCallback(object sender, WebBrowserCallbackEventArgs e)
    {
      // ожидаем, когда будет получен результат
      if (e.Url.Query.IndexOf("code=") != -1 || e.Url.Query.IndexOf("oauth_verifier=") != -1)
      {
        // результат получен, извлекаем код авторизации
        // из строка параметров запроса
        // e.Url.Query // <= строка параметров запроса
        // либо oauth_verifier, либо code - точно уже не помню
      }
    }

    Но вообще, все может быть чуть сложнее. Нужно учитывать версию IE и, в идеале, использовать самую свежую (см. SetIEVersion). Вероятность появления ошибок в клиентском коде и адекватная реакция на них. Всех подводных камней сейчас и не припомню.

    Если response_type=code, как в указанном выше коде получения адрес авторизации, то конвертировать код авторизации в маркер доступа можно выполнив запрос (скорее всего POST) к странице https://oauth.vk.com/access_token, на которую нужно передать следующие параметры:

    code=полученный код
    client_id=идентификатор приложения
    client_secret=секретный ключ
    grant_type=authorization_code

    Запрос маркера доступа нужно делать без WebBrowser, а например, с помощью WebClient или HttpClient.
    Если все правильно, сервер вернет access_token.
    Ответ написан
    Комментировать
  • Построение странного запроса в linq to db, как исправить?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    По умолчанию чувствительности к регистру быть не должно и если это не было сделано намерено, то нет необходимости делать ToUpper().
    return await dbSet.FirstOrDefaultAsync(u => u.Email == email);
    Ответ написан
  • Как запретить десериализацию объекта с Json.Net?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Попробуйте Required:

    [JsonProperty(PropertyName = "start", Required = Required.Always)]
    public long? start { get; set; }

    Либо проверяйте значение ключевого поля (или полей), если значение null, считайте что десериализация не дала результатов (outRequest = null).

    Ключевое поле - это поле, которое присутствует всегда и не может иметь значение null.

    Если зацепиться не за что, то можно и по всем пройтись:

    if (!outRequest.start.HasValue && !outRequest.finish.HasValue && !outRequest.template.HasValue)
    {
      outRequest = null;
    }

    По идее, если у ключевых полей использовать тип отличный от Nullable (например, long вместо long?), либо совместно с атрибутом Required.Always, то при десериализации должно выбрасываться исключение, если нужные поля в данных не будут найдены. Исключение можно перехватывать (try-catch) и таким образом понять, что входящие данные неверные.

    Еще можно сделать пользовательский конвертер - JsonConverter, но это может быть сложным решением, в плане «энергозатрат» :-)

    [JsonConverter(typeof(ORequestConverter))]
    public sealed class ORequest
    {
      [JsonProperty("start")]
      public long? start { get; set; }
    
      [JsonProperty("finish")]
      public long? finish { get; set; }
    
      [JsonProperty("template")]
      public int? template { get; set; }
    }
    
    public class ORequestConverter : JsonConverter
    {
    
      public override bool CanWrite
      {
        get
        {
          return false;
        }
      }
    
      public override object ReadJson
      (
        JsonReader reader, 
        Type objectType, 
        object existingValue, JsonSerializer serializer
      )
      {
        if (reader.TokenType == JsonToken.Null)
        {
          return null;
        }
    
        var target = (ORequest)Activator.CreateInstance(objectType);
    
        serializer.Populate(reader, target);
    
        if (!target.start.HasValue && !target.finish.HasValue && !target.template.HasValue)
        {
          // необходимые поля не найдены или имеют значение null
          // возвращаем null
          return null;
        }
    
        return target;
      }
    
      public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
      {
        throw new NotImplementedException();
      }
    
      public override bool CanConvert(Type objectType)
      {
        throw new NotImplementedException();
      }
    
    }
    Ответ написан
    Комментировать
  • Почему сохраняются старые значения в потоке?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Вы используете экземпляр BackgroundWorker, который живет на уровне класса.

    Всякий раз, когда вы нажимаете на кнопку, к экземпляру BackgroundWorker подключается новый обработчик события DoWork. Т.е. нажмете один раз на кнопку, будет один обработчик, нажмете два раза, то уже будет два обработчика, нажмете десять раз - будет десять обработчиков и все они будут работать. Это будет хорошо видно, когда обработчиков будет много, из-за рассинхронизации значение i будет меняться быстрее, хаотичней. Так что вывод переменной на уровень класса и присваивание ей нулевого значения - это не решение проблемы.

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

    private void button2_Click(object sender, EventArgs e) // запуск таймера класс BackgroundWorker
    {
      // создаем новый экземпляр BackgroundWorker
      bw = new BackgroundWorker();
      // остальной код
      key = true;
      // ...
    }

    Либо отключать обработчик, но для этого придется иметь ссылку на него (bw.DoWork -= ссылкаНаОбработчик). При таком варианте проще отказаться от анонимных функций и использовать обычные.

    Если интересно, то количество обработчиков события DoWork у экземпляра BackgroundWorker можно проверить так:

    EventHandlerList events = (EventHandlerList)typeof(Component).GetProperty
    (
      "Events", 
      BindingFlags.NonPublic | BindingFlags.Instance
    ).GetValue(bw, null);
    
    var k = typeof(BackgroundWorker).GetField
    (
      "doWorkKey", 
      BindingFlags.NonPublic | BindingFlags.Static
    ).GetValue(null);
    
    var handlers = events[k];
    
    Console.WriteLine
    (
      "Обработчиков {0}", 
      handlers.GetInvocationList().Length
    );
    Ответ написан
    1 комментарий
  • Почему нет доступа к форме при использовании потока?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Как изменить textbox формы из другого класса?

    bw.DoWork += (o, eo) =>
    {
      for (;;)
      {
        i = time.Time(i);
        Thread.Sleep(1000);
        // https://msdn.microsoft.com/ru-ru/library/zyzhdc6b.aspx
        Invoke(new Action(() => {
          Text = "Таймер. Время: " + i;
        }));
      }
    };
    Ответ написан
    3 комментария
  • Как учитывать часовой пояс пользователя на asp.net сайте?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    DateTime.Now - просто сделать поиск и замену (по файлам). Можно ограничить поиск типами файлов (.cs, .cshtml, .aspx, .ascx), чтобы не зацепить лишнего.

    Что касается вывода времени в нужном часовом поясе, то возможно стоит это делать с помощью JavaScript. Для каждого отдельного пользователя выводить время со стороны сервера будет дорогим удовольствием (в плане затраты ресурсов).

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

    В представлениях делать вывод даты и времени можно в какие-нибудь span-ы, например:
    <p>
      <span class="datetime">
        @Html.GetDateTime(DateTime.Now)
      </span>
    </p>
    <p>
      <span class="datetime" data-timestamp="@Html.GetTimestamp()">
        @Html.GetDateTime(DateTime.Now)
      </span>
    </p>
    
    <p>Метод <b>Html.GetDateTime</b> вполне может возвращать дату в тегах, тогда будет:</p>
    <p>@Html.GetDateTime(DateTime.Now)</p>
    <p>а на выходе может быть:<p>
    <p>
      <span class="datetime" data-timestamp="123">
        15.11.2016 21:11:21
      </span>
    </p>
    <p>
      <b>timestamp</b> - позволит обойти проблему с форматированием, 
      при расчете времени на стороне клиента.
    </p>

    С UTC будет проще работать, но вполне можно конвертировать время из времени сервера, главное не затеряться во времени, особенно между зимой и летом :-)

    С представлениями придется повозиться. Хотя если у вас имена полей, содержащие дату и время, более ли менее унифицированы, то проблем особых быть не должно.

    Поиск и замену можно выполнять с использованием регулярных выражений, в тех местах где нужно делать вывод через вспомогательную функцию, но это может оказаться сложным решением.

    В коде (C#) с заменой особых проблем быть не должно.

    Замену в базе, при необходимости, можно автоматизировать. Единственное, если используется где-то GETDATE(), могут быть проблемы, тоже придется учитывать.

    Попробуйте для начала с представлениями (выводом) разобраться.
    А стоит проводить (в коде и базе) крупномасштабную замену времени сервера на UTC - время покажет.
    Ответ написан
    Комментировать
  • Как правильно отправлять soap-запросы, используя C# (в особенности - на сервера AdvCash)?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Обычно это так делается:

    cc28a9b90a31407aab7b6af750336a88.png

    Ссылка на службу: https://wallet.advcash.com:8443/wsm/merchantWebSer...

    3b825f13a4d64c25ae507b698550b686.png
    А дальше дело кода :-)

    // ServiceReference1 - см. на картинке выше, можно указать любое имя
    var client = new ServiceReference1.MerchantWebServiceClient();
    // и смотрите какие есть методы в client и что требуется для их использования
    // могут быть и другие подводные камни и особенности,
    // но свой код взаимодействия писать не придется
    // для большинства случаев достаточно будет автоматически созданных классов
    Ответ написан
    5 комментариев
  • Как изменить textbox формы из другого класса?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    private void Cl_SendMessage(string mess)
    {
        // если метод вызывается не из потока, к которому привязана форма
        // https://msdn.microsoft.com/ru-ru/library/system.windows.forms.control.invokerequired.aspx
        if (this.InvokeRequired)
        {
          // делаем вызов из потока формы
          // https://msdn.microsoft.com/ru-ru/library/zyzhdc6b.aspx
          this.Invoke(new Action<string>(this.Cl_SendMessage), mess);
          // уходим из этого метода
          return;
          // или можно в условии сделать else
          // кому как больше нравится
        }
        // else {
    
        // код находящийся здесь будет выполняться только если 
        // текущий поток - это поток в котором находится форма
        chatLogTB.Text += mess;
    
       // }
    }
    Ответ написан
    8 комментариев
  • C# как реализовать Контрол в WPF?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Чтобы написать элемент управления WPF в виде библиотеки на C# необходимо запустить Visual Studio (2015), выбрать меню Файл => Создать => Проект. В появившемся окне, перейти в раздел Шаблоны => Visual C# => Windows => Классическое приложение. В списке шаблонов найти и выбрать Библиотека пользовательских элементов управления WPF. Ввести название проекта и нажать на кнопку Ok. Написать какой-нибудь код :-)

    84553e4c4eea431dbeec25e86d0115fe.png
    Что касается написания кода, то возможно следующие ссылки вам помогут:
    Руководство по программированию в C#
    + Классы и структуры
    + Свойства

    Информацию по работе с Windows Presentation Foundation можно найти по следующей ссылке:
    https://msdn.microsoft.com/ru-ru/library/ms754130(...
    Ответ написан
    Комментировать
  • Как выделить строку таблицы при нажатии пр. клавиши мыши?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Добавить обработчик MouseDown:

    <DataGrid x:Name="dataGrid" MouseDown="dataGrid_MouseDown"/>

    Код обработчика примерно такой:

    private void dataGrid_MouseDown(object sender, MouseButtonEventArgs e)
    {
      if (e.RightButton == MouseButtonState.Pressed)
      {
        var row = DataGridRow.GetRowContainingElement(e.OriginalSource as FrameworkElement);
        if (row != null)
        {
          // dataGrid.SelectedIndex = row.GetIndex();
          dataGrid.SelectedItem = row;
        }
      }
    }
    Ответ написан
    Комментировать
  • Как сделать все ячейки равных размеров в tableLayoutPanel WinForms?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Указать размер в процентах, если размер должен изменяться в зависимости от размера формы.
    Либо указать точный размер в пикселях.

    Если требуются разные размеры для разных колонок/строк, то этого можно достичь с использованием вложенных TableLayoutPanel.

    Для выравнивания можно использовать колонки/строки с размером 100%. Примерно как в HTML.

    Вот пример формы для программного генерирования TableLayoutPanel с добавлением колонок и строк одинакового размера (этот код можно просто вставить в Form1):
    public partial class Form1 : Form
    {
    
      private Panel TableContainer = new Panel();
      private NumericUpDown Columns = new NumericUpDown();
      private NumericUpDown Rows = new NumericUpDown();
    
      public Form1()
      {
        InitializeComponent();
    
        this.Width = 420;
          
        var flowLayoutPanel = new FlowLayoutPanel();
        var LabelColumns = new Label();
        var LabelRows = new Label();
        var Create = new Button();
    
        // панель для редактора таблицы
        flowLayoutPanel.SuspendLayout();
        flowLayoutPanel.AutoSize = true;
        flowLayoutPanel.Controls.Add(LabelColumns);
        flowLayoutPanel.Controls.Add(Columns);
        flowLayoutPanel.Controls.Add(LabelRows);
        flowLayoutPanel.Controls.Add(Rows);
        flowLayoutPanel.Controls.Add(Create);
        flowLayoutPanel.Dock = DockStyle.Top;
        flowLayoutPanel.Location = new Point(0, 0);
    
        LabelColumns.AutoSize = true;
        LabelColumns.Dock = DockStyle.Fill;
        LabelColumns.ImageAlign = ContentAlignment.MiddleLeft;
        LabelColumns.Location = new Point(3, 0);
        LabelColumns.Size = new System.Drawing.Size(53, 29);
        LabelColumns.Text = "Колонок:";
        LabelColumns.TextAlign = ContentAlignment.MiddleLeft;
    
        LabelRows.AutoSize = true;
        LabelRows.Dock = DockStyle.Fill;
        LabelRows.Location = new Point(117, 0);
        LabelRows.Size = new Size(40, 29);
        LabelRows.Text = "Строк:";
        LabelRows.TextAlign = ContentAlignment.MiddleLeft;
    
        Columns.Dock = DockStyle.Fill;
        Columns.Location = new Point(62, 3);
        Columns.Minimum = 1;
        Columns.Size = new System.Drawing.Size(49, 20);
        Columns.Value = 3;
    
        Rows.Dock = DockStyle.Fill;
        Rows.Location = new Point(163, 3);
        Rows.Minimum = 1;
        Rows.Size = new Size(49, 20);
        Rows.Value = 2;
    
        Create.AutoSize = true;
        Create.Dock = DockStyle.Left;
        Create.Location = new Point(218, 3);
        Create.Size = new Size(75, 23);
        Create.Text = "Создать";
        Create.UseVisualStyleBackColor = true;
        Create.Click += new System.EventHandler(CreateTable);
    
        // контейнер для вывода готовой таблицы
        TableContainer.Dock = DockStyle.Fill;
          
        // добавляем необходимые элементы на форму
        this.Controls.Add(flowLayoutPanel);
        this.Controls.Add(TableContainer);
    
        this.Controls.SetChildIndex(flowLayoutPanel, 1);
        this.Controls.SetChildIndex(TableContainer, 0);
      }
    
      private void CreateTable(object sender, EventArgs e)
      {
        // удаляем предыдущую таблицу
        TableContainer.Controls.Clear();
    
        // создаем новую
        var tableLayoutPanel = new TableLayoutPanel();
        tableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
        tableLayoutPanel.Location = new System.Drawing.Point(0, 0);
        tableLayoutPanel.Visible = true;
    
        tableLayoutPanel.ColumnCount = Convert.ToInt32(Columns.Value);
        tableLayoutPanel.RowCount = Convert.ToInt32(Rows.Value);
    
        // генератор случайных чисел для раскраски панелей (чтобы было видно)
        var rnd = new Random(DateTime.Now.Millisecond);
    
        // определяем размер одной колонки и строки, в процентах
        int width = 100 / tableLayoutPanel.ColumnCount;
        int height = 100 / tableLayoutPanel.RowCount;
    
        this.Text = String.Format("{0}x{1}", width, height);
    
        // добавляем колонки и строки
        for (int col = 0; col < tableLayoutPanel.ColumnCount; col++)
        {
          // добавляем колонку
          tableLayoutPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, width));
    
          for (int row = 0; row < tableLayoutPanel.RowCount; row++)
          {
            // добавляем строку
            if (col == 0) 
            {
              tableLayoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, height));
            }
    
            // добавляем цветную панель, чтобы было видно ячейку в таблице
            var panel = new Panel();
            panel.BackColor = Color.FromArgb(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
            panel.Dock = DockStyle.Fill;
            tableLayoutPanel.Controls.Add(panel, col, row);
          }
        }
    
        // добавляем таблицу в контейнер
        TableContainer.Controls.Add(tableLayoutPanel);
      }
    
    
    }

    2ff063c8d06f474da07fa7df6c515be8.gif
    Ответ написан
  • Заняты ли потоки при использовании async/await?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    https://msdn.microsoft.com/ru-ru/library/hh156528.aspx
    Выражение await не блокирует поток, в котором оно выполняется. Вместо этого оно указывает компилятору объявить оставшуюся часть асинхронного метода как продолжение ожидаемой задачи. Управление затем возвращается методу, вызвавшему асинхронный метод. Когда задача завершается, она вызывает свое продолжение и возобновляет выполнение асинхронного метода с того места, где она была прервана.

    Метод Task.Run ставит в очередь заданную задачу для запуска в пуле потоков.

    Т.е. в первом случае, у вас просто будет добавлена задача в очередь на выполнение и продолжено выполнение программы (без ожидания, программа может завершить работу раньше, чем созданная задача). А во втором, текущий поток будет ожидать завершения выполнения асинхронной операции. Основной поток при использовании await заморожен не будет.

    // без ожидания
    Console.WriteLine(DateTime.Now);
    
    Task.Run(() => { Thread.Sleep(5000); });
    
    // этот код будет выполнен сразу
    Console.WriteLine(DateTime.Now);
    
    // ожидание с await
    Console.WriteLine(DateTime.Now);
    
    await Task.Run(() => { Thread.Sleep(5000); });
    
    // этот код будет выполнен, только после завершения выполнения задачи
    // текущий (вызывающий) поток не будет приостановлен
    // (например, в Windows Form это будет хорошо видно)
    Console.WriteLine(DateTime.Now);

    Если сделать задачу и вызвать метод Wait, то основной поток будет приостановлен, пока не завершится задача.

    Console.WriteLine(DateTime.Now);
    
    var t = Task.Run(() => { Thread.Sleep(5000); });
    t.Wait(); // ожидание выполнения задачи
    
    // этот код будет выполнен, только после завершения выполнения задачи
    // текущий (вызывающий) поток будет блокирован
    // (например, в Windows Form это будет хорошо видно)
    Console.WriteLine(DateTime.Now);

    74ad1b048cee439faab48113a61a7ec7.gif
    Ответ написан
    6 комментариев
  • Как сделать повторение всей программы по запросу (C#)?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Если ответ пользователя будет отличаться от yes, то работа цикла завершится.

    Проверку ответа лучше делать без учета регистра, возможно проблема в этом:

    string answer;
    string yes = "yes"; 
    // из yes лучше сделать константу, чтобы в этом был смысл :)
    // const string yes = "yes"; 
    // либо массив - это позволит проверять разные варианты ответов
    string[] yesVariants = { "yes", "y", "true", "да", "еще бы", "только об этом и мечтаю" };
    // или можно использовать регулярные выражения
    
    do
    {
    
      // любой код здесь
    
      Console.WriteLine("Желаете повторить тест?(Yes/No) ");
      answer = Console.ReadLine();
    
    } while (answer.Equals(yes, StringComparison.OrdinalIgnoreCase));
    // проверка по массиву ответов
    // } while (yesVariants.Contains(answer, StringComparer.OrdinalIgnoreCase));

    56258ea1eb5a4826a05a07bf0beeddd8.gif
    Ответ написан
    Комментировать
  • C#. Как вызвать балуна из трея?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Разместить на форме компонент NotifyIcon, если используется проект Windows Forms.

    Для вывода уведомления, использовать метод NotifyIcon.ShowBalloonTip:

    // необходимо обязательно указать иконку
    // иначе уведомление не будет отображаться
    // можно использовать иконку формы
    notifyIcon1.Icon = this.Icon;
    
    // показываем уведомление
    notifyIcon1.ShowBalloonTip(
      10000, 
      "Новый ответ", 
      "Поступил новый ответ на ваш вопрос на Toster.ru",
      ToolTipIcon.Info
    );
    
    // или с текстом по умолчанию (см. BalloonTipTitle, BalloonTipText)
    // notifyIcon1.ShowBalloonTip(10000);

    Окно программы может находиться в любом состоянии.

    В проектах отличных от Windows Forms можно подключить сборку System.Windows.Forms, но лучше разобраться, может быть есть более правильно решение. Например, для консольных приложений такое решение вполне годится.
    Ответ написан
    3 комментария
  • Почему не срабатывает маршрут?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Проект точно был компилирован? Ошибок в процессе не возникало?

    Судя по маршруту, страница должна открываться по адресу:

    http://localhost:11733/meropriyatia

    Первый параметр - это имя сопоставляемого маршрута. Затем идет шаблон и значения по умолчанию.

    https://msdn.microsoft.com/ru-ru/library/dd504958(...

    Для адреса:

    http://localhost:11733/meropriyatiavnikolaeve

    маршрут должен быть таким:

    routes.MapRoute(
      "meropriyatiavnikolaeve", // имя
      "meropriyatiavnikolaeve", // шаблон
      new { controller = "Home", action = "meropriyatiavnikolaeve" }
    );
    Ответ написан
    5 комментариев