Задать вопрос
  • Как переопределить метод, чтобы он создавал и возвращал копию объекта, из которого вызывается?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Как-то так это делается.

    Кстати, в производном классе вместо
    public override Base CreateCopy()
    можно указать
    public override Derivative CreateCopy()
    и это будет работать. Насколько помню, это нововведение новой версии C#. Вот в справке написано, что версии C# 9.

    using System;
    
    namespace ConsoleApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                var objectBase = new Base("Base");
                var objectDerivative = new Derivative("Derivative", '7');
    
                var copyBase = objectBase.CreateCopy();
                var copyDerivative = objectDerivative.CreateCopy();
    
                Console.WriteLine(copyBase);
                Console.WriteLine(copyDerivative);
            }
        }
    
        public class Base
        {
            private readonly string _text;
    
            public string Text
            {
                get { return _text; }
            }
    
            public Base(string text)
            {
                _text = text;
            }
    
            public virtual Base CreateCopy()
            {
                return new Base(Text);
            }
    
            public override string ToString()
            {
                return $"{nameof(Text)}: {Text}";
            }
        }
    
        public class Derivative : Base
        {
            private readonly char _symbol;
    
            public char Symbol
            {
                get { return _symbol; }
            }
    
            public Derivative(string text, char symbol)
                : base(text)
            {
                _symbol = symbol;
            }
    
            public override Base CreateCopy()
            {
                return new Derivative(Text, Symbol);
            }
    
            public override string ToString()
            {
                return $"{nameof(Text)}: {Text}, {nameof(Symbol)}: {Symbol}";
            }
        }
    }
    Ответ написан
    Комментировать
  • Как шифровать и расшифровывать строки(с посылом и получением строки)?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Можно использовать библиотеку или подсмотреть реализацию:
    https://github.com/2Toad/Rijndael256
    https://www.nuget.org/packages/Rijndael256.Core/3.2.5

    Почитай главу 3 в книге: Advanced ASP.NET Core 3 Security. Understanding Hacks, Attacks, and Vulnerabilities to Secure Your Website. Узнаешь о проблемах шифрования текста, к примеру и вообще увидишь исходники примеров, как надо. Книгу можно купить на Amazon для приложения для Windows, которое называется Kindle.

    string password = "sKzvYk#1Pn33!YN";  // The password to encrypt the data with
    string plaintext = "Top secret data"; // The string to encrypt
    
    // Encrypt the string
    string ciphertext = Rijndael.Encrypt(plaintext, password, KeySize.Aes256);
    
    // Decrypt the string
    plaintext = Rijndael.Decrypt(ciphertext, password, KeySize.Aes256);
    Ответ написан
    Комментировать
  • Актуальна ли книга C# 2.0 для 8.0?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Вот здесь описаны изменения от версии к версии. Отличия существенны.
    Ответ написан
    Комментировать
  • К чему привязать ProgressBar c#?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Нужно в классе "шифраторе" создать событие. Лучше всего шифровать файл вызвав Task.Factory.StartNew, а в конструкторе "шифратора" захватить текущий контекст синхронизации (SynchronizationContext) и по мере шифрования очередной части потока байт вызывать у контекста синхронизации метод Post (он не ждёт пока обработчик события отработает до конца) и внутри него уже вызывать событие ProgressChanged у "шифратора".

    Чтобы понять, что я имею ввиду, смотри код здесь: С# Taks и Invoke почему то блокируется форма?
    Ответ написан
  • Как исправить ошибку в с++?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    На примере UWP C++ проекта. Нужно сформировать С++ строку и конвертировать её в Managed String.
    Свойство Text имеет тип данных Platform::String (в других типах проектов не знаю какой там тип, как и не знаю, какой у вас тип проекта, видимо, Windows Forms, который для С++ мне создать не удалось из Visual Studio 2019)
    6043bd0d024ab531874548.png

    Подсмотреть разные примеры можно здесь: https://github.com/stammen/uwp-cpp-examples
    Использовать Sleep в UI потоке -- это зависание окна.
    И да, как писали в комментариях это не чистый С++, а С++/CX. Здесь пригодились бы знания C# и платформы .NET.

    MainPage.xaml.cpp
    #include "pch.h"
    #include "MainPage.xaml.h"
    
    using namespace RemoveApp;
    
    using namespace Platform;
    using namespace Windows::Foundation;
    using namespace Windows::Foundation::Collections;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Controls::Primitives;
    using namespace Windows::UI::Xaml::Data;
    using namespace Windows::UI::Xaml::Input;
    using namespace Windows::UI::Xaml::Media;
    using namespace Windows::UI::Xaml::Navigation;
    
    std::string FormatPoint(POINT&);
    
    std::wstring_convert<std::codecvt_utf8<wchar_t>> MainPage::s_converter;
    std::mutex MainPage::s_mutex;
    
    DispatcherTimer^ _timer;
    uint32_t _counter;
    const uint32_t TICK = 10000;
    
    MainPage::MainPage()
    {
        InitializeComponent();
        UpdateLabel();
    
        _timer = ref new DispatcherTimer();
        TimeSpan ts;
        ts.Duration = 1000 * TICK;
        _timer->Interval = ts;
        
        auto registrationToken = _timer->Tick += ref new EventHandler<Object^>(this, &MainPage::OnTick);
    
        _button->Click += ref new RoutedEventHandler(this, &MainPage::OnClick);
    }
    
    void MainPage::OnTick(Object^ sender, Object^ e)
    {
        UpdateLabel();
    }
    
    void MainPage::UpdateLabel()
    {
        //GetCursorPos(&cp);
        //SetCursorPos(0, 0);
        //SetCursorPos(cp.x, cp.y);
        ++_counter;
        POINT cp = { _counter, _counter + 10 };
        std::string text = FormatPoint(cp);
        _label1->Text = StringToPlatformString(text);
    }
    
    void MainPage::OnClick(Object^ sender, RoutedEventArgs^ e)
    {
        if (!_timer->IsEnabled)
        {
            _timer->Start();
        }
    }
    
    std::string MainPage::PlatformStringToString(Platform::String^ text)
    {
        if (text == nullptr) {
            return std::string("");
        }
    
        std::lock_guard<std::mutex> lock(s_mutex);
        return s_converter.to_bytes(text->Data());
    }
    
    Platform::String^ MainPage::StringToPlatformString(const std::string& text)
    {
        if (text.empty()) {
            return ref new Platform::String();
        }
        std::lock_guard<std::mutex> lock(s_mutex);
        std::wstring converted = s_converter.from_bytes(text);
        return ref new Platform::String(converted.c_str());
    }
    
    std::string FormatPoint(POINT& point)
    {
        return "x = " + std::to_string(point.x) + ", y = " + std::to_string(point.y);
    }


    MainPage.xaml.h
    #pragma once
    
    #include "MainPage.g.h"
    #include <codecvt>
    #include <string>
    #include <mutex>
    #include <cstdint>
    
    using namespace Platform;
    using namespace Windows::UI::Xaml;
    
    namespace RemoveApp
    {
    	/// <summary>
    	/// An empty page that can be used on its own or navigated to within a Frame.
    	/// </summary>
    	public ref class MainPage sealed
    	{
    	public:
    		MainPage();
    
    		void OnTick(Object^ sender, Object^ e);
    		void OnClick(Object^ sender, RoutedEventArgs^ e);
    		void MainPage::UpdateLabel();
    
    	private:
    		std::string PlatformStringToString(String^ s);
    		String^ StringToPlatformString(const std::string& s);
    
    		static std::wstring_convert<std::codecvt_utf8<wchar_t>> s_converter;
    		static std::mutex s_mutex;
    	};
    }


    MainPage.xaml
    <Page
        x:Class="RemoveApp.MainPage"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="using:RemoveApp"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
        mc:Ignorable="d">
    
        <Grid>
            <StackPanel
                HorizontalAlignment="Center"
                VerticalAlignment="Center">
                <TextBlock
                    x:Name="_label1"
                    Margin="0,25,0,0"
                    FontSize="24" />
                <Button
                    x:Name="_button"
                    Margin="0,18,0,0"
                    Content="Запустить таймер" />
            </StackPanel>
        </Grid>
    </Page>


    pch.h
    #pragma once
    
    #include <collection.h>
    #include <windows.h>
    
    #include "App.xaml.h"


    pch.cpp
    #include "pch.h"
    Ответ написан
    Комментировать
  • Как организовать постоянное обновление DataGrid в отдельном потоке?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Как организовать постоянное обновление DataGrid в отдельном потоке, чтобы при этом DataGrid был доступен, т.к. планируется добавить возможность выбирать строку из DataGrid и смотреть подробную информацию.

    Нельзя обновлять элементы UI из не UI потока. Можно получать данные в другом потоке, но обновлять UI нужно из UI потока. Я бы просто пробегал по ObservableCollection<{T}>, где T это класс, реализующий интерфейс INotifyPropertyChanged и обновлял нужные объекты уже в UI потоке. Судя по всему, проект не придерживается паттерна MVVM, а лучше бы придерживался.

    Список процессов нужно получать как раз в Task.Run(...), а вот вызывать обновление процессов в UI через Dispatcher или SynchronizationContext.

    Посмотри мои ответы здесь:
    Как осуществить ввод и вывод данных WPF?
    Здесь про Task.Delay и вообще цикл в Task.Run Как сделать цикл на основе ответа на Web запрос в C#?
    Здесь как перенаправлять выполнение через SynchronizationContext в UI поток С# Taks и Invoke почему то блокируется форма?

    Ещё, ожидание в методах, которые запускаются тасками нужно делать через вызов await Task.Delay(...), но точно не через Thread.Sleep(...):
    using System;
    using System.Threading.Tasks;
    
    namespace Tasks
    {
        class Program
        {
            static async Task Main(string[] args)
            {
                // Так
                await Task.Run(async () =>
                {
                    while (true)
                    {
                        await Task.Delay(1000);
                        Console.Write("=");
                    }
                });
    
                // Или так (обрати внимание на Unwrap()) 
                await Task.Factory.StartNew(async () =>
                {
                    while (true)
                    {
                        await Task.Delay(1000);
                        Console.Write("=");
                    }
                }).Unwrap();
            }
        }
    }
    Ответ написан
    1 комментарий
  • Не могу понять, как работает async?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Конкретно по вопросу: файл открыт в другой программе, скорее всего, на запись в том числе. Логично, что нельзя этот файл ещё раз открыть на запись.
    Конструктор FileStream, используемый в вашем коде FileAccess.ReadWrite:
    public FileStream(string path, FileMode mode) :
                this(path, mode, (mode == FileMode.Append ? FileAccess.Write : FileAccess.ReadWrite), DefaultShare, DefaultBufferSize, DefaultIsAsync)
            { }


    Попробуйте такой код. Он тоже выдаст такую же ошибку, если файл попробовать таким способом открыть только в своём приложении:
    using (FileStream source = new FileStream("NLog.config", FileMode.Open))
    using (FileStream source2 = new FileStream("NLog.config", FileMode.Open))
    using (FileStream target = new FileStream("newPath", FileMode.OpenOrCreate))
    {
        await source.CopyToAsync(target);
    }


    Стоит почитать книгу "Конкурентность в C#. Асинхронное, параллельное и многопоточное программирование"
    Скриншот сделан с видео: https://youtu.be/lh8cT6qI-nA?t=1123

    5fb13cbe569ab013358287.jpeg
    Ответ написан
    Комментировать
  • Как вывести из json только определенные элементы?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Берёшь свой запрос. Открываешь программу Postman. Отправляешь запрос. Копируешь полученный JSON в буфер обмена. Переходишь на какой-нибудь сайт вроде https://json2csharp.com/ и вставляешь туда JSON. Копируешь полученные классы, правильно именуешь. Параметры генерации классов:
    5fe665235f0a1045486034.png

    Кстати, для поискового запроса (текста) можно попробовать использовать Uri.EscapeUriString

    Подключение быстрого сериализатора System.Text.Json из .NET Core

    RestSharpExample.csproj
    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>netcoreapp3.1</TargetFramework>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="RestSharp" Version="106.11.7" />
        <PackageReference Include="RestSharp.Serializers.SystemTextJson" Version="106.11.7" />
      </ItemGroup>
    
    </Project>


    Api/Kinopoisk/KinopoiskService.cs
    using System.Threading.Tasks;
    using System.Web;
    using RestSharp;
    using RestSharp.Serializers.SystemTextJson;
    using RestSharpExample.Api.Kinopoisk.Model;
    
    namespace RestSharpExample.Api.Kinopoisk
    {
        public class KinopoiskService
        {
            private const string API = "https://api.kinopoisk.cloud/";
            private const string TOKEN = "7777";
    
            private readonly RestClient _client;
    
            public KinopoiskService()
            {
                _client = new RestClient(API);
                _client.UseSystemTextJson();
            }
    
            public async Task<SearchResult> SearchAsync(string movieTitle)
            {
                var request = new RestRequest($"movies/search/{HttpUtility.UrlEncode(movieTitle)}/token/{TOKEN}", Method.GET);
                var response = await _client.ExecuteAsync<SearchResult>(request);
                return response.Data;
            }
        }
    }


    Program.cs
    using System;
    using System.Linq;
    using System.Threading.Tasks;
    using RestSharpExample.Api.Kinopoisk;
    using RestSharpExample.Api.Kinopoisk.Model;
    
    namespace RestSharpExample
    {
        class Program
        {
            private KinopoiskService _kinopoisk;
    
            #region Entry point
    
            static async Task Main(string[] args)
            {
                var program = new Program();
                await program.Run(args);
            }
    
            #endregion
    
            private async Task Run(string[] args)
            {
                _kinopoisk = new KinopoiskService();
    
                try
                {
                    string searchRequest = "Дурак";
                    SearchResult result = await _kinopoisk.SearchAsync(searchRequest);
                    Console.WriteLine($"Результаты поиска по запросу \"{searchRequest}\":");
                    if (result.Results.Any())
                    {
                        Display(result.Results.First());
                    }
                    else
                    {
                        Console.WriteLine("Ничего не найдено.");
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
            }
    
            private void Display(SearchMovieInfo info)
            {
                Console.WriteLine($"ИД: {info.Id}, Название: {info.Title}, Год: {info.Year}");
            }
        }
    }


    Api/Kinopoisk/Model/SearchResult.cs
    using System.Collections.Generic;
    using System.Text.Json.Serialization;
    
    namespace RestSharpExample.Api.Kinopoisk.Model
    {
        public class SearchResult
        {
            [JsonPropertyName("results")]
            public List<SearchMovieInfo> Results { get; set; }
    
            [JsonPropertyName("pagination")]
            public Pagination Pagination { get; set; }
        }
    }


    Api/Kinopoisk/Model/SearchMovieInfo.cs
    using System.Text.Json.Serialization;
    
    namespace RestSharpExample.Api.Kinopoisk.Model
    {
        public class SearchMovieInfo
        {
            [JsonPropertyName("id")]
            public string Id { get; set; }
    
            [JsonPropertyName("id_kinopoisk")]
            public int IdKinopoisk { get; set; }
    
            [JsonPropertyName("title")]
            public string Title { get; set; }
    
            [JsonPropertyName("description")]
            public string Description { get; set; }
    
            [JsonPropertyName("year")]
            public int Year { get; set; }
    
            [JsonPropertyName("poster")]
            public string Poster { get; set; }
    
            [JsonPropertyName("rating_kinopoisk")]
            public double? RatingKinopoisk { get; set; }
    
            [JsonPropertyName("rating_imdb")]
            public double? RatingImdb { get; set; }
        }


    Api/Kinopoisk/Model/Pagination.cs
    using System.Text.Json.Serialization;
    
    namespace RestSharpExample.Api.Kinopoisk.Model
    {
        public class Pagination
        {
            [JsonPropertyName("current_page")]
            public string CurrentPage { get; set; }
    
            [JsonPropertyName("end_page")]
            public string EndPage { get; set; }
    
            [JsonPropertyName("total_pages")]
            public int TotalPages { get; set; }
        }
    }
    Ответ написан
    Комментировать
  • WPF vs UWP в 2021?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    • Между UWP и WPF есть ещё отличие в жизненном цикле приложения. UWP приложение не совсем тоже самое, что и WPF.
    • В UWP преобладает страничный режим. Там всё построено на переходах между страницами.
    • Приложения UWP устанавливаются из Microsoft Store.
    • Приложения UWP требуют включения разных разрешений для приложения.

    5fe41c979dcc6193298625.png
    Ответ написан
    5 комментариев
  • Почему в WPF не работает перенаправление на другое окно?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Посмотри рабочий пример реализации здесь:
    Как осуществить переход между страницами в Windows Presentation Foundation?

    У тебя просто открыто окно и в нём вызывается работа сервиса. Это не означает, что MainWindow закроется и откроется новое окно. Нужно использовать или Frame или NavigationWindow (справка поддерживает русский язык, смени en-us на ru-ru).
    Ответ написан
    Комментировать
  • С# используется больше для разработки игр или приложений?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    WPF (Windows Presentation Foundation)
    UWP (Universal Windows Platform)
    ASP.NET Core (в общем и целом)
    ASP.NET Core Blazor
    ASP.NET Core MVC
    ML.NET
    Unity
    Я в этот список не включаю всякие Entity Framework Core и подобные технологии. Там ещё есть чем список пополнить.

    Достаточно посмотреть вакансии на разных сайтах. В основном, по моему субъективному мнению это ASP.NET и всякие другие консольные утилиты, которые работают в качестве серверов. Понятно, что все вышеперечисленные технологии используются тоже. На моей работе C# используется для UI приложения, для пары тройки серверов и для машинного обучения (в разных отделах) и много всего другого на разных языках.

    Писать приложения можно под все популярные ОС (Linux, MacOS, Windows)
    Ответ написан
    Комментировать
  • Вывести записи из БД циклом с фильтром по массиву?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Нужно сформировать одну строку вида "@record1, @record2" (параметризованные запросы), а потом уже передать в качестве параметров нужные значения.

    SELECT * FROM testimony WHERE Name IN(@record1, @record2)


    Примерно так (код не запускал, по быстрому набросал). Да, код можно написать лучше, это просто пример для понимания идеи. StringBuilder вместо string здесь приведёт к просадке производительности, малые строки быстрее так складывать:
    string sql = "SELECT * FROM testimony WHERE Name IN({0})";
    string parameters = "";
    string[] array = new string[] { "Запись 1", "Запись 2" };
    for (int i = 0; i < array.Length; i++)
    {
        parameters += "@record" + (i + 1) + (i < array.Length - 1 ? ", ": "");
    }
    
    using (SqlCommand cmd = new SqlCommand(string.Format(sql, parameters), this.connect))
    {
        // Добавить параметры
        for (int i = 0; i < array.Length; i++)
        {
            cmd.Parameters.AddWithValue("@record" + (i + 1), array[i]);
        }
    
        cmd.ExecuteNonQuery();
    }
    Ответ написан
    9 комментариев
  • Как взаимодействовать с элементом внутри DataTemplate?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Можно ещё сделать свой UserControl, в который поместить Ellipse и всю логику модификации его внешнего вида в XAML. Но нужно ли оно? Думаю, нет. Способов много это сделать.

    <DataGrid
        AutoGenerateColumns="False"
        ItemsSource="{Binding Income}">
        <DataGrid.Columns>
            <DataGridTemplateColumn
                CanUserReorder="False"
                CanUserResize="False"
                CanUserSort="False"
                IsReadOnly="True">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate DataType="viewModels:IncomeViewModel">
                        <Ellipse Fill="{Binding IsModified, Converter={StaticResource BooleanToBrushConverter}}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
    </DataGrid>


    using GalaSoft.MvvmLight;
    
    namespace WpfApp.ViewModels
    {
        public class IncomeViewModel : ViewModelBase
        {
            private bool _isModified;
    
            public bool IsModified
            {
                get { return _isModified; }
                set { Set(ref _isModified, value); }
            }
        }
    }


    using System;
    using System.Globalization;
    using System.Windows.Data;
    using System.Windows.Media;
    
    namespace WpfApp.Converters
    {
        [ValueConversion(typeof(bool), typeof(SolidColorBrush))]
        public class BooleanToBrushConverter : IValueConverter
        {
            public SolidColorBrush TrueBrush { get; set; } = new SolidColorBrush(Colors.Tomato);
    
            public SolidColorBrush FalseBrush { get; set; } = new SolidColorBrush(Colors.CornflowerBlue);
    
            public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return (bool)value ? TrueBrush : FalseBrush;
            }
    
            public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
            {
                return Binding.DoNothing;
            }
        }
    }
    Ответ написан
  • Как осуществить ввод и вывод данных WPF?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Обновлено:
    Ответ не совcем верный в том плане, что здесь нужно, как сейчас видится четыре DataGrid'a. Где у вас написано Зарплата, Сумма, Аренда, Сумма это всё данные одного DataGrid должны быть.
    5fc24d309b48a634078946.png
    *.csproj
    <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
    
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>netcoreapp3.1</TargetFramework>
        <UseWPF>true</UseWPF>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="MvvmLightLibsStd10" Version="5.4.1.1" />
      </ItemGroup>
    
    </Project>


    <DataGrid
        Grid.Row="1"
        Grid.Column="1"
        AutoGenerateColumns="False"
        ItemsSource="{Binding Income}">
        <DataGrid.Columns>
            <DataGridTextColumn
                Width="67"
                Binding="{Binding Title}"
                Header="Title" />
            <DataGridTextColumn
                Width="67"
                Binding="{Binding Sum}"
                Header="Sum" />
        </DataGrid.Columns>
    </DataGrid>


    using System.Collections.ObjectModel;
    using GalaSoft.MvvmLight;
    
    namespace WpfApp1.ViewModels
    {
        public class MainViewModel : ViewModelBase
        {
            public ObservableCollection<IncomeViewModel> Income { get; }
    
            public MainViewModel()
            {
                Income = new ObservableCollection<IncomeViewModel>();
                // Просто для теста заполняем
                Income.Add(new IncomeViewModel { Title = "Зарплата", Sum = 100_000m });
            }
        }
    
        /// <summary>
        /// Модель представления одной строки в таблице Доход.
        /// </summary>
        public class IncomeViewModel : ViewModelBase
        {
            private string _title;
            private decimal _sum;
    
            public string Title
            {
                get { return _title; }
                set { Set(ref _title, value); }
            }
    
            public decimal Sum
            {
                get { return _sum; }
                set { Set(ref _sum, value); }
            }
        }
    }


    -------------

    У каждого DataGrid задайте свойства
    AutoGenerateColumns="False"
    ItemsSource="{Binding Elements}"


    Где Elements - это у каждого DataGrid своя коллекция данных во вью модели (Названия, понятное дело у них будут разные).

    Ещё можно задать в DataGrid'ах, где очень много данных (VirtualizingStackPanel):
    <DataGrid
        AutoGenerateColumns="False"
        ItemsSource="{Binding Elements}">
        <DataGrid.ItemsPanel>
            <ItemsPanelTemplate>
                <VirtualizingStackPanel
                    IsVirtualizing="True"
                    VirtualizationMode="Recycling" />
            </ItemsPanelTemplate>
        </DataGrid.ItemsPanel>
    </DataGrid>
    Ответ написан
    Комментировать
  • Как правильно реализовать информирование ViewModel'и по MVVM?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Читай комментарии в коде, в том числе зачем там lock. Вдруг ты будешь использовать такой поиск с отменой не в UI потоке (не именно этот код, а саму логику).

    Models/SearchAlgorithm.cs
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace EventsInModel.Models
    {
        public class SearchAlgorithm
        {
            public string CurrentFolder { get; private set; }
    
            public event EventHandler ProgressChanged;
    
            public async Task Search(CancellationToken cancellationToken)
            {
                for (int i = 0; i < 5; i++)
                {
                    await Task.Delay(1200, cancellationToken);
    
                    cancellationToken.ThrowIfCancellationRequested();
    
                    // Можно прогресс передавать и в качестве аргумента события,
                    // но в данном случае, вряд ли это оправдано. Обработав событие можно получить
                    // доступ к отправителю события и прочитать его свойства.
                    CurrentFolder = i.ToString();
                    ProgressChanged?.Invoke(this, EventArgs.Empty);
                }
            }
        }
    }


    ViewModels/MainViewModel.cs
    using System;
    using System.Threading;
    using System.Windows.Input;
    using EventsInModel.Models;
    using GalaSoft.MvvmLight;
    using GalaSoft.MvvmLight.Command;
    
    namespace EventsInModel.ViewModels
    {
        // ViewModelBase из библиотеки MvvmLight
        public class MainViewModel : ViewModelBase
        {
            //private readonly object _sync = new object();
            private readonly SearchAlgorithm _search;
    
            private string _currentFolder;
            // Логику с отменой можно вынести в отдельный класс, чтобы не писать простыню
            // с отменой в каждом таком месте с операцией, которая может быть отменена, а 
            // в UI приложениях такое сплошь и рядом.
            private volatile CancellationTokenSource _lastCancellationTokenSource;
    
            public string CurrentFolder
            {
                get { return _currentFolder; }
                private set { Set(ref _currentFolder, value); }
            }
    
            public ICommand SearchCommand { get; }
    
            public MainViewModel(SearchAlgorithm search)
            {
                _search = search;
                _search.ProgressChanged += OnSearchProgressChanged;
                SearchCommand = new RelayCommand(Search);
            }
    
            public override void Cleanup()
            {
                //lock (_sync)
                {
                    _lastCancellationTokenSource?.Cancel();
                }
    
                _search.ProgressChanged -= OnSearchProgressChanged;
                base.Cleanup();
            }
    
            /// <summary>
            /// Прерывает прошлый поиск и запускает новый.
            /// </summary>
            private async void Search()
            {
                CancellationTokenSource currentTokenSource;
                // В случае, если такой метод вызывать не из UI потока, то lock здесь нужен
                // Если использовать только из UI потока как здесь, то lock можно удалить.
                // Ещё бы я вынес логику в отдельный класс и использовал в других проектах в том числе.
                //lock (_sync)
                {
                    _lastCancellationTokenSource?.Cancel();
                    currentTokenSource = new CancellationTokenSource();
                    _lastCancellationTokenSource = currentTokenSource;
                }
    
                try
                {
                    await _search.Search(currentTokenSource.Token);
                }
                catch (OperationCanceledException)
                {
                    // Ignored.
                }
                finally
                {
                    //lock (_sync)
                    {
                        currentTokenSource.Dispose();
                        if (ReferenceEquals(_lastCancellationTokenSource, currentTokenSource))
                        {
                            _lastCancellationTokenSource = null;
                        }
                    }
                }
            }
    
            private void OnSearchProgressChanged(object sender, EventArgs e)
            {
                var search = (SearchAlgorithm)sender;
                CurrentFolder = search.CurrentFolder;
            }
        }
    }


    Затестил я по быстрому так. Это тебе не особо нужно:

    MainWindow.xaml.cs
    using System.Windows;
    using EventsInModel.Models;
    using EventsInModel.ViewModels;
    
    namespace EventsInModel
    {
        public partial class MainWindow : Window
        {
            private readonly MainViewModel _viewModel;
    
            public MainWindow()
            {
                InitializeComponent();
                _viewModel = new MainViewModel(new SearchAlgorithm());
                DataContext = _viewModel;
    
                Loaded += OnLoaded;
                Closing += OnClosing;
            }
    
            private void OnLoaded(object sender, RoutedEventArgs e)
            {
                _viewModel.SearchCommand.Execute(null);
                _viewModel.SearchCommand.Execute(null);
            }
    
            private void OnClosing(object sender, System.ComponentModel.CancelEventArgs e)
            {
                _viewModel.Cleanup();
            }
        }
    }


    Проект с зависимостями
    <Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
    
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>netcoreapp3.1</TargetFramework>
        <UseWPF>true</UseWPF>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="MvvmLightLibsStd10" Version="5.4.1.1" />
      </ItemGroup>
    
    </Project>
    Ответ написан
    1 комментарий
  • Как сделать цикл на основе ответа на Web запрос в C#?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Данный пример хорош тем, что ты можешь очень быстро создать ещё хоть 10 разных методов для обращения к API сервера. Часть кода была взята со справки майкрософта, частично модифицирована (где XML комментарии на английском).

    HttpClientExample.csproj
    <Project Sdk="Microsoft.NET.Sdk">
    
      <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>netcoreapp3.1</TargetFramework>
      </PropertyGroup>
    
      <ItemGroup>
        <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
      </ItemGroup>
    
    </Project>


    Точка входа в приложение и основной алгоритм по получению данных в цикле.
    Program.cs
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace HttpClientExample
    {
        class Program
        {
            // В качестве исключения я расположил в самом верху класса метод Main, 
            // обычно здесь должны быть поля класса, а все методы ниже.
            #region Entry point
    
            static async Task Main(string[] args)
            {
                var program = new Program();
                await program.Run(args);
            }
    
            #endregion
    
            private readonly SomeClient _client;
    
            public Program()
            {
                _client = new SomeClient("http://localhost:5000");
            }
    
            private async Task Run(string[] args)
            {
                bool success = false;
                do
                {
                    try
                    {
                        // CancellationToken пригодится в приложениях с UI, где нужно, например, 
                        // закрыть окно или уйти со страницы не дожидаясь, пока запрос отработает. 
                        // Здесь заранее это заложено, можно и не использовать, если приложение консольное.
                        string data = await _client.GetData(CancellationToken.None);
                        success = true;
    
                        Console.WriteLine(data);
                    }
                    catch (ApiException ex)
                    {
                        // Одна реакция
                        Console.WriteLine(ex);
                        Console.WriteLine();
                    }
                    catch (Exception ex)
                    {
                        // Другая реакция
                        Console.WriteLine(ex);
                        Console.WriteLine();
                    }
    
                    await Task.Delay(150);
                } while (!success);
    
                _client.Dispose();
            }
        }
    }


    SomeClient.cs
    using System;
    using System.Collections.Generic;
    using System.Net;
    using System.Net.Http;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using Newtonsoft.Json;
    
    namespace HttpClientExample
    {
        public class SomeClient : IDisposable
        {
            private const string GET_TEXT_PART = "/api/system/get-text";
            private const string GET_USER_PART = "/api/system/get-user";
    
            private HttpClient _httpClient;
    
            public Dictionary<string, string> DefaultHeaders { get; }
    
            public SomeClient(string baseAddress)
            {
                var httpHandler = new HttpClientHandler
                {
                    AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
                };
    
                _httpClient = new HttpClient(httpHandler)
                {
                    BaseAddress = new Uri(baseAddress)
                };
    
                DefaultHeaders = new Dictionary<string, string>
                {
                    ["Accept"] = "*/*",
                    ["Accept-Encoding"] = "gzip, deflate",
                    ["Cache-Control"] = "no-cache",
                    ["Connection"] = "keep-alive",
                };
            }
    
            public void Dispose()
            {
                if (_httpClient != null)
                {
                    _httpClient.Dispose();
                    _httpClient = null;
                    GC.SuppressFinalize(this);
                }
            }
    
            public async Task<string> GetData(CancellationToken cancellationToken)
            {
                string text = await InvokeText(HttpMethod.Get, GET_TEXT_PART, cancellationToken);
                // Возможно, что-то залогировал.
                return text;
            }
    
            public async Task<User> GetUser(CancellationToken cancellationToken)
            {
                var user = await InvokeJson<User>(HttpMethod.Get, GET_USER_PART, cancellationToken);
                // Возможно, что-то залогировал.
                return user;
            }
    
            /// <summary>
            /// Sets the request.
            /// </summary>
            /// <param name="request">The request.</param>
            /// <param name="requestContent">Content of the request.</param>
            private void SetRequest(HttpRequestMessage request, object requestContent)
            {
                foreach (var header in DefaultHeaders)
                {
                    request.Headers.Add(header.Key, header.Value);
                }
    
                if (requestContent != null)
                {
                    request.Content = new StringContent(JsonConvert.SerializeObject(requestContent),
                        Encoding.UTF8,
                        Constants.HttpMimeTypes.JsonContentType);
                }
            }
    
            /// <summary>
            /// Invokes the specified HTTP method.
            /// </summary>
            /// <param name="httpMethod">The HTTP method.</param>
            /// <param name="relativeUrl">The relative URL.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            /// <param name="requestContent">Content of the request.</param>
            /// <returns>instance of the type T</returns>
            /// <exception cref="ApiException"></exception>
            private async Task<string> InvokeText(HttpMethod httpMethod, string relativeUrl, CancellationToken cancellationToken, object requestContent = null)
            {
                using (var request = new HttpRequestMessage(httpMethod, relativeUrl))
                {
                    SetRequest(request, requestContent);
    
                    using (HttpResponseMessage response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken))
                    {
                        string responseText = await response.Content.ReadAsStringAsync();
                        if (response.IsSuccessStatusCode)
                        {
                            return responseText;
                        }
    
                        throw new ApiException(response.StatusCode, responseText);
                    }
                }
            }
    
            /// <summary>
            /// Invokes the specified HTTP method.
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="httpMethod">The HTTP method.</param>
            /// <param name="relativeUrl">The relative URL.</param>
            /// <param name="cancellationToken">Cancellation token.</param>
            /// <param name="requestContent">Content of the request.</param>
            /// <returns>instance of the type T</returns>
            /// <exception cref="ApiException"></exception>
            private async Task<T> InvokeJson<T>(HttpMethod httpMethod, string relativeUrl, CancellationToken cancellationToken, object requestContent = null)
            {
                using (var request = new HttpRequestMessage(httpMethod, relativeUrl))
                {
                    SetRequest(request, requestContent);
    
                    using (HttpResponseMessage response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken))
                    {
                        string responseText = await response.Content.ReadAsStringAsync();
                        if (response.IsSuccessStatusCode)
                        {
                            var resource = JsonConvert.DeserializeObject<T>(responseText);
                            return resource;
                        }
    
                        throw new ApiException(response.StatusCode, responseText);
                    }
                }
            }
    
            private static class Constants
            {
                public static class HttpMimeTypes
                {
                    /// <summary>
                    /// The json content type
                    /// </summary>
                    public const string JsonContentType = "application/json";
                }
            }
        }
    }


    Этот класс используется просто для примера, как преобразовать JSON, полученный от сервера в экземпляр класса
    User.cs
    using Newtonsoft.Json;
    
    namespace HttpClientExample
    {
        public class User
        {
            [JsonProperty("id", Required = Required.Always)]
            public long Id { get; set; }
    
            [JsonProperty("name", Required = Required.Always)]
            public string Name { get; set; }
        }
    }


    async await, чтобы было примепрное понимание, что происходит.
    Скриншот сделан с видео: https://youtu.be/lh8cT6qI-nA?t=1123
    5fb13cbe569ab013358287.jpeg
    Ответ написан
    Комментировать
  • С помощью чего отправлять почту в Asp.Net Core 3.1?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Есть такой вариант: How to send emails in ASP.NET Core

    www.mimekit.net

    В описании по ссылке написано:
    Run Anywhere
    MimeKit and MailKit support Windows, Linux, Mac, iOS, Android, Windows Phone and more!
    Ответ написан
    2 комментария
  • С# вместо Python - стоит ли усложнять?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Я пишу на C# и писал немного на Python. Мне второй понравился тем, что там можно быстро писать всякие небольшие программы. Быстро что-то набросать. Есть много разных книг по машинному обучению. Ну и как новое после C# было интересно учить. С одной стороны динамическая типизация - это удобно, с другой можно наворотить страшные вещи. Я за строгую типизацию. В таких проектах легче разбираться, легче поддерживать, рефакторить и т.д. Я за C#. Бэкенд на C# тоже можно писать весьма приличный. На C# можно писать под Linux, MacOS и Windows. Скорость разработки упадёт или нет это ещё спорный вопрос, смотря как код писать, если на Python чисто копипастить - за то быстро, то я бы посмотрел на выражение лица того, кто этот проект будет поддерживать. Я хотел сказать, что на любом языке можно написать плохо и динамическая типизация не спасёт от того, что человек будет зависать на частях проекта, что-то менять и ломать другую часть проекта. Со строгой типизацией и средствами рефакторинга вроде JetBrains ReSharper (в JetBrains Rider это сразу встроено) в C# проект не поломаешь так легко, а если поломаешь, то он просто не соберётся, пока не поправишь все ошибки, ведь типизация статическая и сразу будут видны все нестыковки. Вообще странное утверждение, что под Windows хорош C#. Он хорош под всё. На Python, какое-то время назад было больше библиотек для науки, для машинного обучения и т.д. сейчас уже много чего для этих дел есть и у C# (там, кстати, есть и сравнение с Python библиотеками - раздел High performance and accuracy). Ну и сам язык развивается, постоянно появляются новые крутые фишки в нём. Так же есть ещё и F#, почти Python, но не так популярен и распространён.

    Среды разработки:
    * Microsoft Visual Studio - под Windows
    * JetBrains Rider - под всё
    Ответ написан
    7 комментариев
  • Задать текст вместо картинки в MenuItem?

    Casper-SC
    @Casper-SC
    Программист (.NET)
    Тебе нужно на меню (MenuItem выберется, если меню не пустое) навести курсор мыши и нажать правую кнопку мыши, выбрать EditTemplate -> Edit a copy. Создастся стиль с шаблонами.

    Там ты увидишь такой код
    <ControlTemplate
        x:Key="{ComponentResourceKey ResourceId=SubmenuItemTemplateKey,
                        TypeInTargetAssembly={x:Type MenuItem}}">
    <!-- Много кода -->
    </ControlTemplate>

    Ищи там

    Это та штука, которая отображает иконку. Как видно, она отображает то, что задано в свойстве Icon (см. ContentSource="Icon").
    <ContentPresenter
        x:Name="Icon"
        Width="16"
        Height="16"
        Margin="3"
        HorizontalAlignment="Center"
        VerticalAlignment="Center"
        ContentSource="Icon"
        SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />


    Задай свойству Width значение побольше. Margin слева и/или справа можешь уменьшить, если по дизайну это норм.
    Как видно, этот ContentPresenter отображает то, что содержится в свойстве Icon у MenuItem. Просто в Icon задай нужный текст.

    Вертикальная черта в том шаблоне это
    <Rectangle
        Width="1"
        Margin="29,2,0,2"
        HorizontalAlignment="Left"
        Fill="{StaticResource Menu.Static.Separator}" />


    <Menu
        Grid.Row="0"
        Height="25"
        VerticalAlignment="Top"
        DockPanel.Dock="Top">
        <MenuItem Style="{StaticResource MenuItemStyle}" Header="Тесты">
            <MenuItem
                Icon="101"
                Command="{Binding WorkspaceViewModel.UpdateTestsCommand}"
                Header="Обновить список тестов"
                IsEnabled="{Binding Mode, Converter={StaticResource ApplicationModeEnumToBooleanConverter}, ConverterParameter='Tests'}" />
             <!-- -->
        </MenuItem>
        <MenuItem Header="Режим">
             <!-- -->
        </MenuItem>
    </Menu>


    5f999711c2b74120986977.png
    Ответ написан