Задать вопрос
@kaka888
C, C++, Qt, Python, Flask, aiogram, MySQL, Redis..

Почему ругается на команду Exit в CommandBinding?

Решил добавить пару горячих клавиш в приложении на C# WPF, в том числе Ctrl+W для закрытия приложения. C# ругается только на эту команду...

При запуске проекта в режиме отладки вываливается исключение:
System.Windows.Markup.XamlParseException: ""Предоставление значения для "System.Windows.Baml2006.TypeConverterMarkupExtension" вызвало исключение.": номер строки "9" и позиция в строке "10"."

Внутреннее исключение
NotSupportedException: CommandConverter cannot convert from System.String.


XAML:
<Window x:Class="WpfTextEditor.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="WPF Text Editor" Height="600" Width="900"
        Closing="Exit_Handler">
    <Window.CommandBindings>
        <CommandBinding Command="Save" Executed="Save_Handler"/>
        <CommandBinding Command="Open" Executed="OpenFile_Handler"/>
        <CommandBinding Command="Exitfoo" Executed="Exit_Handler"/>
        <CommandBinding Command="SaveAs" Executed="SaveAs_Handler"/>
    </Window.CommandBindings>
    <Window.InputBindings>
        <KeyBinding Command="Save" Key="S" Modifiers="Ctrl"/>
        <KeyBinding Command="Open" Key="O" Modifiers="Ctrl"/>
        <KeyBinding Command="Exitfoo" Key="W" Modifiers="Ctrl"/>
        <KeyBinding Command="SaveAs" Key="S" Modifiers="Ctrl+Shift"/>
    </Window.InputBindings>
...


C#:
using Microsoft.Win32;
using System;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using Spire.Doc;
using Spire.Doc.Documents;
using TextAlignment = System.Windows.TextAlignment;
using Paragraph = System.Windows.Documents.Paragraph;
using Hyperlink = System.Windows.Documents.Hyperlink;
using System.ComponentModel;
using System.Windows.Controls.Primitives;
using System.Windows.Threading;
using NLog;

namespace WpfTextEditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private TextPointer _lastSearchPosition;
        private double _defaultEditorFontSize;
        private bool _hasUnsavedChanges = false;
        private string _currentFileName = "";
        private bool _isFirstEvent_Editor_TextChanged = true;
        private bool _intervalSaving = false;
        private DispatcherTimer _intervalSavingTimer = new DispatcherTimer();
        private static readonly Logger _logger = LogManager.GetCurrentClassLogger();

        public MainWindow()
        {
            InitializeComponent();
            _lastSearchPosition = Editor.Document.ContentStart;
            _defaultEditorFontSize = Editor.Document.FontSize;
            _intervalSavingTimer.Interval = TimeSpan.FromMinutes(1);
            _intervalSavingTimer.Tick += IntervalSavingTimerHandler;
            _logger.Info("WpfTextEditor started");
        }

        private void Exit()
        {
            if (_hasUnsavedChanges)
            {
                MessageBoxResult result = MessageBox.Show(
                    $"Сохранить изменения в файле {_currentFileName}?",
                    "Сохранение",
                    MessageBoxButton.YesNo);

                if (result == MessageBoxResult.No)
                {
                    return;
                }
                else if (result == MessageBoxResult.Yes)
                {
                    Save();
                }
            }
            _logger.Info("WpfTextEditor exited");
        }

        private void Exit_Handler(object sender, CancelEventArgs e)
        {
            Exit();
        }

        private void Exit_Handler(object sender, ExecutedRoutedEventArgs e)
        {
            Exit();
        }
...


Из-за чего ругается и как это исправить?
  • Вопрос задан
  • 71 просмотр
Подписаться 1 Простой Комментировать
Помогут разобраться в теме Все курсы
  • Skillfactory
    Профессия C#-разработчик
    12 месяцев
    Далее
  • Merion Academy
    C# разработчик с нуля
    4 месяца
    Далее
  • Stepik
    PRO C#. Профессия "Backend разработчик"
    4 месяца
    Далее
Решения вопроса 1
VoidVolker
@VoidVolker Куратор тега C#
Dark side eye. А у нас печеньки! А у вас?
Ну, очевидно же, что вот на эту вашу фантазию:

Command="Exitfoo"

Даже сама студия прям сразу же пишет в редакторе, что данное значение тут недопустимо.
RTFM:
  1. Windows Desktop / WPF/ Advanced / Commanding Overview: https://learn.microsoft.com/en-us/dotnet/desktop/w...
  2. Далее смотрим что это за свойство — CommandBinding.Command Property: https://learn.microsoft.com/en-us/dotnet/api/syste...
  3. И далее открываем список допустимых значений данного свойства — ApplicationCommands Class: https://learn.microsoft.com/en-us/dotnet/api/syste...

Кроме того, ваш хоткей Ctrl+W для выхода из приложения является контр-интуитивным и использовать такой хоткей для выхода из приложения нельзя, т.к. этот хоткей традиционно используется для закрытия вкладок. Кроме того, винда имеет стандартный хоткей Alt+F4 для закрытия окна или выхода из приложения.

Для тех, кто в танке или кому просто интересно, объясню почему ошибка происходит в конвертере. А происходит следующее: парсер после разбора XML, видит значение в поле Command — т.к. это всё XML, то там строка и из этой строки надо сделать конвертацию в тот тип, который указан в классе CommandBinding для свойства Command, а именно в тип, реализующий интерфейс ICommand.
У класса Window через цепочку наследования в предках есть класс UIElement, который и реализует свойство привязки команд через коллекцию:
public class System.Windows.UIElement : Visual, IInputElement, IAnimatable
{
    public CommandBindingCollection CommandBindings { get; }
}

И соответственно тип в этой коллекции:
public class System.Windows.Input.CommandBinding
{
    public ICommand Command { get; set; }
}

И вот дальше в работу включаются конвертеры. Есть список конвертеров, а в каждом конвертере есть методы, которые сообщают что во что они могут конвертировать. И парсер по очереди опрашивает эти самые конвертеры. И вот какой-то конвертер как раз умеет конвертировать из строки в нужный тип, который и реализует интерфейс ICommand, а именно в класс System.Windows.Input.RoutedUICommand(src). Соответственно список этих команд находится в статическом классе System.Windows.Input.ApplicationCommands, среди которых и происходит поиск нужной команды. Костыльно? Да, костыльно, ну хотя бы работает.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы