❗️Внимание. Для тех, кто разрабатывает проекты используя WPF.
XAML Behaviors для WPF теперь Open Source.
В прошлом году мы открыли исходный код XAML Behaviors для UWP, который имел большой успех, и пакет Behaviors NuGet был загружен более 500 000 раз. XAML Behaviors для WPF теперь поставляется в виде пакета NuGet — Microsoft.Xaml.Behaviors.Wpf. Подробнее под катом!
Это позволит быстрее добавлять новые фичи и исправлять ошибки. Когда новый Behavior или фича добавляется в репозиторий, их можно будет использовать практически сразу. Открытость для исправлений позволяет платформе Behaviors развиваться, предоставляя сообществу возможность задавать темп и направление. Хотя вы можете продолжать использовать Extension SDK, дальнейшая разработка будет осуществляться только на GitHub и будет опубликована в пакете NuGet в новом пространстве имен Microsoft.Xaml.Behaviors.
ссылка на полную статью.
#Csharp,#WPF,#XAML
XAML Behaviors для WPF теперь Open Source.
В прошлом году мы открыли исходный код XAML Behaviors для UWP, который имел большой успех, и пакет Behaviors NuGet был загружен более 500 000 раз. XAML Behaviors для WPF теперь поставляется в виде пакета NuGet — Microsoft.Xaml.Behaviors.Wpf. Подробнее под катом!
Это позволит быстрее добавлять новые фичи и исправлять ошибки. Когда новый Behavior или фича добавляется в репозиторий, их можно будет использовать практически сразу. Открытость для исправлений позволяет платформе Behaviors развиваться, предоставляя сообществу возможность задавать темп и направление. Хотя вы можете продолжать использовать Extension SDK, дальнейшая разработка будет осуществляться только на GitHub и будет опубликована в пакете NuGet в новом пространстве имен Microsoft.Xaml.Behaviors.
ссылка на полную статью.
#Csharp,#WPF,#XAML
Хабр
XAML Behaviors для WPF теперь Open Source
Рады сообщить, что совсем недавно XAML Behaviors для WPF стал Open Source. В прошлом году мы открыли исходный код XAML Behaviors для UWP, который имел большой успех, и пакет Behaviors NuGet был...
Пример использования HttpClient для работы с запросами:
1. Подключаем пространство имён.
2. Создаём класс.
3. Создаем новый экземпляр класса.
4. Устанавливаем свойства текущ.-их методов.
5. Создаём асинхронный метод(для того, чтобы не блокировать основной поток).
5.1. Создаем новый экземпляр класса.
5.2. Добавляем тип контента(Content-Type) в заголовок.
5.3. Делаем асинхронный запрос к серверу.
5.4. Проверяем полученный код статуса запроса.
5.5. Читаем полученный ответ.
5.6. Устанавливаем значения из Json в объект класса Poster.
5.7. Получаем значение одного из свойств объекта класса Poster.
#HttpClient, #Csharp
1. Подключаем пространство имён.
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using System.IO;
2. Создаём класс.
public class Poster
{
public int Id { get; set; }
public int MovieId { get; set; }
public string Name { get; set; }
}
3. Создаем новый экземпляр класса.
private static HttpClient _httpClient = new HttpClient();
4. Устанавливаем свойства текущ.-их методов.
_httpClient.BaseAddress = new Uri("http://localhost:987456");
_httpClient.Timeout = new TimeSpan(0, 0, 30);
_httpClient.DefaultRequestHeaders.Clear();
5. Создаём асинхронный метод(для того, чтобы не блокировать основной поток).
private async Task<string> GetResourceThroughHttpRequestMessage()
{
5.1. Создаем новый экземпляр класса.
var request = new HttpRequestMessage(
HttpMethod.Get,
$"api/books/6e0drwqt/posters/{Guid.NewGuid()}");
5.2. Добавляем тип контента(Content-Type) в заголовок.
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
5.3. Делаем асинхронный запрос к серверу.
using (var response = await _httpClient.SendAsync(request))
{
5.4. Проверяем полученный код статуса запроса.
response.EnsureSuccessStatusCode();
5.5. Читаем полученный ответ.
var stream = await response.Content.ReadAsStreamAsync();
using (var streamReader = new StreamReader(stream))
{
using (var jsonTextReader = new JsonTextReader(streamReader))
{
var jsonSerializer = new JsonSerializer();
5.6. Устанавливаем значения из Json в объект класса Poster.
var poster = jsonSerializer.Deserialize<Poster>(jsonTextReader);
5.7. Получаем значение одного из свойств объекта класса Poster.
return poster.Name;
}
}
}
}
#HttpClient, #Csharp
This media is not supported in your browser
VIEW IN TELEGRAM
Для того, чтобы использовать в пространстве имён библиотеку Newtonsoft.Json(как пример), необходимо её установить.
Установка пакета(ов) будем производить, через NuGet (систему управление пакетами).
Задача довольно простая и тривиальная, но многие новички теряются и не знают, что и как делать.
#Csharp,#видео
Установка пакета(ов) будем производить, через NuGet (систему управление пакетами).
Задача довольно простая и тривиальная, но многие новички теряются и не знают, что и как делать.
#Csharp,#видео
Необходимо помнить, что повторные Http запросы надо блокировать т.к. это может привести к ошибкам, а именно "могут исчерпаться все доступные TCP соединения"
1. Подключаем пространство имён.
2. Создаём асинхронный метод(что это такое, очень хорошо написано, здесь).
2.1. Создаём экземпляра класса.
2.2. Обязательно цикл делать внутри конструкции using(для
того, чтобы повторные запросы происходили, через 1 TCP
соединение)
2.3. Делаем запрос.
2.4. Читаем полученные данные.
#Csharp,#HTTP
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, который представляет собой элемент с вложенной структурой.
Пример:
Рекомендую разбивать подготовительный этап на определённые шаги.
1. Подключение пространство имён.
2. Создание объектов классов.
3. Создаём метод, который возвращает экземпляр объект класса с необходимыми заголовками.
4. Создаём обязательно асинхронный метод, который возвращает метод класса.
4.1. Переменные которые хотим передать.
4.2. Передадим значения переменных в наш объект класса.
4.3. Создадим Json из объекта класса.
4.4. Установим формат нашего тело запроса.
4.5. Делаем запрос.
4.6. Читаем полученный ответ от сервера.
4.7. Конвертируем полученный ответ в Json и передаём его в наш объект класса.
4.8. Получаем необходимый метод с нашего объекта класса.
Пример показывает работу с вложенной структурой Json и Http запросами, которые нам надо передать.
#Csharp,#HTTP
Пример:
{
"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).
2. При создание проекта WPF автоматически создаётся файл MainWindow.xaml с разметкой(это, то что у меня):
2.1. В этот код добавляем:
- элемент компоновки "StackPanel",
- текстовый элемент "TextBlock",
- элемент кнопки "Button"
Продолжение следует...
#Рефлексия, #Csharp, #WPF
Звучит довольно интересно и интригующе, в практике позволяет:
- перечислять члены типа
- извлекать информацию о типе
- извлекать информацию о сборке
- исследовать пользовательские атрибуты
Надо понимать, что это всё применимо для общедоступных (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.Подключаем пространство имён.
3. Метод который описывает действие, которое происходит при нажатие на кнопку(которую создали в прошлый раз).
3.1. Имя нашего текстового блока, который расположен в
нашей разметке.
3.2. Поучаем экземпляр класса Туре, в котором прописываем
"полное имя типа(нашего класса) в строковом
представлении".
3.3. Устанавливаем размер шрифта, для нашего текстового
блока.
3.4. Получаем разную информацию о классе с помощью
методов класса Type.
3.5. Получаем информацию о методах, которые существуют в
этом классе.
Рассмотрел простейшие примеры, но уже и они показывают возможности рефлексии, которые помогают нам узнать некоторые вещи.
#Рефлексия, #Csharp
Рассматриваем проект, который реализовали ранее.
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) - этот метод в качестве аргументов, кроме строки с датой, принимает еще строку в которой указан формат представления даты, а так же строку с указанием культуры, в которой принято такое представление.
Пример:
Пример:
Поэтому будет разбивать на определённые этапы:
1. Создаёт список с типом данных 'DateTime':
3. Конвертируем и сохраняем полученное время в ранее созданный список 'listTimeTemp':
#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).
2. Создаём кодировку base64 из нашей картинки, как строку(тип данных string).
2.1. Рекомендую использовать оператор "using" - для того, чтобы правильно освобождать,сбрасывать,удалять неуправляемые ресурсы(аналог метода Dispose)
2.2. Получаем нашу картинку из файла.
{
2.3. Для чтения/записи данных в память, в виде массива, используя класс MemoryStream.
{
2.4. Сохраняем нашу картинку в экземпляр класса MemoryStream.
}
}
3. Полученную строку base64String, уже можем передавать, сохранять и делать с ней что угодно.
3.1. Сервис для просмотра полученной картинки из кодировки base64(тип данных string)
Продолжение следует....
#Csharp, #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.
2. Получаем массив из переменной base64StringNew.
3. Используем оператор "using"(рекомендую) для работы с картинкой.
3.1. Создаём картинку из полученного массива и сохраняем её, согласно нашего пути (pathFileNew).
Все эти случаи в практике используются довольно часто: сервисы для решения каптч, создание сайтов, работа с Б.Д.
Кодирование и декодирование в base64 - это очень полезная вещь.
#Csharp, #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 файла, который реализовал т.е. интерфейс самой программы.
Основной код:
В дальнейшем опишу, почему использовал эти блоки и некоторую логику самого приложения.
В результате создания приложения этот код будет корректироваться.
#WPF, #Csharp
Планирую написать ряд статей по созданию приложения, которое отсылает текстовые сообщения в ТГ(телеграм), используя его 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. Это часть кода генерируется автоматически в которой объявляются пространства имён.
Мне интересна, в данном случае, вот эта строка, где указываем размеры формы приложения:
2. Контейнер DockPanel - его очень удобно использовать для создания стандартных интерфейсов.
Верхнюю и левую часть - можно использовать для меню.
Нижнюю часть - для отображение какой-то доп информации(чаще строка состояния, в сайтостроении называют также эту часть "подвал").
Правую часть - для отображения дополнительной части.
Центр - здесь находится основное содержание.
Свойство
3. Элемент управления Menu (выделяю его специально, т.к. в него входят другие элементы) - который расположен в верхней части нашей DockPanel панели.
3.1.
3.2. Свойство
3.3. Элемент
3.4. Событие
Продолжение следует....
#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. Часть кода, который буду рассматривать.
2. Элемент управления ListView(главный элемент в нашем интерфейсе), расположен в левой части интерфейса
2.1. Короткий пример(добавление "Заголовков " столбцов т.е. "Headers"):
2.2. Элемент управления TextBlock - служит для вывода текстовой информации и имеет свойство
3. Переходим к след. элементу управления StatusBar (главный элемент в нашем интерфейсе), располагается в нижней части интерфейса
Интересный момент: Многие элементы управления имеют ограничения в оформлении и для того, чтобы убрать их необходимо использовать другие элементы, которые будут вложены в текущие.
3.1. Обратимся к свойству ItemsPanel у элемента StatusBar т.е.
3.2. Далее обратимся к элементу ItemsPanelTemplate, который будет в какой-то степени отвечать за оформления нашего StatusBar.
Элемент ItemsPanelTemplate, необходимо будет использовать довольно часто при работе с WPF.
3.3. В ItemsPanelTemplate добавляем такой элемент управления, как Grid
Grid - ЭТО мощный и часто используемый контейнер, напоминающий обычную таблицу. Прошу запомнить это т.к. без него интерфейс напоминал "беспорядочность".
3.3.1. Элемент Grid имеет два интересных свойств:
а)
б)
На этом всё.
Продолжение следует.....
#WPF, #Csharp
В предыдущем посте остановился на рассмотрении элемента управления "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.
Пример кода:
Очень удобное вещь, особенно если работаете также в многопоточном режиме. Советую это сохранить и использовать.
#Csharp, #File
Работают два разных приложения:
Одно создаёт текстовый файл и записывает в него содержимое.
Второе читает это файл.
Проблема заключается в том, что первое приложение не успевает записывать в него информацию, как тут же второе его считывает, получая при этом ошибку "Файл пуст".
Решение для этой проблемы весьма простое:
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"), то рекомендуется комбинировать эти размеры по необходимости.
Завершаем знакомство с элементами ItemsPanelTemplate и StatusBar.ItemsPanel - которые отвечали за основное оформление(т.е. своего рода "заготовки") и перехожу к след.-му.
Здесь появляется новый элемент StatusBarItem. Если прошлые элементы StatusBar.ItemsPanel и ItemsPanelTemplate служили нам заготовкой(разметкой), то уже этот служит нам для заполнения нужной информацией.
Каждый элемент размещается в своей колонке, привязка к определенной колонке происходит таким образом: Grid.Column="1" , где "1" это номер нашей колонки. Таким образом добавив нужные элементы(TextBlock и Separator) и привязав их к нужным колонкам у нас получится готовый StatusBar.
Продолжение следует.....
#WPF,#Csharp
В прошлой статье остановился на рассмотрение такого элемента как 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 и некоторых его свойств.
Свойства:
AcceptsReturn="True" - служит для того, чтобы переводить по нажатию на клавишу Enter курсор на следующую строку.
VerticalScrollBarVisibility="Auto" и HorizontalScrollBarVisibility="Auto" - служит для отображения полос прокрутки TextBox поддерживает свойства.
Элемент: Button - где ему дал имя в свойстве: x:Name="buttonSentMessage"(это имя будет использоваться в коде).
Надпись(содержание), которая отображается на самой кнопке находится в свойстве: Content="Отправить".
Событие: Click="Button_Click" с собственным названием.
В коде будет примерно так(при нажатие на эту кнопку, выскакивает сообщение "Кнопка была нажата!"):
На этом шаге с оформлением останавливаюсь.
Продолжение следует.....
#WPF,#Csharp
В текущем коснусь такого элемента, как 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 запросами и не только:
#LINQ , #CSharp
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 ключевых момента, на которые необходимо сделать акцент.
1.Обязательно конвертировать фото в base64
2.Обязательно создать экземпляр класса StringContent
3.Обязательно загружать фото используя экземпляр класса MultipartFormDataContent
Вроде всего 3 акцента, но о них нигде и ничего не пишется на их странице, особенно о Multipart.
#API, #POST, #GET, #CSharp
Выделяю 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