Ответы пользователя по тегу C#
  • Переход с 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. Этот виндовский номер монитора я так и не смог найти. Виндовские номера зависят порта видеокарты, к которому они подключены.
    Ответ написан
    2 комментария
  • Как загрузить 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());
    Ответ написан
    Комментировать
  • В чем разница между переопределением (override), от перекрытия (new), наследуемого метода?

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

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

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

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

    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";
            }
        }
    }
    Ответ написан
    3 комментария
  • Как правильно "Собрать решение"?

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

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    В Windows уже есть готовая функция, которая выполняет натуральное сравнение строк, лучше использовать её. Эта функция разбивает строку на числовые и строковые части и ищет эти части друг с другом, причём числовые части сравнивает как числа, а строковые - алфавитно.

    var testFiles = Directory.EnumerateFiles(solutionDirectory + @"\samples");
    
    var sortedTestFiles1 = testFiles.OrderBy(x => x, new NaturalComparer());
    // или
    var sortedTestFiles2 = testFiles.ToList();
    sortedTestFiles2.Sort(new NaturalComparer());
    
    /// <summary>
    /// Натуральное сравнение строк
    /// </summary>
    public class NaturalComparer : IComparer<string>
    {
        /// <summary>
        /// Вызов WinApi-функции для натурального сравнения строк
        /// </summary>
        [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
        private static extern int StrCmpLogicalW(string psz1, string psz2);
    
        /// <summary>
        /// Натуральное сравнение строк
        /// </summary>
        /// <param name="x">Первая строка</param>
        /// <param name="y">Вторая строка</param>
        /// <returns>Сравнивает две строки, возвращая -1, 0 или 1</returns>
        public static int Compare(string x, string y)
        {
            return StrCmpLogicalW(x, y);
        }
    
        /// <summary>
        /// Натуральное сравнение строк
        /// </summary>
        /// <param name="x">Первая строка</param>
        /// <param name="y">Вторая строка</param>
        /// <returns>Сравнивает две строки, возвращая -1, 0 или 1</returns>
        int IComparer<string>.Compare(string x, string y)
        {
            return StrCmpLogicalW(x, y);
        }
    }
    Ответ написан
    1 комментарий
  • C# WPF почему при Window.Left = 0 между границей экрана и окном появляется пустое пространство?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Это хром окна (WindowChrome), обычно выглядит как тень вокруг окна, является частью окна, поверх клиентской частью. Обычно размер хрома 7px.
    https://docs.microsoft.com/ru-ru/dotnet/api/system...

    Хром отключается при указании окна в развёрнутое состояние (Maximized). А также, можно указать WindowStyle.None окну, тогда неклиентская часть отключится.
    Ответ написан
    Комментировать
  • Чем можно заменить интерполяцию строк в C# 5?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    public void Print(string str)
    {
          Console.WriteLine(string.Format("{0} {1} Ok!", DateTime.Now, str));
    }
    Ответ написан
    3 комментария
  • Почему не добавляет элемент в список?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Вероятно, вы где-то пересоздаёте список meet. Поставьте readonly на поле meet (а ещё лучше, переименовать её на MeetingsList)
    public readonly List<Meetings> MeetingsList = new List<Meetings>();

    Это предотвратит случайное перезаписание переменной.

    >foreach (Meetings meet in meet)
    Тут ошибка в коде, нельзя создать переменную-счётчик цикла Meetings meet, потому что переменная с именем meet уже есть. Если ошибка здесь не возникает, то, скорее всего, в какой-то из двух имён вместо английской "e" стоит русская "е".
    Ответ написан
    4 комментария
  • Как загрузить с помощью include других уровней данные с помтощью entity fraimwork?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    В одной Include можно сразу показывать несколько вложенных связей, тогда все промежуточные тоже подключатся:
    return (from e in db.Position
        .Include(p => p.Operations.Select(o => o.Transition.Select(t => t.RiggingTransition)))
        .Include(p => p.Operations.Select(o => o.MaterialOperation))
        where ...
        select e).ToList();

    В данном случае в Position должны подключиться MaterialOperation и Transition, в котором подключатся RiggingTransition
    Ответ написан
    Комментировать
  • Как привязать данные разного типа к одному элементу управления и выводить в зависимости от определенного условия?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Можно сделать шаблоны DataTemplate, которые изменят шаблон нужного типа:
    <Window x:Class="TestApp.MainWindow"
            xmlns:testApp="clr-namespace:TestApp"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <DataTemplate DataType="{x:Type testApp:TestInt}">
                <TextBlock Text="{Binding Value}" Background="Red"/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type testApp:TestStr}">
                <TextBlock Text="{Binding Value}" Background="Yellow"/>
            </DataTemplate>
        </Window.Resources>
        <StackPanel>
                <ContentControl Content="{Binding Test1}"/>
                <ContentControl Content="{Binding Test2}"/>
                <ListBox ItemsSource="{Binding TestList}"/>
        </StackPanel>
    </Window>

    public class TestInt
    {
        public int Value { get; set; }
    }
    public class TestStr
    {
        public string Value { get; set; }
    }
    public partial class MainWindow : Window
    {
        public TestInt Test1 { get; set; }
        public TestStr Test2 { get; set; }
        public List<object> TestList { get; set; } 
    
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            Test1 = new TestInt { Value = 123 };
            Test2 = new TestStr { Value = "asd" };
            TestList = new List<object> { Test1, Test2 };
        }


    Чтобы даташаблоны автоматически подключались, тип нужно указывать через DataType="{x:Type testApp:TestInt}", а не через DataType="testApp:TestInt"
    Ответ написан
    Комментировать
  • Property и Fields?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Да, при создании автосвойства создаётся скрытая переменная - данные должны где-то храниться.

    Эта переменная создаётся с хитрым именем (с запрещёнными в c# символами в имени переменной). Можно увидеть это таким способом:
    class TestClass
    {
        private int _a;
        public int A
        {
            get { return _a; }
            set { _a = value; }
        }
    
        public int B { get; set; }
    }
    
    var testClass = typeof(TestClass);
    var fields = testClass.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
    foreach (var fieldInfo in fields)
    {
        Console.WriteLine(fieldInfo.Name);
    }
    // результат:
    // _a
    // <B>k__BackingField
    Ответ написан
    4 комментария
  • Как сработает переопределение?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    В классе A метод Print отмечен виртуальным, то есть, его можно переопределить. Но в наследнике B нет переопределения этого метода, а создаётся новый виртуальный метод с тем же именем - ключевое слово new указывает, что создаётся новый метод, не связанный с методом A.Print.
    В классе C метод Print переопределяет метод родителя, то есть, метод B.Print, но не переопределяет метод A.Print.
    Так как у переменной указан тип A, то вызов ac.Print() вызовется метод - единственный из реализаций метода A.Print.

    Вот смотрите, чтобы было понятнее:
    class A { public virtual void Print() { Console.WriteLine("A"); } }
    class B : A { public override void Print() { Console.WriteLine("B"); } }
    class C : B { public new virtual void Print() { Console.WriteLine("C"); } }
    class D : C { public override void Print() { Console.WriteLine("D"); } }
    
    A ad = new D();
    ad.Print(); // покажет B
    Ответ написан
    3 комментария
  • Как строку форматировать в функцию (C#)?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    lam0x86 сказал всё правильно, я распишу про сложный путь.
    Нужно разделить строку на части, состоящие из чисел и операций.
    В простом случае (когда два числа и одна операция) это можно сделать однократным поиском str.IndexOfAny(new []{'+', '-', '*', '/'}), который вернёт индекс символа операции.
    В общем случае (когда несколько операций в строке) нужно искать циклом и добавлять найденные части в список, и потом обрабатывать его другим циклом. Более правильно, но более сложно - учитывать приоритеты операций (сначала умножение, потом сложение).
    Если в строке возможны скобки, то лучше части заполнять не в список, а в специальный класс, который будет разбирать полученные части в дерево вычислений.
    Ответ написан
    6 комментариев
  • Какие различия между Parse и Convert.To?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Чтобы разобраться, в чём разница, то смотрите источник - исходный код вот здесь (в строке 887).
    В общем, Parse берёт строку и возвращает число (возможно, будет исключение). Convert.ToInt16 имеет много вариантов, в зависимости от типа аргумента. Конкретно Convert.ToInt16(string) вызывает Int16.Parse(value, CultureInfo.CurrentCulture), то есть, абсолютно нет разницы с Parse (в том числе, возникнет исключение, если строка не является числом).
    Ответ написан
  • Как обработать событие из DataTemplate нажатия радиокнопок?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    ListBox (как и другой наследник ItemsControl) имеет список элементов (удобнее всего его указывать через ItemsSource). Каждый элемент списка строится отдельным пунктом. У каждого пункта автоматически устанавливается DataContext - тот элемент исходного списка, который строится. В DataTemplate хорошо и правильно указывать нужный тип, который будет у элемента в датаконтексте, тогда появится подсказка у {Binding} внутри шаблона.

    public class Item : BaseViewModel
    {
        private string _name;
        public string Name 
        { 
            get => _name;
            set
            {
                _name = value;
                RaisePropertyChanged();
            }
        }
        
        private int _itemMode;
        public int ItemMode 
        { 
            get => _itemMode;
            set
            {
                _itemMode = value;
                RaisePropertyChanged();
            }
        }
    }
    
    public class ItemsList
    {
        public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>
        {
            new Item { Name = "Один", ItemMode = 1 },
            new Item { Name = "Два", ItemMode = 2 },
            new Item { Name = "Три", ItemMode = 3 },
        };
    }

    <Grid>
        <Grid.Resources>
            <converters:IntToBoolConverter x:Key="IntToBoolConverter"/>
            <DataTemplate DataType="{x:Type viewModels:Item}">
                <Border x:Name="Data">
                    <Grid x:Name="ContentBase" Margin="1,0">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <Label Name="DisplayText" Grid.Column="0" Content="{Binding Name}"/>
                        <RadioButton Name="_1" Grid.Column="1" Content="1" 
                                     IsChecked="{Binding ItemMode, Converter={StaticResource IntToBoolConverter}, ConverterParameter=1}"/>
                        <RadioButton Name="_2" Grid.Column="2" Content="2" 
                                     IsChecked="{Binding ItemMode, Converter={StaticResource IntToBoolConverter}, ConverterParameter=2}"/>
                        <RadioButton Name="_3" Grid.Column="3" Content="3" 
                                     IsChecked="{Binding ItemMode, Converter={StaticResource IntToBoolConverter}, ConverterParameter=3}"/>
                    </Grid>
                </Border>
            </DataTemplate>
        </Grid.Resources>        
        <Grid.DataContext>
            <viewModels:ItemsList/>
        </Grid.DataContext>
        <ListBox ItemsSource="{Binding Items}"/>
    </Grid>

    Код класса BaseViewModel
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        
        protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    Код конвертера
    public class IntToBooleanConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value != null 
                && parameter != null 
                && value.ToString() == parameter.ToString();
        }
     
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || parameter == null) return Binding.DoNothing;
    
            int result;
            if ((bool)value && int.TryParse(parameter.ToString(), out result))
            {
                return result;
            }
    
            return Binding.DoNothing;
        }
    }

    Ответ написан
    8 комментариев