Ответы пользователя по тегу C#
  • Можно ли во ViewModel узнать ширину элемента View?

    FoggyFinder
    @FoggyFinder
    Зависит от того зачем вам нужна информация о размерах UI - элементов.

    Если это часть пользовательских настроек, то хранить их на уровне ViewModel совершенно нормально и никак не противоречит паттерну.

    Но если это нужно исключительно для временной настройки UI, то и код должен находится на уровне View.

    Для вашего примера решение будет таким: добавьте обработчик события клика на кнопку и выводить информацию там.

    MVVM это не означает что в code-behind не должно быть никакого кода. Это типичная ошибка. В .xaml.cs не должно быть того, что не относится непосредственно к отображению.

    Управление анимацией, вывод вспомогательных сообщений, кастомизация пользовательских элементов управления - все это может быть там.
    Ответ написан
  • Найти минимальный элемент из максимальных элементов каждой строки двумерного массива?

    FoggyFinder
    @FoggyFinder
    Вы не уточнили какой способ ввода данных, поэтому для демонстрации воспользуемся стандартным классом Random для генерации псевдослучайных чисел.

    Для поиска значения в последовательности можно использовать:

    Max - для максимального.

    Min - для минимального.

    const int n = 5;
    const int m = 5;
    
    const int max = 10;
    
    var rand = new Random();
    
    var array = new int[n][];
    for (int i = 0; i < n; i++)
    {
        array[i] = new int[m];
        for (int j = 0; j < m; j++)
            array[i][j] = rand.Next(max + 1);
    }
    
    Console.WriteLine("Массив = ");
    foreach (var row in array)
    {
        foreach (var value in row)
            Console.Write($"{value,4}");
        Console.WriteLine();
    }
    
    var min = array.Min(row => row.Max());
    Console.WriteLine($"Минимальный элемент из максимальных каждой строки = {min}");


    По хорошему код нужно разбить на функции, но эту часть оставлю вам.
    Ответ написан
  • Какое оформление предпочтительнее?

    FoggyFinder
    @FoggyFinder
    Общие замечания (ко всем кусочкам):

    Нет разделения на функции (проблема: дублирование кода, усложняет восприятие)

    Вынесите каждую часть в удобный метод и вызывайте. Например:
    * Получение числа с консоли
    * Поиск минимального из трех чисел
    * Проверка на четность

    Вам будет намного понятнее что выполняет код - ваш взгляд охватит несколько вызовов и не будет отвлекаться на конкретные детали реализации.

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

    1 кусок:

    1. [ Критично ] Нет проверки на корректность ввода. Пользователь может случайно ввести букву и метод Convert.ToInt32 выбросит исключение. Довольно неприятно.

    2. Нарушение соглашения об именовании для переменной FinalResult

    3. Необязательная инициализация переменной FinalResult при объявлении.

    4. [ Субъективно ] Проблемы с форматированием: тернарный оператор занимает слишком много места для одной строки

    5. [ Может рассматриваться как придирка ] Копипаст подход. Вывод сообщения можно вынести из if-ов в - он одинаков для всех случаев.

    2 кусок:

    1. [ Критично ] Аналогично предыдущему

    2. Нарушение соглашения об именования для переменной MuchSmaller

    3. Объявление и лишняя инициализация переменной MuchSmaller намного впереди реального использования.

    4. Слишком сложные условные выражения.

    5. Аналогично п.5 предыдущего примера.

    3 кусок:

    Здесь, ура!, нет основной проблемы которая встречается во всех остальных примерах.

    1. Нарушение соглашения об именовании для переменной Result

    2. Странная смесь английского и русского языка в информационных сообщениях

    3. Странная смесь использования интернирования строк ($) и старого форматирования.

    4 кусок:

    1. [ Критично ] Нет проверки на корректность ввода. Пользователь может случайно ввести букву и метод Convert.ToInt32 выбросит исключение. Довольно неприятно.

    2. [ Существенно ] Копипаст подход для проверки на четность.

    3. Избыточные скобки в switch case

    5 кусок:

    1. [ Критично ] Нет проверки на корректность ввода. Пользователь может случайно ввести букву и метод Parse выбросит исключение.

    2. Проблемы с названием переменных. inputOne

    Итого:

    3-ий вариант условно нормальный. Остальные хуже.

    Упражнение:

    Попробуйте написать свой вариант с учетом замечаний перечисленных выше =)
    Ответ написан
  • WPF ListBox как вручную отсортировать список перетаскиванием элементов мышкой?

    FoggyFinder
    @FoggyFinder
    Есть замечательная библиотека которая практически все делает за вас:

    GongSolutions.WPF.DragDrop

    Все, что вам нужно в простейших сценариях, это установить на разрешение перетаскивать ИЗ и В:

    dd:DragDrop.IsDragSource="True"
    dd:DragDrop.IsDropTarget="True"


    Обновляю ответ в связи с комментарием:

    Общая рекомендация:

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


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

    VM:

    public class MainVM
    {
        private IEnumerable<FileInfo> GetFiles() =>
            Directory.EnumerateFiles(Directory.GetCurrentDirectory())
                        .Select(path => new FileInfo(path));
        public MainVM()
        {
            FileList = new ObservableCollection<FileInfo>(GetFiles());
        }
        public FileInfo CurrentFile { get; set; }
        public ObservableCollection<FileInfo> FileList { get; }
    }


    View:

    пространство имен:

    xmlns:dd="urn:gong-wpf-dragdrop"

    сам список:

    <ListBox
        dd:DragDrop.IsDragSource="True"
        dd:DragDrop.IsDropTarget="True"
        ItemsSource="{Binding FileList}"
        SelectedItem="{Binding CurrentFile}">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock VerticalAlignment="Center" Text="{Binding Name}" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>


    Гиф с результатом:

    5dee28369295b074649445.gif
    Ответ написан
  • Почему происходит NullReferenceException при добавлении экземпляра класса в лист?

    FoggyFinder
    @FoggyFinder
    List нужно предварительно инициализировать:

    public List<Node> children = new List<Node>();

    Но, обычно, публичные поля это далеко не лучшая стратегия.
    Ответ написан
  • Как динамически создавать элементы сетки Grid?

    FoggyFinder
    @FoggyFinder
    Можно установить в качестве панели UniformGrid, тогда все строки будут иметь одинаковую высоту.

    <ListBox Grid.Column="1" ItemsSource="{Binding Notes}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <UniformGrid Columns="1" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Title}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ListBox>


    За идею спасибо Athari
    Ответ написан
  • Проблемы с NETStandard.Library в Xamarin?

    FoggyFinder
    @FoggyFinder
    В списке источников NuGet:

    Tools -> NuGet Package Manager -> Package Manager Settings -> Package Sources.

    должен находится стандартный:

    https://api.nuget.org/v3/index.json


    Если его там нет, то нужно будет добавить.
    Ответ написан
  • Как построить график функции в windows forms c#?

    FoggyFinder
    @FoggyFinder
    Тут у вас сразу несколько ошибок:

    1. Нужно явно приводить к double иначе при делении двух int получите тоже целое.
    2. Вы берете очень большие границы. Рост знаменателя намного превышает скорость роста числителя. Сами посмотрите: возведите 50 в куб, что получите? График вы конечно построите, но наглядности будет немного.

    Что более важно: ваш отрезок OA = 2*a, а это значит что ваша правая граница точно должна быть меньше чем 2*a;

    3. Посмотрите как задана функция - в виде квадрата. Извлекая корень вы должны не забывать про нижнюю ветку - ту которую дают отрицательные значения y.
    Насколько мне известно возможности стандартного контрола довольно ограничены - вам придется заносить значения во вторую линию.

    Собирая все вместе, попробуйте что-то вроде такого:

    double a = 3;
    double step = 0.1;
    
    for (double x = 0; x < 2 * a - 1; x += step)
    {
        var n = x * x * x;
        var d = 2 * a - x;
        chart1.Series[0].Points.AddXY(x, Math.Sqrt(n / d));
        chart1.Series[1].Points.AddXY(x, -Math.Sqrt(n / d));
    }


    Здесь 1 (x < 2 * a - 1) вычитается только для примера, чтобы вы сравнили результат. В реальности вам придется определять границу более сложным путем чтобы для разных a и левой границы (x) получать наглядное представление. Но, по крайней мере, у вас есть от чего отталкиваться.
    Ответ написан
  • Как связать конкретную фигуру с выбранным элементом?

    FoggyFinder
    @FoggyFinder
    Пока нет возможности написать пример, но попробую дополнить ответ когда она появится.

    Если количество отображаемых элементов не слишком большое (в пределах 100-1000), то, вероятно, лучшим вариантом будет вынести часть информации в ViewModel (а именно, координаты расположения линий и состояние IsSelected). В вашем примере этот класс назван Model, но я рекомендую его переименовать, в дальнейшем это может сбить с толку.

    Тогда вы привяжите коллекцию к ItemsControl, где панелью будет выступать Canvas.
    Координаты привяжите индивидуально. Реагировать на изменение цвета можно будет или через триггер или через конвертер, но если вариантов всего 2, то проще, на мой взгляд, будет использовать именно триггер.

    При выделении элемента в коллекции сбрасываете состояние IsSelected предыдущего элемента в False и указанному соответственно ставите True.

    Обновлено:

    Появились несколько свободных минут, привожу пример:

    Для начала, для удобства создадим ViewModelBase, чтобы несколько раз не реализовывать интерфейс INPC:

    public abstract class ViewModelBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    
        protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
        {
            if (EqualityComparer<T>.Default.Equals(storage, value))
                return false;
            storage = value;
            this.OnPropertyChanged(propertyName);
            return true;
        }
    }


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

    public class LineVM : ViewModelBase
    {
        private bool isSelected;
        public bool IsSelected
        {
            get { return isSelected; }
            set { SetProperty(ref isSelected, value); }
        }
    
        public LineVM(string name, int x, int y)
        {
            Name = name;
            X = x;
            Y = y;
        }
        public string Name { get; set; }
    
        public int X { get; private set; }
    
        public int Y { get; private set; }
    }


    Здесь нет необходимости в изменении координат во время работы программы, поэтому сеттеры оставим приватными.

    Главная ViewModel :

    public class ItemsVM : ViewModelBase
    {
        private LineVM selectedVM;
    
        public ObservableCollection<LineVM> Models { get; }
        public LineVM SelectedModel
        {
            get { return selectedVM; }
            set
            {
                if (SelectedModel != null)
                    SelectedModel.IsSelected = false;
                value.IsSelected = true;
                SetProperty(ref selectedVM, value);
            }
        }
    
        public ItemsVM()
        {
            Models = new ObservableCollection<LineVM>
            {
                new LineVM ("A", 10, 20 ),
                new LineVM ("B", 10, 40),
                new LineVM ("C", 10, 60),
                new LineVM ("D", 10, 80)
            };
        }
    }


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

    Теперь View часть. Привожу только измененный третий столбец:

    <ItemsControl Grid.Column="2" ItemsSource="{Binding Models}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas IsItemsHost="True" />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Line
                X1="0"
                X2="100"
                Y1="0"
                Y2="00">
                    <Line.Style>
                        <Style TargetType="Line">
                            <Setter Property="Stroke" Value="Blue" />
                            <Style.Triggers>
                                <DataTrigger Binding="{Binding IsSelected}" Value="True">
                                    <Setter Property="Stroke" Value="Red" />
                                </DataTrigger>
                            </Style.Triggers>
                        </Style>
                    </Line.Style>
                </Line>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding X}" />
                <Setter Property="Canvas.Top" Value="{Binding Y}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>


    Тут тоже все достаточно просто - триггер реагирует на изменение свойства IsSelected и меняет цвет на красный для текущего элемента.

    И, для наглядности, оставляю гифку:

    5dd6e7e03664e626729135.gif
    Ответ написан
  • Как открыть файл определенного формата в c#?

    FoggyFinder
    @FoggyFinder
    Можно использовать метод Path.GetExtension чтобы получить расширение.

    Останется только убедится что файл в списке подходит под параметры:

    foreach (var filename in filenames)
        if (Path.GetExtension(filename) == ".exe")
            System.Diagnostics.Process.Start(filename);
    Ответ написан
  • Как упростить переменную в C#?

    FoggyFinder
    @FoggyFinder
    Можно вынести в отдельную функцию:

    string CreateASCIIStr(string value) => Encoding.ASCII.GetString(Convert.FromBase64String(value));


    или, в локальную, с использованием LocalData:

    string CreateASCIIStr(string value) =>
                    LocalData + Encoding.ASCII.GetString(Convert.FromBase64String(value));


    Добавлено:

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

    В данном случае у вас есть многократный последовательный вызов двух методов:

    Convert.FromBase64String и Encoding.ASCII.GetString

    для удобства их совмещаем в один вызов и получаем

    Encoding.ASCII.GetString(Convert.FromBase64String(value))

    Пример:

    string LocalData = "0";
    string CreateASCIIStr(string value) =>
        LocalData + Encoding.ASCII.GetString(Convert.FromBase64String(value));
    
    var data = new[] {
        CreateASCIIStr("Тут"),
        CreateASCIIStr("Все"),
        CreateASCIIStr("значения"),
        CreateASCIIStr("будут"),
        CreateASCIIStr("совершенные"),
        CreateASCIIStr("Разные")
    };


    Здесь возвращается массив, но если ваши совершенно разные значения в действительности являются массивом, то и обрабатывать их лучше как массив ;-)
    Ответ написан
  • Как сделать, что бы после закрытия messageboxа не открывались следующие?

    FoggyFinder
    @FoggyFinder
    Не уверен что правильно понял проблему, поэтому если окажется что мой ответ не подходит не стесняйтесь уточнять и спрашивать в комментариях, будем разбираться как починить.

    Код в обработчике button1_Click выполняется последовательно. После того как сработает первое условие вы уменьшаете значение на единицу и следующий if тоже становится верным.

    Первое что вам нужно сделать это вынести количество попыток в приватную переменную.
    kolvopotytok не самое подходящее название (и хотя я, например, могу понять что тут имеется ввиду количество попыток) лучше использовать названия на английском. Давайте возьмем countOfAttempts . Так как вы хотите отслеживать количество, то вам нужно завести еще одну переменную для учета их изначального (максимального) числа:

    public Form2(int digitfrom, int digitto)
    {
        InitializeComponent();
    
        Random rnd = new Random();
        r = rnd.Next(digitfrom, digitto);
        maxCountOfAttempts = countOfAttempts = 3;
    }
    private int r;
    private int maxCountOfAttempts, countOfAttempts;


    Затем вы можете заметить что каждый раз код внутри проверочного условия одинаковый.
    А теперь представьте, что диапазон будет достаточно широкий (скажем около сотни), а количество попыток увеличиться до 10. Копировать один и тот-же участок раз за разом не слишком эффективно. Давайте тогда вынесем весь код в отдельную функцию, которая будет принимать значение и в зависимости от него выводить информационное сообщение. Если пользователь угадал, то функция вернет true, в противном случае - false:

    private bool CheckResult(int a)
    {
        if (a == r)
        {
            MessageBox.Show($"Поздравляем!!! Вы угадали число с {countOfAttempts - 1} раза.");
            return true;
        }
        else if (a > r)
        {
            MessageBox.Show($"Число которое загадал ИИ меньше вашего!\nУ вас осталось {countOfAttempts - 1} попытки.");
            return false;
        }
        else
        {
    
            MessageBox.Show($"Число которое загадал ИИ больше вашего!\nУ вас осталось {countOfAttempts - 1} попытки.");
            return false;
        }
    }


    Теперь немного изменим сам обработчик:

    private void button1_Click(object sender, EventArgs e)
    {
        if (IsDigitsOnly(answer.Text))
        {
            int a = Convert.ToInt32(answer.Text);
            if (CheckResult(a))
                this.Close();
            else
            {
                countOfAttempts--;
                if (countOfAttempts == 0)
                {
                    MessageBox.Show("К сожелению вы проиграли\nЗагаданное число: " + r);
                    this.Close();
                }
            }
        }
        else
        {
            MessageBox.Show("Некорректный ввод данных", "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
        answer.Clear();
    }


    Должно работать. Как видите код достаточно близкий к вашему, но в то-же время немного понятнее.

    И, если позволите, небольшой совет на будущее: старайтесь избегать использования goto.
    Ответ написан
  • NET Core. Как перенести с виндовс на mac os?

    FoggyFinder
    @FoggyFinder
    Собрать приложение под другую платформу можно с помощью команды publish

    dotnet publish

    с указанием правильного runtime. Например:

    dotnet publish -c Release --framework netcoreapp2.0 --runtime osx-x64
    Ответ написан
  • Проблема с get и set. Почему не работает?

    FoggyFinder
    @FoggyFinder
    Свойства по сути маскируют в себе обычные методы для извлечения/установки полей.
    Представим что есть такой метод:

    public string GetValue()
    {
        return GetValue();
    }


    Здесь содержится только новый рекурсивный вызов. Какой результат будет получен при вызове такого метода? Естественно, StackOverflowException.

    Поэтому каждому свойству должно соответствовать какое-то поле.

    private string _actNumber;
    public string ActNumber {
        get { return _actNumber; }
        set { _actNumber = value; }
    }

    Во многих случаях не требуется какая-то определенная проверочная логика, поэтому в языке предусмотрены специальные конструкции (как, например, авто-свойства)

    public string ActNumber { get; set; }

    Что, конечно, может сбить с толку когда потребуется дополнительные условия.

    ----

    По поводу второй части вопроса. Обрабатывать null - результат нужно на самом верхнем (допустимом) уровне.

    В C# для этого есть целый набор null-операторов:

    ?., ??, ??=

    о которых можно прочитать, например, в справке:

    ?? and ??= operators

    Null-conditional operators ?. and ?[]
    Ответ написан
  • Вывод текста в label при клике c# windows forms?

    FoggyFinder
    @FoggyFinder
    В WinForms проектах конструктор форм прячет детали пользовательского интерфейса в файл Form1.Designer.cs (вместо Form1 будет название конкретного класса). Поэтому при копировании из других проектов или примеров нужно не забывать перенести также все подписки.

    Чтобы проверить их наличие (или отсутствие), в конструкторе форм выделите элемент и перейдите к просмотру его свойств (Properties).

    По умолчанию откроется вкладка свойств контрола. Для просмотра информации о событиях нужно перейти на следующую за ней (с нарисованной молнией):

    5db6e64c1bdca244908136.jpeg

    В списке находите требуемый элемент. Если справа ничего не присвоено, значит дело в отсутствующей подписке. Через выпадающий список (ComboBox) выбираете обработчик из доступных:

    5db6e7036cb33351827234.jpeg

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

    Для подписки не обязательно использовать конструктор форм, можно создавать их самостоятельно, например, в конструкторе форм:

    button1.Click += Button1_Click;

    Но лучше придерживаться одного стиля, так и код будет проще восприниматься и лишних подписок не будет.
    Ответ написан
  • Обращение к файлам из listbox?

    FoggyFinder
    @FoggyFinder
    Проблема заключается в выбранном подходе - вы храните важную информацию в самом элементе управления. Не стоит так делать.

    Создайте класс, который будет хранить полные пути в файлам:

    public class SomeClass
    {
        public string FilePath { get; }
    
        public SomeClass(string filepath) => FilePath = filepath;
    
        public override string ToString() => Path.GetFileName(FilePath);
    }


    А при отображении отдавать только сами названия файлов.

    Здесь класс назван SomeClass, но вы должны дать осмысленное название отражающее назначение.

    Теперь при считывании, создавайте коллекцию этих объектов:

    public IEnumerable<SomeClass> ReadData(string path) =>
        File.ReadLines(path).Select(row => new SomeClass(row));


    и выводите в ListBox любым удобным способом, например, через DataSource:

    listBox1.DataSource = ReadData(path).ToArray();

    Если понадобится изменять коллекцию, то выбирайте ту, которая сообщает об изменениях. Например, BindingList или даже просто BindingSource.
    Ответ написан
  • Обработка события нажатия ЛКМ на заголовке DGV?

    FoggyFinder
    @FoggyFinder
    Обрабатывайте общее событие CellMouseClick. У выделенной на скриншоте ячейки оба индекса равны -1:

    private void DataGridView1_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    {
        var row = e.RowIndex;
        var column = e.ColumnIndex;
    
        if(row == -1 && column == -1)
        {
            // Do something
        }
    }
    Ответ написан
  • Если объект соприкасается с чёрным цветом, то выполнять вывод в консоль?

    FoggyFinder
    @FoggyFinder
    Вначале небольшое лирическое отступление.

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

    Теперь к вопросу.

    Ваш объект (obj) это PictureBox , элемент управления, у которого, что не удивительно, нет своего метода intersects.

    Но вы можете использовать свойство Bounds чтобы получить местоположение и размеры ваших объектов:

    if(pictureBox1.Bounds.IntersectsWith(pictureBox2.Bounds))
    {
    // put your code here
    }


    По поводу второй части - проверки что объект определенного цвета. Чтобы быть уверенным что картинка полностью одного цвета придется проверять каждый пиксель.
    Но, я думаю, что вы можете добиться цели намного более простым способом.

    Например, если ваш объект может менять цвета, а вас интересует является ли он черным, то в момент смены цветы храните это состояние (isBlack) в свойстве Tag вашего pictureBox.

    Если вы пишите какую-то простую игру по типу змейки, то пока не завязли, советую уделить немного времени на разделение кода на смысловые части - определите классы описывающую ваши объекты, логику взаимодействия и отдельно их отображение на форме.
    Ответ написан
  • Как смотреть на конструктор WPF и на XML одновременно?

    FoggyFinder
    @FoggyFinder
    5d6bc7e79d2c0080809369.png

    нажмите на "|" кнопку на той панели которая обведена на скриншоте.

    Если конструктор окажется с правой стороны, то поменять их местами можно кнопкой с "Swap panes" - будет на той-же панели но вверху на центральном сплиттере.
    Ответ написан