• Как сделать эффект бесконечного холста?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Можно представить, что у гуглокарт - бесконечный холст. Но это не гигантская картинка, это куча маленьких картинок. Просто потом нужно дорисовать нужные кусочки после скролла.
    1. Делаем двумерный массив картинок (или список списков картинок, чтобы количество можно было легко изменять). Размер картинок может быть любым, как больше размера канваса, так и меньше. Например, размер одной картинки 100x100
    2. Помещаем картинки на холст. Например, размер холста 500x500, то помещаем 25 картинок из массива. Возможно, дополнить по краям ещё по одной картинке, то есть, сделать размер холста 600x600 (или даже 700x700), но показывать только центр
    3. Отключить стандартные скроллбары холста, а скролл сделать самому - перетаскиванием мышки (можно - правой кнопкой, или настроить и скролл мышки). Потому что скроллбар показывает, где сейчас находится окно просмотра в общем документе, а в бесконечном холсте это не имеет смысла
    4. При прокрутке холста проверять, дошли ли до части, где нет картинки, тогда убрать дальний ряд или столбец и дорисовать новый ряд или столбец из массива.
    5. При дорисовке картинок из массива, сменить скролл холста - вернуть холст в центр, как раз там будут нужные картинки после удаления ряда картинок
    Ответ написан
  • C#. Как посчитать количество слова из словаря в тексте?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Скорее всего, str.Contains(line1) показывает наличие (одно или несколько слов) в строке, а не количество слова в этой строке. Нужно искать слово до конца, даже после первого совпадения.

    int Count_Good = 0, Count_Bad = 0;
    
    // получаем словари в память, чтобы не открывать файлы словарей повторно
    // а также переводим слова в строчные
    var goods = File.ReadAllLines("slovar_good.txt").Select(word => word.ToLower());
    var bads = File.ReadAllLines("slovar_bad.txt").Select(word => word.ToLower());
    
    var file = File.OpenText(@"C:\Users\pavlovaa\Desktop\hik.txt");
    string str;
    while ((str = file.ReadLine()) != null)
    {
        str = str.ToLower();
        Count_Good += CountWords(str, goods);
        Count_Bad += CountWords(str, bads);
    }
    
    int CountWords(string str, IEnumerable<string> words)
    {
        if (string.IsNullOrWhiteSpace(str)) return 0;
        var count = 0;
        foreach (var word in words)
        {
            if (string.IsNullOrWhiteSpace(word)) continue;
            int pos;
            while ((pos = str.IndexOf(word)) != -1)
            {
                count++;
                str = str.Substring(pos + word.Length);
            }
        }
        return count;
    }
    Ответ написан
  • Как сделать вывод 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 анализатор), проблема исчезла. Посмотрю, что будет дальше...
    Странно, что на других устройствах тот же канал не приводил проблемам..
    Ответ написан