Ежедневник IT-ка
130 subscribers
24 photos
4 videos
1 file
44 links
Программируем на практике!!
По вопросам по поводу проектов: @vladimir_dzen
Download Telegram
​​Необходимо помнить, что повторные Http запросы надо блокировать т.к. это может привести к ошибкам, а именно "могут исчерпаться все доступные TCP соединения"

1. Подключаем пространство имён.

using System.Net.Http;
using System.Threading.Tasks;

static async Task Main(string[] args)
{
  await ReuseQuery();
}

2. Создаём асинхронный метод(что это такое, очень хорошо написано, здесь).

    public static async Task<string> ReuseQuery()
    {
      string result = string.Empty;

2.1. Создаём экземпляра класса.

      using (var client = new HttpClient())
      {

2.2. Обязательно цикл делать внутри конструкции using(для
того, чтобы повторные запросы происходили, через 1 TCP
соединение)

        for (int i = 0; i < 5; i++)
        {
          var request = new HttpRequestMessage(HttpMethod.Get,
            "google.com");

2.3. Делаем запрос.

          using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
          {

2.4. Читаем полученные данные.

            result = await response.Content.ReadAsStringAsync();
            response.EnsureSuccessStatusCode();
          }
        }
      }
      return result;
    }
  }

#Csharp,#HTTP
​​Часто бывают случаи необходимо работать с Json, который представляет собой элемент с вложенной структурой.

Пример:

{
 "clientKey": "fdfggghhttyydshkahue26",
 "task": {
  "cookies": "",
  "type": "NoCaptchaTaskProxyless",
  "websiteURL": "https://www.somesite/ru/login",
  "websiteKey": "6LcMERGSGSGSHHHS-KcbYQMd-wcLt6ight7u"
  }
}

Рекомендую разбивать подготовительный этап на определённые шаги.

1. Подключение пространство имён.

using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.IO;
using System.Text;

2. Создание объектов классов.

  public class CreateTaskRecaptcha
  {
    public string clientKey { get; set; }
    public DataForCreateTaskRecaptcha task { get; set; }
  }
  public class DataForCreateTaskRecaptcha
  {
    public string cookies { get; set; }
    public string type { get; set; }
    public string websiteURL { get; set; }
    public string websiteKey { get; set; }
  }
  public class ResultRecaptcha
  {
    public int errorId { get; set; }
    public string status { get; set; }
    public int taskId { get; set; }
    public string errorDescription { get; set; }
  }

3. Создаём метод, который возвращает экземпляр объект класса с необходимыми заголовками.

    public static HttpClient CreateHttpClient()
    {
      var client = new HttpClient();
      client.Timeout = new TimeSpan(0, 0, 30);
      client.DefaultRequestHeaders.Clear();
      client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36");
      client.DefaultRequestHeaders.Add("Accept", "*/*");
      client.DefaultRequestHeaders.Add("Accept-Language", "ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7");
      client.DefaultRequestHeaders.Add("Connection", "keep-alive");
      return client;
    }

4. Создаём обязательно асинхронный метод, который возвращает метод класса.

    private async Task<int> GetPostThroughHttpRequest(HttpClient client)
    {

4.1. Переменные которые хотим передать.

      string websiteURL = "https://www.google.com/";
      string websiteKey = "6LcMERGSGSGSHHHS-KcbYQMd-wcLt6ight7u";
      string clientKey = "fdfggghhttyydshkahue26";
      string uri = "https://somesite.com/auth/login";

4.2. Передадим значения переменных в наш объект класса.

      var dict = new CreateTaskRecaptcha
      {
        task = new DataForCreateTaskRecaptcha
        {
          cookies = "",
          type = "NoCaptchaTaskProxyless",
          websiteURL = websiteURL,
          websiteKey = websiteKey
        },
        clientKey = clientKey
      };

4.3. Создадим Json из объекта класса.

      string bodyJson = JsonConvert.SerializeObject(dict);

4.4. Установим формат нашего тело запроса.

      var body = new StringContent(bodyJson, Encoding.UTF8, "application/json");

4.5. Делаем запрос.

      var response = await client.PostAsync(uri, body);

4.6. Читаем полученный ответ от сервера.

      string content = await response.Content.ReadAsStringAsync();

4.7. Конвертируем полученный ответ в Json и передаём его в наш объект класса.

      var jsonResult = JsonConvert.DeserializeObject<ResultRecaptcha>(content);
      if (jsonResult.errorId > 0) Console.WriteLine(jsonResult.errorDescription);

4.8. Получаем необходимый метод с нашего объекта класса.

      return jsonResult.taskId;
    }

Пример показывает работу с вложенной структурой Json и Http запросами, которые нам надо передать.

#Csharp,#HTTP
​​Рефлексия - это процесс, который позволяет программе изучать саму себя.
Звучит довольно интересно и интригующе, в практике позволяет:
- перечислять члены типа
- извлекать информацию о типе
- извлекать информацию о сборке
- исследовать пользовательские атрибуты
Надо понимать, что это всё применимо для общедоступных (public) методов из заданного типа.

