Ответы пользователя по тегу WPF
  • Почему вьюха не видим вьюмодель в том же неймспейсе?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Да, всё верно поняли. Дизайнер XAML не совсем "дружит" с кодом - он ищет только те, которые уже скомпилированы. Видимо, дизайнер ищет классы стандартным способом - через тип Type (и т.п.), а тот ищет среди сборок (exe/dll) проекта.

    Нужно сразу скомпилировать новый тип, даже просто пустой class Test {} - и всё, будет работать. Лучше делать это пораньше - до того, как пытаетесь связать его в xaml, иначе билд не срабатывает, и приходится выпиливать его из xaml-а.
    Ответ написан
    1 комментарий
  • Как обратиться к ListBox из кода?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Вам не нужно делать кастомный контрол для листбокса, такое поведение делается по другому:
    1) создаётся класс Anime, в котором нужно указать необходимые свойства аниме - Название, рейтинг, автор, серия, ссылка на скачивание и пр.
    2) создаётся вьюмодель окна (или части окна),
    2.1) во вьюмодели нужно сделать коллекцию объектов аниме (можно и List, но лучше ObservableCollection)
    3) вьюмодель указывается в окно как датаконтекст (тут есть несколько вариантов, ниже покажу самый прсотой вариант)
    4) привязать коллекцию аниме к списку ListBox, и указать имя свойства для показа (тут, видимо - Название).
    5) всё, список готов - будет видно в окне. Осталось получить конкретный элемент и его свойства - объект класса Anime указано в свойстве DataContext элемента списка (ListBoxItem) и остаётся привести к нужному классу:
    var anime = (Anime)AnimeListBox.Items.First().DataContext;
    Console.WriteLine(anime.Name + " " + anime.Rating);

    class Anime
    {
        public string Name {get; set;}
        public double Rating {get; set;}
        public string Author {get; set;}
        public string Serie {get; set;}
        public string Url {get; set;}
    }
    class AnimeListViewModel
    {
        public ObservableCollection<Anime> AnimeList {get; set;} = new ObservableCollection<Anime>
        {
            new Anime { Name = "Наруто",  Rating = 5, Author = "Масаси Кисимото"},
            new Anime { Name = "Стальной алхимик",  Rating = 5, Author = "Хирому Аракава"},
            new Anime { Name = "X",  Rating = 5, Author = "CLAMP"},
        };
    }


    <Window ...
        xmlns:app="clr-namespace:Anime">
        <Window.DataContext>
            <app:AnimeListViewModel/>
        </Window.DataContext>
        <Grid>
            <ListBox ItemsSource="{Binding AnimeList}"
                     DisplayMemberPath="Name"/>
                     x:Name="AnimeListBox"/>
        </Grid>
    </Window>
    Ответ написан
    Комментировать
  • Какие есть способы у ViewModel привязать Model ко View?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Модель описывает данные. Представление определяет внешний вид экрана. Вьюмодель - это модель, которая предназначена для использования данных из модели внутри представления, причём во вьюмодели можно добавлять какие-то поля только для удобства представления. Одна вьюмодель вполне может использовать несколько разных моделей.

    Первейший путь для показа данных в представлении - привязка (биндинг) данных, а также - специальные поля контрола (например, DisplayMemberPath).
    Команды используются для обратной связи от пользователя в представлении во вьюмодель.
    Свойства зависимости сами по себе обычно не используются напрямую, а просто добавляют возможностей для биндинга.
    Расширения разметки дают связывать несколько классов в одном контроле.

    В представлении вполне можно обойтись без вьюмодели, используя саму модель (передав её как датаконтекст). Но у модели обычно нет интерфейса INotifyPropertyChanged, и вьюмодель как раз реализует её для использования биндинга, а также добавляет команды и доп.свойства. Для одной модели вполне может быть несколько вьюмоделей, по одному для представлений, или наоборот, одна вьюмодель вполне может подходить для нескольких представлений.

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

    public class Person
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string MiddleName { get; set; }
    }
    public class EditPersonVm : BaseViewModel // который реализует INotifyPropertyChanged
    {
        private readonly Person _person;
        public Person Person => _person;
    
        private string _firstName;
        public string FirstName
        {
            get => _firstName;
            set { _firstName = value; RaisePropertyChanged(); }
        }
        private string _lastName;
        public string LastName 
        {
            get => _lastName;
            set { _lastName= value; RaisePropertyChanged(); }
        }
        private string _middleName;
        public string MiddleName 
        {
            get => _middleName;
            set { _middleName= value; RaisePropertyChanged(); }
        }
    
        public string InitFullName => $"{_person.LastName} {_person.FirstName[0]}. {_person.MiddleName[0]}.";
    
        public RelayCommand OkButton { get; set; } = new RelayCommand(Save);
    
        public EditPersonVm(Person person)
        {
            _person = person;
            FirstName = person.FirstName;
            LastName = person.LastName;
            MiddleName = person.MiddleName;
        }
    
        private void Save()
        {
            //
        }
    }

    <Window ...>
        <StackPanel Orientation="Vertical">
            <Label Content="Изначальное имя:"/>
            <StackPanel>
                <TextBlock Text="{Binding Person.LastName}"/>
                <TextBlock> </TextBlock>
                <TextBlock Text="{Binding Person.FirstName}"/>
                <TextBlock> </TextBlock>
                <TextBlock Text="{Binding Person.MiddleName}"/>
            </StackPanel>
            <Label Content="Фамилия:"/>
            <TextBox Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}"/>
            <Label Caption="Имя:"/>
            <TextBox Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}"/>
            <Label Caption="Отчество:"/>
            <TextBox Text="{Binding MiddleName, UpdateSourceTrigger=PropertyChanged}"/>
            <Button Content="OK"  Command="{Binding OkCommand}">
        </StackPanel>
    </Window>
    Ответ написан
    Комментировать
  • Как выполнить команду по событию KeyDown для TextBox?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Можно использовать конвертер i:InvokeCommandAction из System.Windows.Interactivity.

    1) Добавить сборку System.Windows.Interactivity с помощью добавления ссылки (она в списке расширений).
    5cf0c5efb3bee685207836.png
    2) Добавить указанную сборку в xaml:
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

    3) Использовать i:EventTrigger и i:InvokeCommandAction:
    <TextBox>
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="KeyDown">
                <i:InvokeCommandAction Command="{Binding KeyDownCommand}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </TextBox>


    Но если вы хотите там вызывать только обработку представления, например, запретить определённые клавиши (то есть, не нужны данные из слоя бизнес-логики), то есть смысл обработку сделать обычным способом - в code-behind (xaml.cs).
    Ответ написан
    Комментировать
  • Как сделать привязку данных по выбору?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Можно попробовать сделать третье свойство, которое в геттере будет получать нужное свойство.
    Ответ написан
    6 комментариев
  • Как загрузить UserControl при клике на элемент ListBox?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Вам нужно просто связать датаконтекст групбоксов с выделенным элементом списка. Всё остальное сделает WPF.
    <ListBox ItemsSource="{Binding DevList}" SelectedItem="{Binding SelectedElement}" />
    <GroupBox DataContext="{Binding SelectedElement}">
    </GroupBox>
    <GroupBox DataContext="{Binding SelectedElement.Subelement1}">
    </GroupBox>


    Чтобы это заработало, то нужно сделать так, чтобы DevList был список объектов, а не строк. Создайте класс (вьюмодель) для ваших элементов списка. В нём можно хранить те свойства, которые вы хотите показывать в групбоксах. То есть, в списке элементов хранятся ваши объекты, а потом они же указываются датаконтекстом в другие контролы (групбоксы).

    В таком случае хорошо сделать во вьюмодели реализацию INotifyPropertyChanged.
    Ответ написан
  • C# WPF почему при Window.Left = 0 между границей экрана и окном появляется пустое пространство?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Это хром окна (WindowChrome), обычно выглядит как тень вокруг окна, является частью окна, поверх клиентской частью. Обычно размер хрома 7px.
    https://docs.microsoft.com/ru-ru/dotnet/api/system...

    Хром отключается при указании окна в развёрнутое состояние (Maximized). А также, можно указать WindowStyle.None окну, тогда неклиентская часть отключится.
    Ответ написан
    Комментировать
  • Как привязать данные разного типа к одному элементу управления и выводить в зависимости от определенного условия?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Можно сделать шаблоны DataTemplate, которые изменят шаблон нужного типа:
    <Window x:Class="TestApp.MainWindow"
            xmlns:testApp="clr-namespace:TestApp"
            Title="MainWindow" Height="350" Width="525">
        <Window.Resources>
            <DataTemplate DataType="{x:Type testApp:TestInt}">
                <TextBlock Text="{Binding Value}" Background="Red"/>
            </DataTemplate>
            <DataTemplate DataType="{x:Type testApp:TestStr}">
                <TextBlock Text="{Binding Value}" Background="Yellow"/>
            </DataTemplate>
        </Window.Resources>
        <StackPanel>
                <ContentControl Content="{Binding Test1}"/>
                <ContentControl Content="{Binding Test2}"/>
                <ListBox ItemsSource="{Binding TestList}"/>
        </StackPanel>
    </Window>

    public class TestInt
    {
        public int Value { get; set; }
    }
    public class TestStr
    {
        public string Value { get; set; }
    }
    public partial class MainWindow : Window
    {
        public TestInt Test1 { get; set; }
        public TestStr Test2 { get; set; }
        public List<object> TestList { get; set; } 
    
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
            Test1 = new TestInt { Value = 123 };
            Test2 = new TestStr { Value = "asd" };
            TestList = new List<object> { Test1, Test2 };
        }


    Чтобы даташаблоны автоматически подключались, тип нужно указывать через DataType="{x:Type testApp:TestInt}", а не через DataType="testApp:TestInt"
    Ответ написан
    Комментировать
  • Как обработать событие из DataTemplate нажатия радиокнопок?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    ListBox (как и другой наследник ItemsControl) имеет список элементов (удобнее всего его указывать через ItemsSource). Каждый элемент списка строится отдельным пунктом. У каждого пункта автоматически устанавливается DataContext - тот элемент исходного списка, который строится. В DataTemplate хорошо и правильно указывать нужный тип, который будет у элемента в датаконтексте, тогда появится подсказка у {Binding} внутри шаблона.

    public class Item : BaseViewModel
    {
        private string _name;
        public string Name 
        { 
            get => _name;
            set
            {
                _name = value;
                RaisePropertyChanged();
            }
        }
        
        private int _itemMode;
        public int ItemMode 
        { 
            get => _itemMode;
            set
            {
                _itemMode = value;
                RaisePropertyChanged();
            }
        }
    }
    
    public class ItemsList
    {
        public ObservableCollection<Item> Items { get; } = new ObservableCollection<Item>
        {
            new Item { Name = "Один", ItemMode = 1 },
            new Item { Name = "Два", ItemMode = 2 },
            new Item { Name = "Три", ItemMode = 3 },
        };
    }

    <Grid>
        <Grid.Resources>
            <converters:IntToBoolConverter x:Key="IntToBoolConverter"/>
            <DataTemplate DataType="{x:Type viewModels:Item}">
                <Border x:Name="Data">
                    <Grid x:Name="ContentBase" Margin="1,0">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="Auto"/>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                            <ColumnDefinition/>
                        </Grid.ColumnDefinitions>
                        <Label Name="DisplayText" Grid.Column="0" Content="{Binding Name}"/>
                        <RadioButton Name="_1" Grid.Column="1" Content="1" 
                                     IsChecked="{Binding ItemMode, Converter={StaticResource IntToBoolConverter}, ConverterParameter=1}"/>
                        <RadioButton Name="_2" Grid.Column="2" Content="2" 
                                     IsChecked="{Binding ItemMode, Converter={StaticResource IntToBoolConverter}, ConverterParameter=2}"/>
                        <RadioButton Name="_3" Grid.Column="3" Content="3" 
                                     IsChecked="{Binding ItemMode, Converter={StaticResource IntToBoolConverter}, ConverterParameter=3}"/>
                    </Grid>
                </Border>
            </DataTemplate>
        </Grid.Resources>        
        <Grid.DataContext>
            <viewModels:ItemsList/>
        </Grid.DataContext>
        <ListBox ItemsSource="{Binding Items}"/>
    </Grid>

    Код класса BaseViewModel
    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        
        protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    Код конвертера
    public class IntToBooleanConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return value != null 
                && parameter != null 
                && value.ToString() == parameter.ToString();
        }
     
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value == null || parameter == null) return Binding.DoNothing;
    
            int result;
            if ((bool)value && int.TryParse(parameter.ToString(), out result))
            {
                return result;
            }
    
            return Binding.DoNothing;
        }
    }

    Ответ написан
    8 комментариев
  • Как анимировать image в wpf при нажатии на другую кнопку?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    По кнопке изменить свойство во вьюмодели (возможно, булевое свойство). Триггер по изменению этого свойства вызовет анимацию.
    Ответ написан
    1 комментарий
  • Как включить отображение XAML Designer Window в Visual Studio 2017?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Судя по картинке, у вас студия разделила xaml и xaml.cs, а не поняла их как общую сущность. И теперь xaml показывает как простой текстовый файл. Возможно, студия не поняла, какой тип шаблона этого проекта (может, проект не созданный вновь, а подключенный существующий?).

    Создайте в этом же решении новый проект с типом WpfApplication и попробуйте дизайнер в нём. Сравните файлы csproj между вашим дефектным проектом и вновь созданным проектом.
    Ответ написан
    Комментировать
  • Есть ли большой туториал для изучения С# desktop + DB?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Лучшей книгой для вас будет Эндрю Троелсен "Язык программирования C# 6.0 и платформа .NET 4.6".
    Прекрасная книга для профессионалов, рассказывает обо всём в языке и почти обо всех возможностях и использованиях. В первую очередь, предназначена для программистов, которые уже умеют программировать, но не знают язык C#. В ей всё структурировано на разделы, каждый из них можно читать независимо и использовать как справочник.
    У этой книги есть только один недостаток - написана для профессионалов, и автор пишет, как делать, но не пишет, почему так делать - вы и так уже это понимаете, иначе книга стала бы совсем уж гигантской.

    Рекомендую взять время и попробовать написать несколько консольных программ на c#, начиная с моего любимого решателя квадратных уравнений :) и работы со списками (простой телефонный справочник). И только после этого начинайте работать с WPF.

    А вот очень приличный учебник по wpf:
    https://professorweb.ru/my/WPF/base_WPF/level1/inf...
    Довольно суховато написано, но с неплохими примерами.

    И опять же, рекомендую не начинать делать вашу программу, а сделать с помощью WPF те же программы, которые написали с консолью. И освойтё MVVM.
    Ответ написан
    1 комментарий
  • Wpf listview: как обновить только один элемент его?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    1) Список моделей хранить в ObservableCollection,
    2) В классе модели реализовать интерфейс INotifyPropertyChanged (можно сделать это в базовом классе)
    3) В свойствах модели в сеттере вбрасывать событие PropertyChanged с именем свойства:
    private string _name;
    public string Name
    {
        get
        {
            return _name;
        }
        set
        {
            if (_name != value)
            {
                _name= value;
                RaisePropertyChanged(nameof(Name));
            }
        }
    }
    
    // можно сделать в базовом классе, в котором и реализовать интерфейс INotifyPropertyChanged
    protected virtual void RaisePropertyChanged(string propertyName)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
    Ответ написан
    Комментировать
  • Как правильно открыть окно в другом приложении?

    lexxpavlov
    @lexxpavlov Автор вопроса
    Программист, преподаватель
    В описании функции SetForegroundWindow написаны условия, когда эта функция работает. В некоторых случаях почему-то в другое окно не передаётся клавиатурный фокус. Точнее, второй процесс, который открывает окно, не имеет фокуса. Почему-то в некоторых случаях это не мешает ему открывать активное окно (видимо, одно из других условий срабатывает), но в некоторых - не может сделать окно активным.

    Решение - симулировать нажатие клавиши в процессе, который будет открывать окно:
    private const byte VK_LMENU = 0xA4;
    private const byte KEYEVENTF_EXTENDEDKEY = 0x01;
    private const byte KEYEVENTF_KEYUP = 0x02;
    private const byte KEYSCAN_LALT = 0xB8;
    
    [DllImport("user32.dll")]
    internal static extern bool SetForegroundWindow(IntPtr hwnd);
    [DllImport("user32.dll")]
    private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
    
    public static void BringWindowToFront(IntPtr hwnd)
    {
        // симуляция нажатия клавиши ALT https://stackoverflow.com/a/13881647/1343405
        keybd_event(VK_LMENU, KEYSCAN_LALT, 0, 0);
        keybd_event(VK_LMENU, KEYSCAN_LALT, KEYEVENTF_KEYUP, 0);
    
        SetForegroundWindow(hwnd);
    }


    Полезные ссылки: вопрос, статья.
    Ответ написан
    Комментировать
  • Как создать общее свойство для элементов?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Нужно указать тип элементов, которым требуется указать стили. В DataGrid ячейки находятся в элементе DataGridCell, а заголовки - в DataGridColumnHeader.
    <DataGrid>
        <DataGrid.Columns>
            <DataGridTextColumn Header="Qwe"/>
            <DataGridTextColumn Header="Rty"/>
            <DataGridTextColumn Header="Uio"/>
        </DataGrid.Columns>
        <DataGrid.Resources>
            <Style TargetType="DataGridColumnHeader">
                <Setter Property="Width" Value="100"/>
            </Style>
        </DataGrid.Resources>
    </DataGrid>

    Можно положить стиль не в ресурсы, а в специальный контейнер. Разница в том, что стиль в ресурсах применяется во все вложенные подходящие элементы, даже вложенные внутри ячеек (например, в другой DataGrid внутри ячейки). Стиль в ColumnHeaderStyle будет использован только в заголовках этого DataGrid. Пример:
    <DataGrid.ColumnHeaderStyle>
        <Style TargetType="DataGridColumnHeader">
            <Setter Property="Width" Value="100"/>
        </Style>
    </DataGrid.ColumnHeaderStyle>

    Если указывать стиль элементов, то этот стиль не добавляется к текущему, а заменяет предыдущий стиль. Чтобы стиль добавлялся к уже существующем стилю, то стиль нужно наследовать от старого:
    <DataGrid.ColumnHeaderStyle>
        <Style TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
            <Setter Property="Width" Value="100"/>
        </Style>
    </DataGrid.ColumnHeaderStyle>
    Ответ написан
    4 комментария
  • Как изменить задний фон дочерних MenuItem?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Нужно переопределить шаблон элемента меню - просто добавить этот код в Window.Resources и всё, даже добавлять в <Menu> ничего не надо. "MenuItem.TopLevelHeaderTemplateKey" - стандартное название шаблона
    <ControlTemplate x:Key="{x:Static MenuItem.TopLevelHeaderTemplateKey}" TargetType="{x:Type MenuItem}">
        <Border Name="Border" >
            <Grid>
                <ContentPresenter Margin="6,3,6,3" ContentSource="Header" RecognizesAccessKey="True" />
                <Popup Name="Popup" Placement="Bottom" IsOpen="{TemplateBinding IsSubmenuOpen}" AllowsTransparency="True" Focusable="False" PopupAnimation="Fade">
                    <Border Name="SubmenuBorder" SnapsToDevicePixels="True" Background="Transparent">
                        <StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Cycle" />
                    </Border>
                </Popup>
            </Grid>
        </Border>
    </ControlTemplate>


    Решение вот отсюда. Там они ещё триггеры используют, но здесь не обязательно.
    Ответ написан
    1 комментарий
  • Как разделить код ViewModel-ей на каждое окно?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Я правильно понял, что есть несколько моделей (полученных из сервиса) и есть вьюмодель для каждой модели. И в какой-то момент происходит выбор показа нужной модели, это уже есть. Я правильно понял?

    Я бы попробовал в MainViewModel сделать список ViewModel-ей. Если добавляется новая модель, то добавлять в список. В MainViewModel добавить CurrentViewModel, имеющий ссылку на текущую вьюмодель, которая в данный момент нужно выводить. И в MainViewModel сделать метод, которыйпоказывает нужную вьюмодель по выбранной модели (что-то типа private ViewModel SelectModel(Model model) {}).
    Ответ написан
    Комментировать
  • Можно ли указать в одном биндинге несколько конвертеров?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Вам не нужно делать второй конвертер, а настроить тот, чтобы возвращал нужное значение - возвращать Visibility.Collapsed при истине и Visibility.Visible при лжи.
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if ((bool)value)
        {
            return parameter != null ? Visibility.Collapsed : Visibility.Hidden;
        }
        return Visibility.Visible;
    }


    А если нужно именно проверять несколько различных проверок у одного элемента, то используйте MultiBinding. Я написал об этом тут.
    Ответ написан
    9 комментариев
  • Двойной клик по итему ListBox-а?

    lexxpavlov
    @lexxpavlov
    Программист, преподаватель
    Нужно просто поставить привязку с командой у вашей вьюмодели. Вы ведь используете MVVM?

    Гораздо удобнее использовать не создавать вручную ICommand, а сделать класс, реализующий этот интерфейс. Обычно его называют DelegateCommand (классы указаны ниже).

    И дальше во вьюмодели создаёте свойство для команды и привязывайте то место, где нужна команда, к созданной команде во вьюмодели.

    #region Свойства
    
    public ObservableCollection<MyClass> Collection { get; set; } = new ObservableCollection<MyClass>
    {
        new MyClass { Name = "Иванов Иван" },
        new MyClass { Name = "Петров Пётр" },
        new MyClass { Name = "Сидоров Сидор" },
    };
    public MyClass SelectedCollection { get; set; };
    
    #endregion
    
    #region Команды
    
    private ActionCommand _myCommand;
    public ActionCommand MyCommand => _myCommand ?? (_myCommand = new ActionCommand(MyMethod));
    
    private DelegateCommand<MyClass> _myDelegateCommand;
    public DelegateCommand<MyClass> MyDelegateCommand => _myDelegateCommand ?? (_myDelegateCommand = new DelegateCommand<MyClass>(MyMethod2, item => item != null);
    
    private void MyMethod()
    {
        // обработка команды
    }
    
    private void MyMethod2(MyClass item)
    {
        // обработка команды
    }
    
    #endregion


    <ListBox ItemsSource="{Binding Collection}" SelectedItem="{Binding SelectedItem}"/>
    <Button Command="{Binding MyCommand}" Content="Команда 1"/>
    <Button Command="{Binding MyDelegateCommand}" CommandParameter="{Binding SelectedItem}" Content="Команда 2"/>


    Если нужна команда не на событие Сlick, а на любое другое событие (как вы говорите, на DoubleClick), то нужно сделать добавить референс на System.Windows.Interactivity и указать триггер на событие:
    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

    <ListBox ItemsSource="{Binding Collection}" SelectedItem="{Binding SelectedItemc}" DisplayMemberPath="Name">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="MouseDoubleClick">
                <i:InvokeCommandAction Command="{Binding MyDelegateCommand}" CommandParameter="{Binding SelectedItem}" />
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </ListBox>


    Класс DelegateCommand - команда с параметром, и класс ActionCommand - команда без параметра. Нешаблонный класс DelegateCommand используется реже (я вообще не припомню, чтобы его использовал).
    public class DelegateCommand<T> : ICommand
    {
        #region Private fields
    
        private readonly Action<T> _execute;
        private readonly Func<T, bool> _canExecute;
    
        #endregion
    
        #region Constructors
    
        public DelegateCommand(Action<T> execute, Func<T, bool> canExecute = null)
        {
            _execute = execute;
            _canExecute = canExecute;
        }
    
        #endregion
    
        #region DelegateCommand
    
        public void Execute(T parameter)
        {
            var handler = _execute;
            if (handler != null)
            {
                handler(parameter);
            }
        }
    
        public bool CanExecute(T parameter)
        {
            var handler = _canExecute;
            return handler == null || handler(parameter);
        }
    
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    
        #endregion
    
        #region ICommand
    
        void ICommand.Execute(object parameter)
        {
            Execute((T)parameter);
        }
    
        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute((T)parameter);
        }
    
        #endregion
    }
    
    public class DelegateCommand : DelegateCommand<object>
    {
        public DelegateCommand(Action<object> execute, Func<object, bool> canExecute = null)
            : base(execute, canExecute)
        {
        }
    }
    
    public class ActionCommand : DelegateCommand<object>, ICommand
    {
        #region Private fields
    
        private readonly Action _action;
        private readonly Func<bool> _canExecute;
    
        #endregion
    
        #region Constructors
    
        public ActionCommand(Action action, Func<bool> canExecute = null)
            : base(null, null)
        {
            _action = action;
            _canExecute = canExecute;
        }
    
        #endregion
    
        #region ActionCommand
    
        public void Execute()
        {
            var handler = _action;
            if (handler != null)
            {
                handler();
            }
        }
    
        public bool CanExecute()
        {
            var handler = _canExecute;
            return handler == null || handler();
        }
    
        #endregion
    
        #region ICommand
    
        void ICommand.Execute(object parameter)
        {
            Execute();
        }
    
        bool ICommand.CanExecute(object parameter)
        {
            return CanExecute();
        }
    
        #endregion
    }
    Ответ написан
    1 комментарий