• Как сделать вывод key и value из словаря в label?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Не используйте много Label-ов, поместите в список ListBox, и будет вам Щастье. Внешний вид можно настроить - фон BackColor=Control, границы BorderStyle=None. При добавлении записи в список можно преобразовать в строку как надо.
    Ответ написан
  • Как выполнить команду по событию KeyDown для TextBox?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Можно использовать конвертер i:InvokeCommandAction из System.Windows.Interactivity.

    1) Добавить сборку System.Windows.Interactivity с помощью добавления ссылки (она в списке расширений).
    5cf0c5efb3bee685207836.png
    2) Добавить указанную сборку в xaml:
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

    3) Использовать i:EventTrigger и i:InvokeCommandAction:
    <TextBox>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="KeyDown">
                <i:InvokeCommandAction Command="{Binding KeyDownCommand}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>


    Но если вы хотите там вызывать только обработку представления, например, запретить определённые клавиши (то есть, не нужны данные из слоя бизнес-логики), то есть смысл обработку сделать обычным способом - в code-behind (xaml.cs).
    Ответ написан
  • Как заменить элементы в строке C#?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Нужно найти все места, где есть нужное слово.
    string s = "<div>Здесь нам нужно заменить некоторый текст, текст, текст</div>";
    var word = "текст";
    var replaceTo = "ТЕКСТ";
    var words = new List<int>();
    int lastPos = 0;
    int pos = 0;
    do
    {
        pos = s.IndexOf(word, lastPos);
        if (pos >= 0)
        {
            words.Add(pos);
            lastPos = pos + word.Length;
        }
    }
    while (pos >= 0);

    А теперь уже можно сделать с ними всё что угодно - заменять хоть все, хоть по одному.
    Например, замена первого слова
    var result = s.Substring(0, words[0]) + replaceTo + s.Substring(words[0] + word.Length);


    UPD. Можно вместо цикла найти слова с помощью регулярного выражения:
    string s = "<div>Здесь нам нужно заменить некоторый текст, текст, текст</div>";
    var word = "текст";
    var replaceTo = "ТЕКСТ";
    var words = new Regex(word).Matches(s).OfType<Match>().Select(match => match.Index).ToList();
    var result = s.Substring(0, words[0]) + replaceTo + s.Substring(words[0] + word.Length);

    Регекс может работать дольше, чем цикл. Зато код поиска в одну строку!
    Если нужны только слова целиком (не нужно искать "текстовый", например), то алгоритм поиска в цикле усложняется (проверять символ перед и после найденного текста, является ли пробел или пунктуация или ещё что-либо). А в регексе можно написать new Regex(@"\b" + word + @"\b").Matches(s), и регулярное выражение всё сделает само.
    Ответ написан
  • Как сделать,чтобы из textbox можно было стирать введенный текст?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    if (e.KeyChar != (char)Keys.Back && !Regex.Match(Symbol, @"[а-яА-Я]|[a-zA-Z]").Success)

    См. https://toster.ru/q/635163, ответ и комментарии
    Ответ написан
  • C# Индексатор массива объектов - почему так (см.)?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Т.е. data[index] - это у нас объект класса Person

    Не совсем так, это не объект класса Person, а элемент с индексом index в массиве data.
    Индексатор, как и обычные свойства, - это просто сахарный синтаксис для создания методов геттера и сеттера (например, поля).

    Обычное свойство заменяется на методы GetProperty() и SetProperty(<type> value).
    А индексатор заменяется на методы GetIndexator(int index) и SetProperty(int index, <type> value).

    В этих методах можно писать что угодно, в вашем первом варианте вы пишете код доступа к массиву data. Но тип аргументов (index и value) определяются типом индексатора, то есть, вы можете сделать вот так:
    Person[] data;
    
    public string this[int index]
    {
        get
        {
            return data[index]?.Name;
        }
        set
        {
            if (data[index] == null) data[index] = new Person();
            data[index].Name = value;
        }
    }

    Или даже вот так:
    Person[] data;
    
    public Person this[string name]
    {
        get
        {
            return data.FirstOrDefault(p => p.Name == name);
        }
        set
        {
            for (var index = 0; index < data.Length; index++)
            {
                if (person.Name == name)
                {
                    data[index] = value;
                    break;
                }
            }
        }
    }


    То есть, вы не можете сделать так, как вы написали:
    set
    {
         data[index].Name = value;
    }

    потому что типом value будет Person, а не string. (вряд ли, что у вас у свойства Name тоже тип Person, или у вас есть implicit приведение из Person в string).

    А что если там не один только Name будет, ещё поля ??..

    А если у вас там больше свойств, то будьте добры указать это в коде. Точно так же, как и если только один Name, в общем-то.
    Ответ написан
  • Как в этом коде textbox'а сделать,чтобы можно было нажимать клавишу backspace(стереть)?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Использовать KeyDown вместо KeyPress. В KeyDown приходит не печатаемый символ, а код клавиши, в том числе, и управляющие клавиши.

    В вашем случае нужно вызывать оба события. Вначале вызывается KeyDown, и если он не выполнил работу обработки (не указан e.Handled = true), то вызовется обработчик KeyPress.

    Вот примерный код обработки backspace:
    private void textBox1_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Back)
        {
            var selectionStart = textBox1.SelectionStart;
            if (textBox1.SelectionLength > 0)
            {
                textBox1.Text = textBox1.Text.Substring(0, selectionStart) + textBox1.Text.Substring(selectionStart + textBox1.SelectionLength);
                textBox1.SelectionStart = selectionStart;
            }
            else if (selectionStart > 0)
            {
                textBox1.Text = textBox1.Text.Substring(0, selectionStart - 1) + textBox1.Text.Substring(selectionStart);
                textBox1.SelectionStart = selectionStart - 1;
            }
            
            e.Handled = true;
        }
    }
    Ответ написан
  • Как сделать привязку данных по выбору?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Можно попробовать сделать третье свойство, которое в геттере будет получать нужное свойство.
    Ответ написан
  • Как переписать OdbcCommand для массовой вставки?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    А сама база не сможет выполнить запрос вставки по выборке?
    insert into org_iogjt_import_org_gate_job_title (iogjt_jobtitle_id, iogjt_jobtitle_name, iogds_datasource_id) 
        SELECT KSLP, NSLP, 1 FROM SLP WHERE KSLP <> 0 ORDER BY KSLP ASC

    Тогда можно просто выполнить запрос, без цикла и без параметров.
    Ответ написан
  • Переход с C++ на C#?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    С C++ на C# будет гораздо проще, чем было бы наоборот. Не переживайте, у вас получится, через месяц-другой вы будете знать шарп не хуже, чем сейчас плюсы.
    Я рекомендую книгу Троелсена, потому что он подробнейшим образом пишет само описание языка, как писать код на c#, а не учит программированию, насколько я понял, вам это не нужно - алгоритмы, ООП и пр. вы уже знаете.
    Ответ написан
  • Как переместить файлы по заданному условию?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Вторым аргументом указывается не папка, а полный путь к новому файлу, с именем.
    var filename = Path.GetFileName(s);
    File.Move(s, pathA + "\\" + filename);
    Ответ написан
  • Как узнать номер рабочего стола на котором окно?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Если вам нужно именно номер монитора, а не рабочего стола, то вам нужен класс System.Windows.Forms.Screen, который имеет много полезного. Например, Screen.FromControl(), Screen.FromHandle() и пр. Или можно использовать свойство AllScreens, хранящее все мониторы системы.
    Тут есть нюанс - FromHandle берёт не верхнюю левую точку окна, а середину верхней границы окна. Смотрите, что вам нужно.
    И ещё один нюанс - номера мониторов в списке AllScreens могут не совпадать с номером монитора в настройках экранов Windows. Этот виндовский номер монитора я так и не смог найти. Виндовские номера зависят порта видеокарты, к которому они подключены.
    Ответ написан
  • Как загрузить UserControl при клике на элемент ListBox?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Вам нужно просто связать датаконтекст групбоксов с выделенным элементом списка. Всё остальное сделает WPF.
    <ListBox ItemsSource="{Binding DevList}" SelectedItem="{Binding SelectedElement}" />
    <GroupBox DataContext="{Binding SelectedElement}">
    </GroupBox>
    <GroupBox DataContext="{Binding SelectedElement.Subelement1}">
    </GroupBox>


    Чтобы это заработало, то нужно сделать так, чтобы DevList был список объектов, а не строк. Создайте класс (вьюмодель) для ваших элементов списка. В нём можно хранить те свойства, которые вы хотите показывать в групбоксах. То есть, в списке элементов хранятся ваши объекты, а потом они же указываются датаконтекстом в другие контролы (групбоксы).

    В таком случае хорошо сделать во вьюмодели реализацию INotifyPropertyChanged.
    Ответ написан
  • Как остановить поток?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Останавливать поток нужно не убиванием его, а остановкой, желательно - изнутри. А чтобы это можно было сделать нужно передать внутрь токен останова. Сам останов ничего не делает, но он используется внутри тела потока для отмены или аварийного завершения. Этот токен передастся в Worker, в котором нужно будет проверять наличие останова с помощью cancellationToken.IsCancellationRequested. Если в воркере используется цикл, то проверять можно в каждой итерации.

    var formWait = new FormWait(cancellationToken => 
    {
        foreach(var item in _items)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                return;
            }
            item.DoWork();
        }
    });


    wait.Stop = delegate ()
          {
            var source = _tokenSource;
            if (source != null)
            {
                source.Cancel();
                source.Dispose();
                _tokenSource = null;
            }
          };
          myThread.Start();
    
    В форме:
        public Action<CancellationToken> Worker { get; } // добавляем ссылку на токен останова
        public Action Stop { get; set; }
        private CancellationTokenSource _tokenSource;
        public FormWait(Action<CancellationToken> worker)
        {
          InitializeComponent();
          if (worker == null)
            throw new ArgumentNullException();
          Worker = worker;
        }
        protected override void OnLoad(EventArgs e)
        {
          base.OnLoad(e);
          _tokenSource?.Dispose();
          _tokenSource = new CancellationTokenSource();
          var cancellationToken =  = _tokenSource.Token;
          Task.Factory.StartNew(() => Worker(cancellationToken ))
              .ContinueWith(t => { this.Close(); }, TaskScheduler.FromCurrentSynchronizationContext());
        }
    
        private void button1_Click(object sender, EventArgs e)
        {
          Console.WriteLine("Stop");
          Stop();
        }


    Возможно, хорошо сделать таск с возвращаемым результатом, например
    public Func<CancellationToken, bool> Worker { get; }
    
    Task.Factory.StartNew<bool>(() => Worker(cancellationToken))
    .ContinueWith(t => { if (t.Result) this.Close(); }, TaskScheduler.FromCurrentSynchronizationContext());
    Ответ написан
  • Стоит ли читать Троелсена первой книгой?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Книга Троелсена предназначена для профессионалов - программистов, которые хотят изучить язык программирования c#, но сами программировать уже умеют (на другом языке). В этой книге автор мало внимания даёт общим принципам программирования, а всё внимание на то, как их можно использовать в этом языке. Поэтому, эта (очень хорошая) книга не совсем хорошо подойдёт для начала программирования.

    Лично я для новичков предлагаю книгу Герберт Шилдт "Полное руководство C# 4.0". То, что в ней рассказывается старая версия языка - это не плохо (к хорошему быстро привыкаешь). Зато там неплохо рассказывается ООП и базовые концепции программирования.
    Ответ написан
  • В чем разница между переопределением (override), от перекрытия (new), наследуемого метода?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Сначала отвечу на третий вопрос.
    Чтобы понять, в чём разница между переопределением и перекрытием, нужно понять, как программа выбирает, какой вариант метода нужно вызвать, в зависимости от реального типа объекта.
    Компилятор создаёт таблицу всех виртуальных методов класса (в том числе, переопределённых, потому что они тоже виртуальные) и указывает, в каком типе они обозначены. При вызове виртуального метода берётся реальный тип объекта и выбирается нужный вариант реализации метода.
    Если вы указываете в классе-наследнике метод с ключевым словом new, то этот метод перестаёт быть переопределением метода базового класса, и этот метод не добавляется в ту таблицу переопределений. Значит, при вызове метода поиск по типу объекта не найдёт метод, отмеченный new (его же нет в таблице), и попытается найти ближайший метод.
    Обратите внимание, что новый перекрываемый метод тоже может быть указан с ключевым методом virtual, то есть, он тоже попадёт в таблицу виртуальных методов, но как бы в другую таблицу, тем самым создавая новую иерархию реализаций.

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

    Интересно, что в Java все методы виртуальные, и нет возможности перекрыть (но не переопределить) базовый метод, как это можно в c#.

    1. Как можно визуализировать эти подходы?
    Этот вопрос непонятен. В графическом описании иерархии классов? В UML, насколько я помню, виртуальные методы пишутся курсивом. Значит, перекрытый метод будет обычным, не курсивным шрифтом.
    Ответ написан
  • Каков предпочтительный стиль / оформление исходного кода на C#?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    https://docs.microsoft.com/ru-ru/dotnet/csharp/pro...

    Для разделения частей программы на логические блоки используют #region, например, отдельный регион для полей и свойств, для публичных и приватных методов, для конструкторов и обработчиков событий. Я часто делаю регион для указания реализации интерфейса.
    Регионы могут быть внутри регионов. Регионы могут быть внутри класса и внутри кода метода.

    Членам класса (хотя бы публичным) хорошо указывать описания с помощью ///.

    Для примера:
    /// <summary>
    /// Работа с данными
    /// </summary>
    class MyClass : IMyInterface, INotifyPropertyChanged, ICloneable, IDisposable
    {
        #region поля
    
        private readonly Timer _timer;
    
        #endregion 
    
        #region ctor
    
        /// <summary>
        /// Конструктор по умолчанию для работы с данными
        /// </summary>
        public MyClass()
            : this(100500)
        {
        }
        
        /// <summary>
        /// Конструктор для работы с данными
        /// </summary>
        /// <param name="data">Данные</param>
        /// <param name="interval">Интервал таймера</param>
        public MyClass(int data, int interval = 10000)
        {
            _innerData = data;
            _timer = new Timer(interval);
        }
    
        #endregion
    
        #region IMyInterface
    
        private int _innerData;
    
        /// <summary>
        /// Данные
        /// </summary>
        public int Data => _innerData > 0 ? _innerData : 0;
    
        /// <summary>
        /// Выполнить работу
        /// </summary>
        public void DoWork()
        {
                
        }
    
        #endregion
    
        #region IDisposable
    
        public void Dispose()
        {
            _timer.Close();
        }
    
        #endregion
    
        #region ICloneable
    
        object ICloneable.Clone()
        {
            return Clone();
        }
    
        /// <summary>
        /// Клонировать
        /// </summary>
        public MyClass Clone()
        {
            return (MyClass)MemberwiseClone();
        }
    
        #endregion
    
        #region INotifyPropertyChanged
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
    
        #endregion
    }
    Ответ написан
  • Не появляется роутер в списке wifi-устройств. Как исправить?

    lexxpavlov
    @lexxpavlov Автор вопроса
    Программист, преподаватель
    На роутере отключил автонастройку канала и установил самый слабозагруженный канал (нашёл через Wi-fi анализатор), проблема исчезла. Посмотрю, что будет дальше...
    Странно, что на других устройствах тот же канал не приводил проблемам..
    Ответ написан
  • Фокус с делегатом. Как его исполнить?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    namespace DelegateDemo
    {
        public delegate string DemoDel(string s);
       
        class Program
        {  
            static void Main(string[] args)
            {
                ParamMethod(DelLink); // передача ссылки на метод DelLink в ParamMethod
                ParamMethod(s => "(" + s + ")"); // передача анонимной функции в ParamMethod
                Console.ReadKey();
            }
    
            static bool ParamMethod(DemoDel d)
            {
                if(d("что угодно") == "2")
                {
                    Console.WriteLine("делегат вернул 2");
                }
                if(d("5") == "(5)" && d("100500") == "(100500)")
                {
                    Console.WriteLine("делегат обернул число в скобки");
                }
            }
            static string DelLink(string s)
            {
                return "2";
            }
        }
    }
    Ответ написан
  • Как правильно "Собрать решение"?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    А если всю папку C:\Users\pupkin\source\repos\WindowsFormsApp1\WindowsFormsApp1\bin\Debug скопировать и запускать экзешник оттуда?
    При создании готовой программы, лучше выбрать конфигурацию сборки Release, и тогда вся папка C:\Users\pupkin\source\repos\WindowsFormsApp1\WindowsFormsApp1\bin\Release станет дистрибутивом программы, особенно, все dll-ки. Можно попытаться убрать из папки "лишние" файлы, но .exe и .dll все обычно нужны.
    Ответ написан