Рассмотрим простой пример(реализован в WPF приложение):

1. Создаём объект класса(это отдельный файл с расширением .cs).

namespace HumanizerDemo
{
  public class Binance
  {
    public int BalanceUSD { get; set; }
    public int BalanceBTC { get; set; }
    public Binance(int n, int a)
    {
      BalanceUSD = n;
      BalanceBTC = a;
    }

    public int CalculateCount(int countUsd, int priceBtc)
    {
      return countUsd / priceBtc;
    }
  }
}

2. При создание проекта WPF автоматически создаётся файл MainWindow.xaml с разметкой(это, то что у меня):

<Window x:Class="HumanizerDemo.MainWindow"
    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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:HumanizerDemo"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">
  <Grid>

  </Grid>
</Window>

2.1. В этот код добавляем:

- элемент компоновки "StackPanel",
- текстовый элемент "TextBlock",
- элемент кнопки "Button"

  <Grid>
    <StackPanel>
      <Button Name="GetHumanMethodNames" Click="GetHumanMethodNames_OnClick">Получение методов</Button>
      <TextBlock Name="Output"></TextBlock>
    </StackPanel>
  </Grid>

Продолжение следует...

#Рефлексия, #Csharp, #WPF
​​Продолжаем знакомство с рефлексией, и будем получать информацию о классе и о методах в нём.

Рассматриваем проект, который реализовали ранее.

1. Открываем файл "MainWindow.xaml.cs", который был создан автоматически.

2.Подключаем пространство имён.

using System;
using System.Reflection;
using System.Windows;

3. Метод который описывает действие, которое происходит при нажатие на кнопку(которую создали в прошлый раз).

    private void GetHumanMethodNames_OnClick(object sender, RoutedEventArgs e)
    {

3.1. Имя нашего текстового блока, который расположен в
нашей разметке.

      Output.Text = "";

3.2. Поучаем экземпляр класса Туре, в котором прописываем
"полное имя типа(нашего класса) в строковом
представлении".

var infoAboutClass = Type.GetType("HumanizerDemo.Binance",false,true);

3.3. Устанавливаем размер шрифта, для нашего текстового
блока.

      Output.FontSize = 16;

3.4. Получаем разную информацию о классе с помощью
методов класса Type.

      Output.Text += "Получаем разную информацию о классе с помощью методов класса Type";
      Output.Text += Environment.NewLine;
      Output.Text += Environment.NewLine;
      Output.Text += "Полное Имя: " + infoAboutClass.FullName;
      Output.Text += Environment.NewLine;
      Output.Text += "Базовый класс: " + infoAboutClass.BaseType.ToString();
      Output.Text += Environment.NewLine;
      Output.Text += "Абстрактный: " + infoAboutClass.IsAbstract.ToString();
      Output.Text += Environment.NewLine;
      Output.Text += "Запрещено наследование: " + infoAboutClass.IsSealed.ToString();
      Output.Text += Environment.NewLine;
      Output.Text += "class: " + infoAboutClass.IsClass.ToString();
      Output.Text += Environment.NewLine;
      Output.Text += "Публичный: " + infoAboutClass.IsPublic.ToString();
      Output.Text += Environment.NewLine;
      Output.Text += Environment.NewLine;

3.5. Получаем информацию о методах, которые существуют в
этом классе.

      MethodInfo[] mi = infoAboutClass.GetMethods(BindingFlags.Instance
          | BindingFlags.Static
          | BindingFlags.Public
          | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);

      Output.Text += "Получаем информацию о методах";
      Output.Text += Environment.NewLine;
      Output.Text += Environment.NewLine;
      foreach (MethodInfo imi in mi)
      {
        Output.Text += "Полное Имя: " + imi.Name.ToString();
        Output.Text += Environment.NewLine;
      }
    }

Рассмотрел простейшие примеры, но уже и они показывают возможности рефлексии, которые помогают нам узнать некоторые вещи.

#Рефлексия, #Csharp
Продолжаем работать со временем.
В прошлом примере, был получен список 'lstTemp' с временем.
Список выглядит так:

23 часов 58 минут,
23 часов 53 минут,
19 часов 9 минут,
23 часов 57 минут,
и т.д.

Наша цель преобразовать время, которое сейчас является типом данных "string" в тип данных "DateTime".
В C# существуют 2 метода(самые популярные), которые пытаются конвертировать "string" => "DateTime". Рассмотрим их более подробно.

1. DateTime.ParseExact(string s, string format, IFormatProvider? provider) - этот метод в качестве аргументов, кроме строки с датой, принимает еще строку в которой указан формат представления даты, а так же строку с указанием культуры, в которой принято такое представление.
Пример:

string time = "2021 Четверг октябрь 28 16:01";
System.Globalization.CultureInfo culture = System.Globalization.CultureInfo.CreateSpecificCulture("ru-RU");
DateTime dt = DateTime.ParseExact(time, "yyyy dddd MMMM dd HH:mm", culture);

Результат: 23.06.2020 14:35:00

