Ответы пользователя по тегу C#
  • Почему счётчик таймера на windows forms увеличивается на 2, а не на 1?

    FoggyFinder
    @FoggyFinder
    Дело в том что существует 2 разные подписки на событие Tick - одно "автоматическое" созданное конструктором форм и второе "собственное".

    VS показывает все ссылки если нажать на "reference" вверху:

    614b6a2b8e3c2686381595.png

    Удалив одну из них получим ожидаемое поведение.
    Ответ написан
    Комментировать
  • Правильно ли такое структурирование кода в контексте ООП?

    FoggyFinder
    @FoggyFinder
    Для начала сделаю оговорку что основным языком программирования для меня является F#, который в первую очередь функциональный, но пару общих советов, рекомендации и просто мыслей, в том числе и по ООП, оставлю.

    1. Класс Particle. Запомните - никаких публичных полей в классах быть не должно. Это грубое нарушение инкапсуляции. Соответственно метод GetChar не имеет смысла ведь доступ к информации и так есть.

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

    abstract class Particle
    {
        public int X { get; protected set; }
        public int Y { get; protected set; }
        public char C { get; protected set; } = '%';
    
        public Particle(int _x, int _y)
        {
            X = _x;
            Y = _y;
        }
    }


    2. Интерфейс IFallable. Не понятен заложенный в нем смысл. Что он должен отражать? Некоторый объект который может падать? Если да, то

    а) подобный интерфейс был бы уместен в том случае если определенное поведение присуще разным, несвязанным между собой типам.
    б) строгая зависимость от ParticleStorage. [Edit: Здесь имеется ввиду что в контракте CanFall параметром идет ParticleStorage] Интерфейс становится совершенно не гибким.

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

    abstract class FallableParticle : Particle
    {
        protected FallableParticle(int _x, int _y) : base(_x, _y)
        {
        }
    
        public abstract void Fall();
    }


    ---
    [Edit: На вопрос что лучше использовать абстрактный класс или интерфейс во многих ситуациях нет однозначно правильного ответа. Все зависит от будущего использования. В C# нет множественного наследования (что прекрасно) и если есть необходимость в описании разных поведений (падание, горение) то, конечно, применение интерфейсов кажется более подходящим.

    Так что тут все зависит от контекста и планируемого использования, расширения. Другими словами - если другой функционал (горение) в ближайшем будущем не предполагается реализовывать то оставить абстрактный класс кажется адекватным решением. Если планируется, пусть и на перспективу, лучше использовать интерфейсы.]
    ---

    Обратите внимание: я полностью убрал метод CanFall - он не нужен. Падающая частица может падать по своему определению. Если ее способность перемещаться определяется извне то и проверка должна быть там.

    [Edit: В данном случае проверка перемещения в классе Mover

    if (f.CanFall(ps))
    {
        f.Fall();
    }


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

    Сами проверки могут быть многоуровневыми. Например, мы могли бы запретить перемещение в определенное направление или в определенную позицию (скажем, позволить x, y принимать только неотрицательные значения). Тогда бы контракт Fall() имело бы смысл переписать немного по другому

    public bool Fall();
    public bool CanFall { get; }


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

    Теперь ваши объекты могу выглядеть следующим образом

    class Sand : FallableParticle
    {
        public Sand(int x, int y) : base(x, y) => C = '0';
    
        public override void Fall() => Y += 1;
    }
    
    class Block : Particle
    {
        public Block(int x, int y) : base(x, y) => C = '#';
    }


    3. Класс ParticleStorage. Несмотря на то что внутри вы объявляете List сами возможности этой коллекции вы не используете. Нужно четко понимать когда делать класс мутабельным (изменяемым) а когда нет. Когда использовать иммутабельные коллекции а когда нет.

    В данном случае вы не изменяете внутреннюю коллекцию частиц, а значит динамический массив (List по своей сути это именно что динамический массив, а не настоящий список) вам не нужен.

    При использовании изменяемых коллекций будь крайне осторожны давать к ним внешний доступ. Метод GetParticles опасен - с вашим набором кто угодно может сделать все что им вздумается. Вместо этого можно просто реализовать интерфейс IEnumerable что позволит итерировать по коллекции но не менять ее.

    Далее, сам метод GetParticleByPosition не используется по назначению. Вместо этого вы просто проверяете существование частицы а само возвращенное значение отбрасываете.

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

    class ParticleStorage : IEnumerable<Particle>
    {
        readonly ImmutableArray<Particle> particles;
        public ParticleStorage(IEnumerable<Particle> p) =>
            particles = p.ToImmutableArray();
    
        public bool IsParticleExists(int x, int y) =>
            this.Any(p => p.X == x && p.Y == y);
    
        public IEnumerator<Particle> GetEnumerator() =>
            ((IEnumerable<Particle>)particles).GetEnumerator();
    
        IEnumerator IEnumerable.GetEnumerator() =>
            ((IEnumerable)particles).GetEnumerator();
    }


    4. Класс Mover.

    1. Название метода MoveAllParticles может ввести в заблуждение - ведь двигаться будут не все частицы. Кроме того судя по параметру и так понятно что речь идет именно о частицах а не о чем-то другом. Так что лаконичного Move будет достаточно чтобы смысл метода был ясен.

    2. Метод можно упростить использовав Linq-метод OfType

    class Mover
    {
        public void Move(ParticleStorage ps)
        {
            foreach (var p in ps.OfType<FallableParticle>())
            {
                if (!ps.IsParticleExists(p.X, p.Y + 1))
                {
                    p.Fall();
                }
            }
        }
    }
    Ответ написан
    3 комментария
  • Как обратиться к методу?

    FoggyFinder
    @FoggyFinder
    Не очень понятно что конкретно у вас не получилось. Сама идея правильная - вынести общий функционал в метод.

    Единственное что могло бы вызвать сложность так это сделать дополнительное ограничение на введенные данные. Например, у вас для параметра a еще требуется четность. Для этого можно просто передавать фильтр:

    static int ReadInt(Func<int, bool> filter)
    {
        while (true)
        {
            var a = Console.ReadLine();
            bool a_number = int.TryParse(a, out int ai);
    
            if (a_number && filter(ai))
            {
                return ai;
            }
            else
            {
                Console.WriteLine("Некорректные данные. Попробуйте еще раз.");
            }
        }
    }


    и пример вызова

    Console.Write($"\nВведите четное значение <a>: ");
    var a = ReadInt(a => a % 2 == 0);
    Ответ написан
    1 комментарий
  • Как правильно переписать этот код на C#?

    FoggyFinder
    @FoggyFinder
    Используйте метод Add из структуры HashCode:

    public override int GetHashCode()
    {
        var hashCode = new HashCode();
    
        hashCode.Add(GetGitCommitId());
        hashCode.Add(GetGitCommitIdDescribeShort());
        hashCode.Add(GetBuildTimeStamp());
        hashCode.Add(GetProjectVersion());
        hashCode.Add(GetCopyright());
        hashCode.Add(GetLicense());
        hashCode.Add(GetUrl());
        hashCode.Add(GetBuildJDKVersion());
        hashCode.Add(GetTargetJREVersion());
    
        return hashCode.ToHashCode();
    }


    Если бы у вас было меньше 9 аргументов, то можно было бы применить метод Combine.

    Но в C# вместо гет-методов лучше использовать свойства
    Ответ написан
    Комментировать
  • Как вытащить определенные данные из json файла?

    FoggyFinder
    @FoggyFinder
    Один из способов (Linq2Json), с помощью библиотеки Json.Net о которой edward_freedom писал в комментариях.

    var jsonPath = "data.json";
    using StreamReader reader = File.OpenText(jsonPath);
    var jToken = JToken.ReadFrom(new JsonTextReader(reader));
                
    var values =
        jToken["relationships_followers"].Select(j =>
                j["string_list_data"][0]["value"].ToObject<string>())
        .ToArray();
    Ответ написан
  • Разные View в WinForms?

    FoggyFinder
    @FoggyFinder
    Используйте обычный UserControl в качестве "страницы" (экраны), а для их отображения выберите ту панель которая подходит. В простейшем случае это может быть обычная Panel
    Ответ написан
  • Почему "ручной" foreach в три раза быстрее LINQ?

    FoggyFinder
    @FoggyFinder
    Сейчас вы сравниваете версию с селектором (преобразующей функцией) с обычным циклом, вместо этого лучше сравнивать "чистую" версию:

    public static double Average(Dictionary<string, int> stuff) => stuff.Values.Average();


    тогда и разница будет не такой значительной.
    Ответ написан
    1 комментарий
  • Как склеить несколько подмассивов расположенных в одном массиве типа Object?

    FoggyFinder
    @FoggyFinder
    1. Вы не учитываете вложенность массивов: размер нового массива должен быть равен сумме длин всех входящих массивов. За первый пробег вы узнаете общий размер и проверяете что все типы совпадают, за второй пробег уже копируете.
    Тогда получится нечто вроде такого

    static Array Combine(params object[] arr)
    {
        if (arr.Length == 0)
        {
            return null;
        }
    
        var totalSize = 0;
        Type type = null;
        foreach (Array inner in arr)
        {
            var innerType = inner.GetType();
            type ??= innerType;
    
            if (innerType != type)
            {
                return null;
            }
    
            totalSize += inner.Length;
        }
    
        var resultArr = Array.CreateInstance(type.GetElementType(), totalSize);
        var currentSize = 0;
    
        foreach (Array inner in arr)
        {
            Array.Copy(inner, 0, resultArr, currentSize, inner.Length);
            currentSize += inner.Length;
        }
    
        return resultArr;
    }


    2. Отказаться от возвращения Array и перейти к дженерикам. Тогда можно использовать Linq в виде связки SelectMany + ToArray(), в SelectMany нужно будет привести к нужному типу.
    Ответ написан
    9 комментариев
  • В ListView не отображаются данные из ObservableCollection и XAML отказывается делать Binding. Как исправить?

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

    Adding x:DataType to a ContentPage breaks nested o...

    Так что чтобы исправить проблему достаточно просто добавить

    x:DataType="vm:MedicineViewModel"

    чтобы было понятнее привожу контекст

    <ListView.ItemTemplate>
        <DataTemplate x:DataType="vm:MedicineViewModel">
            <ViewCell>
                <ViewCell.View>
                    <StackLayout>
                        <Label FontSize="Large" Text="{Binding Title}" />
                        <Label FontSize="Small" Text="{Binding Format}" />
                        <Label FontSize="Small" Text="{Binding Method}" />
                    </StackLayout>
                </ViewCell.View>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>


    И позвольте дать пару советов которые не относятся непосредственно к ответу

    1. В процессе работы над проектов время от времени запрашивайте CodeReview, так код будет чище и вы сможете "расти" быстрее.

    2. Не отправляйте в гит секретные ключи. Пока проект учебный это не так и страшно, но лучше проявлять осмотрительность как можно раньше.

    3. Когда сталкиваетесь с проблемой (а такое всенепременно будет) не тащите в вопрос код из своего проекта, лучше создайте маленький тестовый проект где не будет ничего лишнего. На первый взгляд это лишняя трата времени, но во многих случаях вы докопаетесь до сути проблемы во время подготовки такого MCVE.
    Ответ написан
    4 комментария
  • Что сделано не так?

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

    Чтобы избежать подобных проблем советую вынести считывание из БД в отдельный метод.

    Что этот метод может возвращать? Как раз то, что мы хотим проверить - а существует ли такой пользователь у нас в системе и какие у него есть права доступа.
    Для удобства добавим отдельный класс для хранения и использования в дальнейшем этой информации. Роль лучше хранить не в виде строк, а в виде перечислений - компактней и работать проще.

    public enum Role 
        { 
            Admin, 
            Manager,
            Accountant
        }
    
        public class User
        {
            public User(string login, Role role)
            {
                Login = login;
                Role = role;
            }
    
            public string Login { get; }
    
            public Role Role { get; }
        }


    Итак, новый метод будет возвращать экземпляр класса User в случае если параметры введены верно и null если такого пользователя нет.
    В вашем коде роль пользователь выбирает сам, что кажется немного странным, на мой взгляд, лучше эту проверку памяти опустить.

    public static User GetUserOrNull(string login, string password)
    {
        using (var connection = new SqliteConnection(connectionString))
        {
            connection.Open();
            using (var command = connection.CreateCommand())
            {
                var query =
                    @"SELECT role FROM users 
                        WHERE login = @login AND password = @password";
    
                command.CommandText = query;
                command.Parameters.AddWithValue("login", login);
                command.Parameters.AddWithValue("password", password);
    
                using (var reader = command.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        // GetOrdinal на тот случай если будем возвращать другие значения
                        var role = reader.GetInt32(reader.GetOrdinal("role"));
    
                        return new User(login, (Role)role);
                    }
                }
    
                return null;
            }
        }
    }


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

    Теперь обработка будет намного проще и чище - ведь сам запрос будет находится в другом месте.

    private void Vhod_Click(object sender, EventArgs e)
    {
        try
        {
            SHA1 hash = new SHA1();
            var login = LoginField.Text;
            var pHash = hash.Hash(PasswordField.Text);
            var user = DataBase.GetUserOrNull(login, pHash);
            if (user != null)
            {
                this.Hide();
                var MainForm = new MainForm();
                MainForm.Closed += (s, args) => this.Close();
                MainForm.Show();
            }
            else
            {
                MessageBox.Show("Неверный логин или пароль! Пожалуйста,повторите попытку.", "Ошибка!");
                LoginField.Text = "";
                LoginField.Clear();
                PasswordField.Text = "";
                PasswordField.Clear();
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
    Ответ написан
  • Как заполнить treeview ( treenode ) данными из datatable?

    FoggyFinder
    @FoggyFinder
    Можно преобразовать DataTable к строго типизированной коллекции с помощью метода AsEnumerable. Тогда вы сможете сделать группировку значений с помощью метода GroupBy.

    После этого заполнить TreeView не составит труда.

    Пример

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            var treeView = new TreeView
            {
                Dock = DockStyle.Fill
            };
            this.Controls.Add(treeView);
    
            var dt = GetDataTable();
            PopulateTreeViewFromDataTable(treeView, dt);
        }
    
        private DataTable GetDataTable()
        {
            var dt = new DataTable("Цемент");
    
            dt.Columns.AddRange(new[] {
                new DataColumn("Name"),
                new DataColumn("NameOfCement")
            });
    
            dt.Rows.Add("Портланд", "Вихрь");
            dt.Rows.Add("Романцемент", "Тайга");
            dt.Rows.Add("Романцемент", "Свой");
            dt.Rows.Add("Алумацемент", "Курон");
    
            return dt;
        }
    
        public void PopulateTreeViewFromDataTable(TreeView tv, DataTable dataTable)
        {
            var data =
                dataTable
                    .AsEnumerable()
                    .Select(row => new
                    {
                        Name = row.Field<string>("Name"),
                        Cement = row.Field<string>("NameOfCement")
                    })
                    .GroupBy(x => x.Name);
    
            tv.Nodes.Clear();
    
            var root = tv.Nodes.Add(dataTable.TableName);
            foreach (var gr in data)
            {
                var node = root.Nodes.Add(gr.Key);
                foreach (var subItem in gr)
                {
                    node.Nodes.Add(subItem.Cement);
                }
            }
        }
    }


    Результат

    60856a498cd61836541563.jpeg
    Ответ написан
    Комментировать
  • Можете дать идеи для проектов на языке C#?

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

    Поскольку вы привели в пример калькулятор, то могу посоветовать библиотеку

    AngouriMath

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

    Если математика не то чем вы хотели бы заниматься, то уточните в комментариях области своих интересов, попробую подобрать пару ссылок на которые можно обратить внимание.
    Ответ написан
    2 комментария
  • Как выделить цветом те элементы первой матрицы, которые больше за соответствующие элементы второй матрицы c#?

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

    Просто замените

    dataGridView1.Columns[j].DefaultCellStyle.BackColor = Color.Chartreuse;


    на

    dataGridView1[j, i].Style.BackColor = Color.Chartreuse;


    для ветки else - аналогично.

    По остальному:

    * Посмотрите границы циклов, оба раза используете количество столбцов - границы для второго должны определятся количеством строк

    Обновлено (исправлена опечатка)

    Должно получится примерно так

    private void task1_Click(object sender, EventArgs e)
    {
        int n1, n2;
        for (int j = 0; j < dataGridView1.ColumnCount; j++)
        {
            for (int i = 0; i < dataGridView1.RowCount; i++)
            {
                if (int.TryParse(dataGridView1[j, i].Value.ToString(), out n1) 
                    && int.TryParse(dataGridView2[j, i].Value.ToString(), out n2))
                {
                    if (n1 > n2)
                    {
                        dataGridView1[j, i].Style.BackColor = Color.Chartreuse;
                    }
                    else
                    {
                        dataGridView1[j, i].Style.BackColor = Color.White;
                    }
                }
            }
        }
    
        task2.Enabled = true;
    }
    Ответ написан
    3 комментария
  • Как десериализовать xml с последующим заполнением textbox?

    FoggyFinder
    @FoggyFinder
    У вас xml не соответствует классу.

    0) Нет закрывающего тега (полагаю, это опечатка)
    1) В xml описывает вложенный элемент radiostation тогда как в классах этого типа нет
    2) name это аттрибут, а вы пытаетесь десериализовать как элемент.

    В таких случаях проще не писать классы самому а использовать сервисы, такие как Xml2Csharp

    Вставляем xml, получаем

    [XmlRoot(ElementName = "radiostation")]
    public class Radiostation
    {
        [XmlElement(ElementName = "country")]
        public string Country { get; set; }
        [XmlElement(ElementName = "genre")]
        public string Genre { get; set; }
        [XmlElement(ElementName = "subgenre")]
        public string Subgenre { get; set; }
        [XmlElement(ElementName = "title")]
        public string Title { get; set; }
        [XmlElement(ElementName = "urlwww")]
        public string Urlwww { get; set; }
        [XmlElement(ElementName = "ico")]
        public string Ico { get; set; }
        [XmlAttribute(AttributeName = "name")]
        public string Name { get; set; }
    }
    
    [XmlRoot(ElementName = "users")]
    public class Users
    {
        [XmlElement(ElementName = "radiostation")]
        public Radiostation Radiostation { get; set; }
    }


    Ответ написан на предположении что менять структуру классов допустимо, если это не так то уточните этот момент в комментариях.

    Также небольшое замечание относительно метода LoadFile: вы в нем совсем не используете параметр filePath
    Ответ написан
    Комментировать
  • Как импортировать таблицу Excel в C#?

    FoggyFinder
    @FoggyFinder
    Существует множество библиотек для работы с Excel. Что из них выбрать зависит от задачи (например, нужно только читать данные или нужно формировать полноценный отчет, со стилями и так далее).

    Судя по описанию, здесь вам нужно только чтение, причем с поддержкой устаревшего формата xls, поэтому рекомендую библиотеку ExcelDataReader

    Если у вас .Net Core приложение не забудьте добавить строку

    Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);


    А теперь пример.

    1. В C# не принято для классов использовать публичные поля, поэтому перепишем их на свойства

    public class Questions
    {
        public string Question { get; set; }
        public string Theme { get; set; }
        public string Description { get; set; }
        public string QuestionEn { get; set; }
        public string DescrEn { get; set; }
    }


    2. Само чтение

    const string FilePath = "sample.xls";
    var qs = new List<Questions>();
    
    using var stream = File.Open(FilePath, FileMode.Open, FileAccess.Read);
    using var reader = ExcelReaderFactory.CreateReader(stream);
    do
    {
        while (reader.Read())
        {
            var question = reader.GetString(0);
            var theme = reader.GetString(1);
            var description = reader.GetString(2);
    
            qs.Add(new Questions()
            {
                Question = question,
                Theme = theme,
                Description = description
            });
        }
    } while (reader.NextResult());


    3. Выводим результат

    foreach(var q in qs)
    {
        Console.WriteLine($"{q.Question}");
        Console.WriteLine($"{q.Theme}");
        Console.WriteLine($"{q.Description}");
        Console.WriteLine(new string('-', Console.WindowWidth));
    }


    4. Запускаем, проверяем

    Индейцы в знак примирения хлопали в ладоши
    history
    Они закапывали топор войны
    ----------------------------------------------------------------------------------------------------
    Моряки пропитывали свою одежду смолой, чтобы она не рвалась
    history
    Чтобы она не пропускала воду
    ----------------------------------------------------------------------------------------------------
    Ответ написан
    1 комментарий
  • Как рисовать в PictureBox на второй форме?

    FoggyFinder
    @FoggyFinder
    Удобнее всего выполнять рисование в обработчике события Paint. Если в какой-то момент нужно обновить отображение, то следует вызвать перерисовку.

    Пример, на основе вашего кода

    readonly int[] q;
    public Form2(int[] q)
    {
        InitializeComponent();
        this.q = q;
        this.WindowState = FormWindowState.Maximized;
        Size size = new Size(Screen.PrimaryScreen.WorkingArea.Width,
            Screen.PrimaryScreen.WorkingArea.Height);
        pictureBox1.Size = size;
        pictureBox1.Paint += PictureBox_Paint;
    }
    
    private void PictureBox_Paint(object sender, PaintEventArgs e)
    {
        Graphics g = e.Graphics;
        g.Clear(Color.FromArgb(q[2], q[3], q[4]));
        for (int i = 0; i < pictureBox1.Width; i += q[0])
        {
            for (int j = 0; j < pictureBox1.Height; j += q[0])
            {
                g.FillRectangle(new SolidBrush(Color.FromArgb(q[5], q[6], q[7])), i, j, q[1], q[1]);
            }
        }
    }


    Небольшой комментарий - совет не относящийся непосредственно к вопросу:

    Не передавайте массив в параметрах, создайте класс со свойствами чьи названия будут отражать смысл значений и используйте его.
    Ответ написан
  • Как использовать переменную типа char?

    FoggyFinder
    @FoggyFinder
    Давайте попробуем разобраться.

    Массив это ограниченный набор некоторых данных определенного типа.

    char[] notAllowedSymbols = { '!', '#', '$', '%', '&', '(', ')', '*', ',', '+', '-' };

    здесь, вы объявили массив элементов типа char.

    Для доступа к значению в массиве используются индексы.

    Первый элемент в массиве доступен по индексу 0.
    Второй - по индексу 1.
    и так далее.

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

    Теперь возвращаемся к нашему массиву notAllowedSymbols.

    Как теперь, учитывая информацию об индексах, получить доступ к какому-нибудь элементу? В C# для это есть индексаторы:

    массив[индекс]


    значит для получения первого элемента нужно написать

    var firstChar = notAllowedSymbols[0]; // '!'

    точно также можно получить любой другой элемент указав правильный индекс ([i]).

    Теперь что касается приведенной части кода: посыл абсолютно правильный - как только мы убедились что строка содержит запрещенный символ нужно прекратить выполнение и сообщить пользователю об ошибке. Но реализация не совсем точная - break прервет выполнение цикла и пользователь так никогда и не узнает что допустил неточность.

    Здесь лучше всего подойдут функции. Вынесем проверку отдельно. Ее результатом может быть или "пройдена успешна" или "содержит ошибки". Всего два значения. Значит, здесь удобно воспользоваться типом bool. True будет означать что пароль подходящий и False будет символизировать ошибку.

    Теперь сама реализация

    private static bool IsValid(string password)
    {
        char[] notAllowedSymbols = { '!', '#', '$', '%', '&', '(', ')', '*', ',', '+', '-' };
    
        for (int i = 0; i < notAllowedSymbols.Length; i++)
        {
            if (password.Contains(notAllowedSymbols[i]))
            {
                return false;
            }
        }
        return true;
    }
    Ответ написан
    Комментировать
  • Почему данные из ObservableCollection не отображаются в ListView?

    FoggyFinder
    @FoggyFinder
    1. В WPF привязываться можно только к свойствам, у вас в коде _Peoples является полем.

    2. Если вы хотите сообщать об изменении объекта то нужно использовать интерфейс INotifyPropertyChanged

    ObservableCollection сообщает об своем изменении, поэтому вы можете очищать и перезаполнять коллекцию

    public ObservableCollection<People> Peoples { get; } =
        new ObservableCollection<People>();


    тогда метод LoadData примет примерно такой вид

    private void LoadData()
    {
        Peoples.Clear();
        if (File.Exists(_FileName))
        {
            using (var reader = new StreamReader(_FileName))
            {
                var xs = new XmlSerializer(typeof(People[]));
                var arr = (People[])xs.Deserialize(reader);
                foreach(var p in arr)
                {
                    Peoples.Add(p);
                }
                reader.Close();
            }
        }
    }


    3. Если DataContext установлен правильно то вместо

    ItemSourse = "{Binding ElementName = window, Path= = _Peoples}"


    будет достаточно

    ItemSourse = "{Binding Peoples}"

    Совет: придерживайтесь принятых соглашений - никаких нижних подчеркиваний в названиях для свойств.
    Ответ написан
    Комментировать
  • Замена значений в объектах PowerPoint C#?

    FoggyFinder
    @FoggyFinder
    Для простой работы с презентациями можно использовать Open XML SDK

    https://github.com/OfficeDev/Open-XML-SDK

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