public override Base CreateCopy()
public override Derivative CreateCopy()
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}";
}
}
}
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);
#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);
}
#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;
};
}
<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>
#pragma once
#include <collection.h>
#include <windows.h>
#include "App.xaml.h"
#include "pch.h"
Как организовать постоянное обновление DataGrid в отдельном потоке, чтобы при этом DataGrid был доступен, т.к. планируется добавить возможность выбирать строку из DataGrid и смотреть подробную информацию.
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();
}
}
}
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);
}
<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>
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;
}
}
}
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}");
}
}
}
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; }
}
}
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; }
}
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; }
}
}
SELECT * FROM testimony WHERE Name IN(@record1, @record2)
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();
}
<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;
}
}
}
<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); }
}
}
}
AutoGenerateColumns="False"
ItemsSource="{Binding Elements}"
<DataGrid
AutoGenerateColumns="False"
ItemsSource="{Binding Elements}">
<DataGrid.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel
IsVirtualizing="True"
VirtualizationMode="Recycling" />
</ItemsPanelTemplate>
</DataGrid.ItemsPanel>
</DataGrid>
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);
}
}
}
}
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;
}
}
}
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>
<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>
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();
}
}
}
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";
}
}
}
}
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; }
}
}
Run Anywhere
MimeKit and MailKit support Windows, Linux, Mac, iOS, Android, Windows Phone and more!
<ControlTemplate
x:Key="{ComponentResourceKey ResourceId=SubmenuItemTemplateKey,
TypeInTargetAssembly={x:Type MenuItem}}">
<!-- Много кода -->
</ControlTemplate>
<ContentPresenter
x:Name="Icon"
Width="16"
Height="16"
Margin="3"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ContentSource="Icon"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<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>