2.DateTime.Parse(string s) - этот метод в качестве аргумента принимает строку с датой.
Пример:

string time = "23:58";
DateTime dt = = DateTime.Parse(time);

Результат: 28.10.2021 23:58:00

Методы существуют, но в нашем случае они не подходят т.к. не смогут распознать формат такого времени: "23 часов 58 минут".

Поэтому будет разбивать на определённые этапы:
1. Создаёт список с типом данных 'DateTime':

var listTimeTemp = new List<DateTime>();

2. Подготовим время в нормальный формат с помощью регулярного выражения, который поймёт метод: DateTime.Parse(string s):

tmpData = Regex.Replace(tmpData, @"ча.*?\s", ":");
tmpData = Regex.Replace(tmpData, @"мин.*", "");

после чистки, время уже будет иметь вот такой формат: "23:58"

3. Конвертируем и сохраняем полученное время в ранее созданный список 'listTimeTemp':

listTimeTemp.Add(DateTime.Parse(tmpData));

4. Сортируем полученное время в порядке возрастания с помощью 'Linq':

listTimeTemp.Sort((a, b) => a.CompareTo(b));

5. Создаёт список с типом данных 'double':

var totalMinutes = new List<double>();

6. Создаём цикл для списка 'listTimeTemp'( для того, чтобы получить минуты из часов и минуты из минут, соответственно. Формат времени из которого берутся эти данные:'28.10.2021 23:58:00'(пример)):

foreach (var hours in listTimeTemp)
{
int minutesFromHours = hours.Hour * 60;
int minutesFromMinutes = hours.Minute;
totalMinutes.Add(minutesFromHours + minutesFromMinutes);
}

7. Получаем среднюю сумму значения всех полученных минут из списка 'totalMinutes'.

var sum = totalMinutes.Sum() / listTimeTemp.Count;

8. Создаём объект 'TimeSpan', для того, чтобы из средней суммы, которую получили выше, получить часы и минуты:

TimeSpan ts = TimeSpan.FromMinutes(sum);
int midHours = ts.Hours;
int midMinutes = ts.Minutes;

9. Пример результата:

string res = ts.Hours.Tostring() + ":" + ts.Minutes.Tostring();

"22:52" - наше полученное искомое среднее время.

10. Пример всего кода:

var tmpData = string.Empty;
var listTimeTemp = new List<DateTime>();
for (x = 0; x < lstTemp.Count; x++)
{
tmpData = lstTemp[x].Trim();
tmpData = Regex.Replace(tmpData, @"ча.*?\s", ":");
tmpData = Regex.Replace(tmpData, @"мин.*", "");
listTimeTemp.Add(DateTime.Parse(tmpData));
}
listTimeTemp.Sort((a, b) => a.CompareTo(b));

var totalMinutes = new List<double>();
foreach (var hours in listTimeTemp)
{
int minutesFromHours = hours.Hour * 60;
int minutesFromMinutes = hours.Minute;
totalMinutes.Add(minutesFromHours +
minutesFromMinutes);
}
var sum = totalMinutes.Sum() / listTimeTemp.Count;
TimeSpan ts = TimeSpan.FromMinutes(sum);
int midHours = ts.Hours;
int midMinutes = ts.Minutes;
string res = ts.Hours.Tostring() + ":" + ts.Minutes.Tostring();

В этом примере рассмотрел те вещи, которые можно делать используя 'DateTime' и 'TimeSpan'.

#Csharp, #Дата
​​Необходимо передавать картинку Post/Get запросами. Подумав, а почему не передавать с помощью кодировки base64?

Пример: №1 (картинку в base64):

1. Путь к нашей картинке и переменную для нашей кодировки base64(тип данных string).

string pathFile = "D:\1_picture.jpeg";
string base64String = null;

2. Создаём кодировку base64 из нашей картинки, как строку(тип данных string).

2.1. Рекомендую использовать оператор "using" - для того, чтобы правильно освобождать,сбрасывать,удалять неуправляемые ресурсы(аналог метода Dispose)

2.2. Получаем нашу картинку из файла.

using (Image image = Image.FromFile(pathFile)
{
2.3. Для чтения/записи данных в память, в виде массива, используя класс MemoryStream. 

using (MemoryStream m = new MemoryStream())
{
2.4. Сохраняем нашу картинку в экземпляр класса MemoryStream.

image.Save(m, image.RawFormat);

2.5. Получаем массив для дальнейшей конвертации.

byte[] imageBytes = m.ToArray();

2.6. Получаем кодировку картинки в base64.

base64String = Convert.ToBase64String(imageBytes);
}
}

3. Полученную строку base64String, уже можем передавать, сохранять и делать с ней что угодно.

3.1. Сервис для просмотра полученной картинки из кодировки base64(тип данных string)

Продолжение следует....

#Csharp, #base64

Пример кодировки.
​​В предыдущем примере рассмотрел случай кодирования картинку в base64.

Теперь рассмотрим обратный случай.

Пример: №2 (base64 в картинку):

1. Инициализируем новые переменные: путь к нашей картинке и кодировку base64.

string pathFileNew = "D:\1_pictureCopy.jpeg";
string base64StringNew = base64String(полученная кодировка base64 из пред. примера);

2. Получаем массив из переменной base64StringNew.

byte[] imgBytes = Convert.FromBase64String(base64StringNew);

3. Используем оператор "using"(рекомендую) для работы с картинкой.

using (var imageFile = new FileStream(pathFileNew, FileMode.Create))
{

3.1. Создаём картинку из полученного массива и сохраняем её, согласно нашего пути (pathFileNew).

imageFile.Write(imgBytes, 0, imgBytes.Length);
imageFile.Flush();
}

Все эти случаи в практике используются довольно часто: сервисы для решения каптч, создание сайтов, работа с Б.Д.

Кодирование и декодирование в base64 - это очень полезная вещь.

#Csharp, #base64

Результаты кодирования и декодирования.
​​Продолжаю писать статьи связанные с созданием приложения используя WPF. Даю ссылку на свой прошлый пост, где касался слегка этой темы.

Планирую написать ряд статей по созданию приложения, которое отсылает текстовые сообщения в ТГ(телеграм), используя его API.

Почему? Потому что, написания приложений используя API ТГ в "тренде" и полагаю он ещё будет долгое время.

В этой части статьи покажу весь код XAML файла, который реализовал т.е. интерфейс самой программы.

Основной код:

<Window x:Class="TelegramBot.MainWindow"
    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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:TelegramBot"
    mc:Ignorable="d"
    Title="MainWindow" Height="150" Width="800">
  <DockPanel LastChildFill="True">
<Menu DockPanel.Dock="Top"> 
      <MenuItem Header="Данные">
        <MenuItem Header="Сохранить"
             Command=""/>
        <Separator/>
        <MenuItem Header="Выход"
             Click="MenuItem_Click"/>
      </MenuItem>
    </Menu>
    <ListView DockPanel.Dock="Left"
         Background="MidnightBlue"
         Width="140">
      <TextBlock Text="Новое сообщение:" Foreground="White"/>
    </ListView>
    <StatusBar DockPanel.Dock="Bottom">
      <StatusBar.ItemsPanel>
        <ItemsPanelTemplate>
          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="100" />
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
          </Grid>
        </ItemsPanelTemplate>
      </StatusBar.ItemsPanel>
      <StatusBarItem>
        <TextBlock Text="Статус:" />
      </StatusBarItem>
      <Separator Grid.Column="1" />
      <StatusBarItem Grid.Column="2">
        <TextBlock Text="Не отправлено" />
      </StatusBarItem>
      <Separator Grid.Column="3" />
      <StatusBarItem Grid.Column="4">
        <TextBlock Text="Кол-во знаков:" />
      </StatusBarItem>
      <Separator Grid.Column="5" />
      <StatusBarItem Grid.Column="6">
        <TextBlock Text="259" />
      </StatusBarItem>
    </StatusBar>
    <Grid>
        <Grid.RowDefinitions>
          <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="5*"/>
        <ColumnDefinition Width="*"/>
      </Grid.ColumnDefinitions>
      <TextBox Grid.Column="0" AcceptsReturn="True" VerticalScrollBarVisibility="Auto"
     HorizontalScrollBarVisibility="Auto"/>
    <Button Grid.Column="1" x:Name="buttonSentMessage" Width="60" Height="30" Content="Нажать" Click="Button_Click" />
    </Grid>
  </DockPanel>
</Window>

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

В результате создания приложения этот код будет корректироваться.

#WPF, #Csharp
​​Рассматриваю более детальнее код, который опубликовал в прошлой статье.

1. Это часть кода генерируется автоматически в которой объявляются пространства имён.

<Window x:Class="TelegramBot.MainWindow"
  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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  xmlns:local="clr-namespace:TelegramBot"
  mc:Ignorable="d"
  Title="MainWindow" Height="150" Width="800">

Мне интересна, в данном случае, вот эта строка, где указываем размеры формы приложения:

" Title="MainWindow" Height="150" Width="800"

2. Контейнер DockPanel - его очень удобно использовать для создания стандартных интерфейсов.

Верхнюю и левую часть - можно использовать для меню.

Нижнюю часть - для отображение какой-то доп информации(чаще строка состояния, в сайтостроении называют также эту часть "подвал").

Правую часть - для отображения дополнительной части.

Центр - здесь находится основное содержание.

<DockPanel LastChildFill="True">
..................
..................
..................
 </DockPanel>

Свойство LastChildFill="True" означает, что последний элемент(в нашем случае, это текстовое поле "TextBox" и кнопка "Button") заполняет всё оставшееся пространство. 

3. Элемент управления Menu (выделяю его специально, т.к. в него входят другие элементы) - который расположен в верхней части нашей DockPanel панели.

<Menu DockPanel.Dock="Top"> 
   <MenuItem Header="Данные">
    <MenuItem Header="Сохранить"
       Command=""/>
    <Separator/>
    <MenuItem Header="Выход"
       Click="MenuItem_Click"/>
   </MenuItem>
  </Menu>

3.1. <MenuItem Header="Данные"> - это название меню. Оно может состоять из нескольких т.е. :

<MenuItem Header="Данные">
<MenuItem Header="Сохранить">
<MenuItem Header="Выход"">

3.2. Свойство "Command" используется для паттерна MVVM(Model-View-ViewModel). О нём буду говорить, в других постах, если будет необходимо.

3.3. Элемент  <Separator/> - это понятно, "разделитель".

3.4. Событие Click="MenuItem_Click" с присвоенным ему именем. Оно будет обрабатываться в коде.

Продолжение следует....

#WPF, #Csharp
​​Продолжаю знакомить с WPF.
В предыдущем посте остановился на рассмотрении элемента управления "Menu".

В этом рассмотрю сразу несколько элементов управления(кратко) "ListView", "TextBlock", "StatusBar" и "Grid"

1. Часть кода, который буду рассматривать.

<ListView DockPanel.Dock="Left"
         Background="MidnightBlue"
         Width="140">
      <TextBlock Text="Новое сообщение:" Foreground="White"/>
    </ListView>
    <StatusBar DockPanel.Dock="Bottom">
      <StatusBar.ItemsPanel>
        <ItemsPanelTemplate>
          <Grid>
            <Grid.ColumnDefinitions>
              ..................
              ..................
             ..................
            </Grid.ColumnDefinitions>
          </Grid>
        </ItemsPanelTemplate>
      </StatusBar.ItemsPanel>
..................
   ..................
   ..................
    </StatusBar>

2. Элемент управления ListView(главный элемент в нашем интерфейсе), расположен в левой части интерфейса DockPanel.Dock="Left". Не смотря на то, что он унаследован от класса другого элемента ListBox, является более "продвинутым" т.к. ещё можно добавлять "заголовки столбцов" т.е. у него есть свойство "View", которое имеет свой элемент(объект) GridView, который позволяет создавать более сложную структуры таблицы(со своими названиями столбцов)

2.1. Короткий пример(добавление "Заголовков " столбцов т.е. "Headers"):

<ListView.View>
   <GridView>
      <GridViewColumn>№</GridViewColumn>
<GridViewColumn>Кол-во штук</GridViewColumn>
<GridViewColumn>Цена</GridViewColumn>
   </GridView>
</ListView.View>

2.2. Элемент управления TextBlock - служит для вывода текстовой информации и имеет свойство Foreground="White". В котором устанавливается цвет, переднего фона элемента, в нашем случае "цвет текста".

3. Переходим к след. элементу управления StatusBar (главный элемент в нашем интерфейсе), располагается в нижней части интерфейса DockPanel.Dock="Bottom".

Интересный момент: Многие элементы управления имеют ограничения в оформлении и для того, чтобы убрать их необходимо использовать другие элементы, которые будут вложены в текущие.

3.1. Обратимся к свойству ItemsPanel у элемента StatusBar т.е. <StatusBar.ItemsPanel>

3.2. Далее обратимся к элементу ItemsPanelTemplate, который будет в какой-то степени отвечать за оформления нашего StatusBar.

Элемент ItemsPanelTemplate, необходимо будет использовать довольно часто при работе с WPF.

3.3. В ItemsPanelTemplate добавляем такой элемент управления, как Grid

Grid - ЭТО мощный и часто используемый контейнер, напоминающий обычную таблицу. Прошу запомнить это т.к. без него интерфейс напоминал "беспорядочность".

3.3.1. Элемент Grid имеет два интересных свойств:

а) <Grid.ColumnDefinitions> - для работы с размерами столбцов - шириной.
б) <Grid.RowDefinitions> - для работы с размерами столбцов - высотой.

На этом всё.

Продолжение следует.....

#WPF#Csharp
Отойду от темы WPF, и покажу довольно "тривиальную задачу".

Работают два разных приложения:

Одно создаёт текстовый файл и записывает в него содержимое.
Второе читает это файл.

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

Решение для этой проблемы весьма простое:

1. Создаём метод "IsLockedFile" с возвращаемым типом данных bool.

2. Создаём конструкцию try...catch, для того чтобы словить исключение.

3. Добавляем оператор "using" - для того, чтобы правильно освобождать,сбрасывать,удалять неуправляемые ресурсы(аналог метода Dispose)

4. Обращаемся к классу "File" и его методу Open с его параметрами(где задаём enum или ещё называют перечисление: FileMode.Open, FileAccess.Read, FileShare.None) и создаем конструктор класса "FileStream".

5. Если файл успешно открылся и прочитался, то возвращаем false(закрывая при этом объект класса FileStream), если нет то true. И в том и в том случае выходим из метода IsLockedFile.

6. В коде вызываю этот метод передавая путь к файлу. Используя цикл "while" т.е. этот будет работать до тех пор, пока возвращает true.

Пример кода:

string pathFile = "D:\Example.txt";
while (IsLockedFile(pathFile));

public bool IsLockedFile(string fileName)
{
try
{
using (FileStream fs = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
{
fs.Close();
return false;
}
}
catch (Exception ex)
{
return true;
}
return true;
}

Очень удобное вещь, особенно если работаете также в многопоточном режиме. Советую это сохранить и использовать.

#Csharp, #File
​​Продолжаю знакомство с WPF.
В прошлой статье остановился на рассмотрение такого элемента как Grid.

Одно из важных свойств этого элемента(и не только этого) ширина и высота.
Есть несколько вариантов установки размеров:
"Auto" - установка ширины элемента согласно его содержанию.
"100" - фиксированный размер.
"*" - пропорциональный размер(маленький лайфхак, хотите увеличить этот размер в 2 в 3 раза, просто сделайте запись таким образом "2*" или "3*" ).

Для того, чтобы необходимый элемент имел правильные очертания(в моём случае это элемент "Grid"), то рекомендуется комбинировать эти размеры по необходимости.

          <Grid>
            <Grid.ColumnDefinitions>
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="100" />
              <ColumnDefinition Width="Auto" />
              <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
          </Grid>

Завершаем знакомство с элементами ItemsPanelTemplate и StatusBar.ItemsPanel - которые отвечали за основное оформление(т.е. своего рода "заготовки") и перехожу к след.-му.

      <StatusBarItem>
        <TextBlock Text="Статус:" />
      </StatusBarItem>
      <Separator Grid.Column="1" />
      <StatusBarItem Grid.Column="2">
        <TextBlock Text="Не отправлено" />
      </StatusBarItem>
      <Separator Grid.Column="3" />
      <StatusBarItem Grid.Column="4">
        <TextBlock Text="Кол-во знаков:" />
      </StatusBarItem>
      <Separator Grid.Column="5" />
      <StatusBarItem Grid.Column="6">
        <TextBlock Text="259" />
      </StatusBarItem>

Здесь появляется новый элемент StatusBarItem. Если прошлые элементы StatusBar.ItemsPanel и ItemsPanelTemplate служили нам заготовкой(разметкой), то уже этот служит нам для заполнения нужной информацией.

Каждый элемент размещается в своей колонке, привязка к определенной колонке происходит таким образом: Grid.Column="1" , где "1" это номер нашей колонки. Таким образом добавив нужные элементы(TextBlock и Separator) и привязав их к нужным колонкам у нас получится готовый StatusBar.

Продолжение следует.....

#WPF,#Csharp
В прошлом посте про WPF, рассмотрел элемент: StatusBar.
В текущем коснусь такого элемента, как TextBox и некоторых его свойств.

    <Grid>
      <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
      </Grid.RowDefinitions>
      <Grid.ColumnDefinitions>
        <ColumnDefinition Width="5*"/>
        <ColumnDefinition Width="*"/>
      </Grid.ColumnDefinitions>
      <TextBox Grid.Column="0" AcceptsReturn="True" VerticalScrollBarVisibility="Auto"
     HorizontalScrollBarVisibility="Auto"/>
      <Button Grid.Column="1" x:Name="buttonSentMessage" Width="80" Height="30" Content="Отправить" Click="Button_Click" />
    </Grid>

Свойства:
AcceptsReturn="True" - служит для того, чтобы переводить по нажатию на клавишу Enter курсор на следующую строку.
VerticalScrollBarVisibility="Auto" и HorizontalScrollBarVisibility="Auto" - служит для отображения полос прокрутки TextBox поддерживает свойства.

Элемент: Button - где ему дал имя в свойстве: x:Name="buttonSentMessage"(это имя будет использоваться в коде).
Надпись(содержание), которая отображается на самой кнопке находится в свойстве: Content="Отправить".
Событие: Click="Button_Click" с собственным названием.

В коде будет примерно так(при нажатие на эту кнопку, выскакивает сообщение "Кнопка была нажата!"):

private void Button_Click(object sender, RoutedEventArgs e)
{
    MessageBox.Show("Кнопка была нажата!");
}

На этом шаге с оформлением останавливаюсь.

Продолжение следует.....

#WPF,#Csharp
​​Затрону тему с LINQ запросами и не только:

   static void Main(string[] args)
    {
      Console.ForegroundColor = ConsoleColor.Red;
      Console.WriteLine("LINQ");
      // Создание списка с произвольными числами.
      List<int> numbers = new List<int> { 1, 5, 10, 12, -1, -300, 500 };
      Console.WriteLine($"Min число: {numbers.Min()}");
      Console.WriteLine($"Max число: {numbers.Max()}");
      Console.WriteLine($"Кол-во чисел в списке: {numbers.Count()}");
      Console.WriteLine($"Кол-во чисел в списке больше 0: {numbers.Count(x => x > 0)}");
      Console.WriteLine($"Сумма отрицательных чисел:{numbers.Where(x => x < 0).Sum()}");

      Console.ReadLine();

      Console.ForegroundColor = ConsoleColor.Yellow;
      // Генерация чётных чисел.
      Console.WriteLine("Генерация чётных чисел");
      var result = Enumerable.Range(-4, 10).Where(x => x % 2 == 0);

      foreach (var item in result)
      {
        Console.Write(item + "\t");
      }

      Console.ForegroundColor = ConsoleColor.Green;
      // Повторение объекта.
      Console.WriteLine(" ");
      Console.WriteLine(" ");
      Console.WriteLine("Повторение объектов");
      var repeat = Enumerable.Repeat("Привет мир!", 5);

      foreach (var item in repeat)
      {
        Console.WriteLine(item);
      }
      Console.ReadLine();
    }
  }

#LINQ , #CSharp
Столкнулся с задачей для сервиса imgbb.com (загрузка фото), точнее с его API. Вроде всё элементарно, но оказалось чуть сложнее, чем на первый взгляд.

Выделяю 3 ключевых момента, на которые необходимо сделать акцент.

foreach (string pathFile in project.Lists["pathToFoto"])
{
if (!string.IsNullOrEmpty(pathFile.Trim()))
{
string url = $"https://api.imgbb.com/1/upload?key={project.Variables["apiKey"].Value}";
string base64String = null;

1.Обязательно конвертировать фото в base64
using (Image image = Image.FromFile(pathFile))
{
using (MemoryStream m = new MemoryStream())
{
image.Save(m, image.RawFormat);
byte[] imageBytes = m.ToArray();
base64String = Convert.ToBase64String(imageBytes);
}
}
HttpClient client = new HttpClient();

2.Обязательно создать экземпляр класса StringContent
StringContent fotoContent = new StringContent(base64String, Encoding.UTF8);

3.Обязательно загружать фото используя экземпляр класса MultipartFormDataContent
var content = new MultipartFormDataContent();
content.Add(fotoContent, "image");
var result = client.PostAsync(url, content);
int code = result.Result.StatusCode.GetHashCode();
Thread.Sleep(1000);
if(result.Result.StatusCode.GetHashCode() != 200) throw new Exception("Problem WITH LOAD FOTO");
string jsonResult = result.Result.Content.ReadAsStringAsync().Result;
}
}

Вроде всего 3 акцента, но о них нигде и ничего не пишется на их странице, особенно о Multipart.

#API, #POST, #GET, #CSharp
​​Немного предыстории.
Заказчик хотел получать сигналы от биржи в телеграмм используя Emoji.
На первый взгляд, всё просто, если вы знаете как...., но тут явного решения не было, поэтому пришлось напрячься, чтобы найти его.

1. Переходим на сайт с emoji (пример: unicode-table.com)

2. Копируем emoji в Юникоде.

3. Пишем код:

    private static ITelegramBotClient _botClient;

    static void Main(string[] args)
    {

// Читаем данные для нашего бота с ранее подготовленного .json
 BotData botData = GetUserCredential<BotData>();

 // Создаём экземпляр класса TelegramBotClient используя ITelegramBotClient
 _botClient = new TelegramBotClient(botData.Token);

   // Получаем чат ID.
 string chatID = botData.Chat;

  // Эмоджи в Юникоде.
 string emoji = "U+1F600";

  // Парсим в INT эмоджи, но уже "1F600"(т.е. без 1-х 2-х знаков), через HexNumber(шестнадцатеричное число)
 int _emoji = int.Parse(emoji.Substring(2), NumberStyles.HexNumber);

  // Отсылаем сообщение.
 _botClient.SendTextMessageAsync(chatID, "Как прекрасен этот мир! " + char.ConvertFromUtf32(_emoji)).Wait();
}

// Метод получение данных из .json
    private static T GetUserCredential<T>()
    {
      using (var fileStream = new FileStream("TGbot.json", FileMode.Open, FileAccess.Read))
      {
        string contents;
        using (var sr = new StreamReader(fileStream))
        {
          contents = sr.ReadToEnd();
        }
        return JsonConvert.DeserializeObject<T>(contents);
      }
    }

На этом всё. Код работает.

#Telegram, #CSharp
4. В конф. проекта добавляем эту строку:
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
Она необходима для того, чтобы программа запускалась на всех операц. системах Windows.
Подробнее можно посмотреть по ссылке.

 #CSharp, #WPF
Хочу сегодня добавить несколько сниппетов по LINQ запросам:

1. Поиск текста по регулярке, множество совпадений в список:

Regex regexJson = new Regex(@"[a-zA-Z0-9]{20,}.*?}");

List<string> tmpList = regexJson.Matches(result).Cast<Match>().Select(m => m.Value).ToList();

List<string> resultsList = tmpList.Where(s => s.Contains("index") && s.Contains("id") && s.Contains("brandId") && !s.Contains("Отзывы") && !s.Contains("advert")).ToList();

2. Создать список ID(индексы строк), в которых содержатся необходимый ключ(особенность: создание анонимного типа с свойствами(Index и Value)):

tmpList.Select((v, i) => new { Index = i, Value = v })
            .Where(p => p.Value.Contains("brandId")).Select(p => p.Index.ToString()).ToList();

3. Получение суммы из списка, которые имеет тип данных string(List<string>) т.е. необходимо значения string сконвертировать в int:

var sumTempResList = tmpResList.Where(d => !string.IsNullOrEmpty(d.Trim())).Select(s => int.Parse(s.Replace("-",""))).ToList().Sum();

4. Создание Dictionary<string,string> из двух разных Списков(List<string>):

List<string> keysList = new List<string>{"2569746", "9876415", "6957459"};

List<string> valuesList = new List<string>{"val1", "val2", "val3"};

Dictionary<string,string> resDict = keysList.Zip(valuesList, (k, v) => new { k, v }).ToDictionary(x => x.k, x => x.v);

5. Создание списка из двух списков, в которых значения не совпадают(т.е. в одном списке, есть в другом нет и те значения, которых нет в другом списке добавляем в новый третий список):

List<string> artsG = new List<string>{"2569746", "9876415", "6957459", "5947945", "9874647"};

List<string> skues = new List<string>{"2569746", "9876415", "6957459"};

List<string> compR_GBD = artsG.Where(n => !skues.Any(s => s.ToLower() == n.ToLower())).ToList();

Получается новый список "compR_GBD" состоящий из этих двух значений("5947945", "9874647")

#LINQ , #CSharp
​​Многие из вас уже знакомы с chatgpt. Не так давно, необходимо было реализовать задачу, по распознаванию фото Локально.

Работаем через API.
1.) Обращаемся к официальной документации по ссылке:
https://platform.openai.com/docs/api-reference/chat/create
( create chat completion )
2.) Выбираем необходимое меню(как на картинке).
3.) Копируем и корректируем код(т.к. там, только через URL, распознаётся фото), нам же надо, если фото находится локально.
Пример моего Кода(Будьте очень Внимательны при его Использование):

string access_token_chatGpt = project.Variables["access_token_chatGpt"].Value;

//СТАРТ. Чтение файла в котором находится задание для ChatGPT
string pathToFileTaskForDescreptionPicture = project.Variables["pathToFileTaskForDescreptionPicture"].Value;
var taskForDescreptionPicture = string.Join(Environment.NewLine, File.ReadAllLines(pathToFileTaskForDescreptionPicture).ToList().Where(s => !string.IsNullOrEmpty(s)).ToList());
//ФИНИШ.

project.Variables["descreption"].Value = string.Empty;

string url = "https://api.openai.com/v1/chat/completions";

string imagePath = project.Variables["imageFilepath"].Value;

string image_url = imagePath;

string taskFrom = string.Empty;

base64Image = string.Empty;

/*Конвертация локального фото в
необходимую строку для распознавания, вместо адреса URL*/
string base64Image = Convert.ToBase64String(File.ReadAllBytes(imagePath));
image_url = $"data:image/jpeg;base64,{base64Image}";


HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", access_token_chatGpt);
client.DefaultRequestHeaders.Host = "api.openai.com";

var requestData = new
{
model = "gpt-4o",
messages = new []
{
new
{
role = "user",
content = new object[]
{
new
{
type = "text", text = taskForDescreptionPicture,
},
new
{
type = "image_url", 
image_url = new
{
url = image_url,
detail = "high"
}
}
}
}
},
max_tokens = 500
};

string jsonRequest = JsonConvert.SerializeObject(requestData);
var content = new StringContent(jsonRequest, Encoding.UTF8, "application/json");
HttpResponseMessage response = client.PostAsync(url, content).Result;
string responseData = response.Content.ReadAsStringAsync().Result;

if (response.IsSuccessStatusCode)
{
 project.Variables["descreption"].Value = responseData;
}
else
{
project.Variables["batch"].Value = responseData;
throw new Exception("");
}

4.) Получаем результат и радуемся.

P.S. Внимание!! Распознавание через API в разы хуже, чем через WEB.

#chatgpt, #Csharp, #zennoposter
Часто бывает, что есть ключевые слова, которые должны присутствовать в строке, которую анализируем.
Использую такое решение.

1.) Создаю список с "ключевыми словами"
List<string> socialUrls = new List<string>()
{
"youtube.com", "facebook.com", "x.com", "tiktok.com", "onlyfans.com"
};

string url = string.Empty;

2.) Список(результат) в котором содержатся строки, в которых есть необходимы ключи.
List<string> sringWithKeys = new List<string>();

3.) Список(входные данные) строк, которые необходимо Проверить
List<string> valuesOfInnerHtml= project.Lists["valuesOfInnerHtml"].ToList();

3.) Основная логика.
for(int x = 0; x < valuesOfInnerHtml.Count; x++ )
{
url = valuesOfInnerHtml[x];
try
{
url = tab.URL.Replace("&amp;", "&");
// LINQ запрос для проверки ключа в строке из списка
if (socialUrls.Any(url.Contains)) sringsWithKeys .Add(url);

}catch
{
throw new Exception("mistake");
}
}

if (sringsWithKeys .Count == 0) throw new Exception("list is empty");

#LINQ , #CSharp