Как привязать настраиваемую кнопку UserControl, нажимаемую для команды, к родительской модели представления UC

У меня есть пользовательский элемент управления, содержащий кнопки, метки и текстовые поля. Этот пользовательский элемент управления используется в другом представлении, имеющем ViewModel. Я хочу привязать обработчик событий щелчка из пользовательского элемента управления к команде в viewModel.

Код для представления:

    public partial class ViewPage : UserControl
    {
        public ViewPage()
        {
            InitializeComponent();
        }

        private void CustomControl_RestartClick(object sender, RoutedEventArgs e)
        {
            Debug.WriteLine("clicked");

        }
    }
}

Код пользовательского элемента управления:

public partial class CustomControl : UserControl
{

    // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TitleProperty =
        DependencyProperty.Register("Title", typeof(string), typeof(CustomControl), new PropertyMetadata(String.Empty));



    public string Title
    {
        get { return (string)GetValue(TitleProperty); }
        set { SetValue(TitleProperty, value); }
    }



    public static readonly RoutedEvent RestartClickEvent = EventManager.RegisterRoutedEvent(nameof(RestartClick), RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(CustomControl));

    public event RoutedEventHandler RestartClick
    {
        add { AddHandler(RestartClickEvent, value); }
        remove { RemoveHandler(RestartClickEvent, value); }
    }

    public CustomControl()
    {
        InitializeComponent();
    }

    public void OnRestartClick(object sender, RoutedEventArgs e)
    {
        RaiseEvent(new RoutedEventArgs(RestartClickEvent));
    }
}

У меня есть эта команда в ViewModel, к которой я хочу ее привязать:

public ICommand RestartCommand { get; private set; }

Я хочу иметь возможность делать что-то вроде

<views:CustomControl Title = "PS04-04"  RestartClick = "{Binding RestartCommand}" />

ссылка на github: https://github.com/MaganaLu/MCP_WPF.git

Целью привязки типа {Binding RestartCommand} будет свойство зависимости типа ICommand, например свойство Команда кнопки.

Clemens 22.08.2024 19:06

К вашему сведению: просто предложение, но вы можете назвать свой элемент управления как-нибудь иначе, чем CustomControl, хотя бы для того, чтобы избежать путаницы с четко определенной концепцией WPF «пользовательский элемент управления», означающей элемент управления, не производный от класса UserControl, который не имеет связанного Страница XAML, для которой можно задать стиль и для которой необходимо определить шаблон элемента управления.

Joe 22.08.2024 19:20

Как мне создать его для ICommand вместо RoutedEVent? @Клеменс

DuckQuackington 22.08.2024 19:23

Как и любое другое свойство зависимостей. У вас уже есть один заголовок с именем типа string. Создайте еще один с именем Restart типа ICommand. В XAML пользовательского элемента управления привяжите его к кнопке «перезагрузка» с помощью <Button ... Command = {Binding Restart, RelativeSource = {RelativeSource AncestorType=UserControl}}"/>.

Clemens 22.08.2024 20:25

Затем привяжите свойство RestartCommand, например <views:CustomControl Title = "PS04-04" Restart = "{Binding RestartCommand}"/>

Clemens 22.08.2024 20:28

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

Clemens 23.08.2024 09:39
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
106
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вам нужно свойство зависимостей, которое принимает ICommand.

public static readonly DependencyProperty FooCommandProperty =
  DependencyProperty.Register(nameof(FooCommand), typeof(ICommand), typeof(CustomControl));
public ICommand FooCommand
{
  get => (ICommand)GetValue(FooCommandProperty);
  set => SetValue(FooCommandProperty, value);
}

Затем где-то в вашем пользовательском элементе управления (вероятно, в обработчике событий) вам нужно выполнить эту команду, когда что-то произойдет.

if (FooCommand != null && FooCommand.CanExecute(fooParameter))
{
  FooCommand.Execute(fooParameter);
}

Когда все это будет готово, вы просто привяжете ICommand к свойству.

Или просто привяжите FooCommand к элементу управления в XAML UserControl.

Clemens 22.08.2024 20:33

@Clemens Я сделал это, но теперь он ищет команду в другой модели представления сети. Я получаю сообщение об ошибке «Ошибка System.Windows.Data: 40: ошибка пути BindingExpression: свойство RestartCommand не найдено в объекте» MainWindowViewModel (HashCode = 65300541) »», предполагается, что точка находится в другой модели представления.

DuckQuackington 22.08.2024 22:10

@DuckQuackington Вы написали: «У меня есть эта команда в ViewModel, к которой я хочу ее привязать: public ICommand RestartCommand { get; private set; }» - мы понятия не имеем, о какой модели представления вы говорите, и существует ли вообще более одной модели представления. Когда вы пишете {Binding RestartCommand}, в текущем DataContext ожидается свойство RestartCommand. то есть текущая модель представления, какой бы она ни была. Вы нам не сказали.

Clemens 22.08.2024 22:38

@Клеменс Извините, что не предоставил достаточно информации. Я использую WPF впервые. Я действительно ценю вашу помощь. Я обновил вопрос. У меня есть «MainWindowViewModel», который содержит представление «MCP06Page».

DuckQuackington 22.08.2024 23:45

Судя по вашим объяснениям и коду, я думаю, что проблема, скорее всего, вызвана путаницей контекстов данных на разных уровнях макета.

На уровне объявления <views:ConveyorControl вы ожидаете экземпляр DesignMCP06PageViewModel в контексте данных. Но на самом деле во время выполнения в контексте данных появляется экземпляр MainWindowViewModel, который не имеет свойства RestartCommand.

Не зная полностью Вашей задачи, условий ее выполнения, я не могу дать точный ответ, подходящий для Вашего решения. Поэтому я предложу один из распространенных подходов, используемых в WPF. Если вас это не устраивает, напишите подробнее о своей задаче и будет лучше, если вы загрузите решение в репозиторий GitHub и предоставите ссылку на него.

Создание локатора.
Это всего лишь контейнер со свойствами для различных данных, которые нужно использовать на разных уровнях вёрстки. Чаще всего свойства Locator являются отдельными экземплярами ViewModel. В простейшем случае (когда свойства инициализируются один раз при запуске приложения, обязательно ДО первого вызова локатора) нет необходимости реализовывать в локаторе интерфейс INotifyPropertyChanged.

    public class Locator
    {
        public MainWindowViewModel MainVM { get; set; } = new();
        public DesignMCP06PageViewModel MCP06_VM { get; set; } = new();
    }

Вы объявляете экземпляр локатора в ресурсах приложения.

    <Application.Resources>
        <somens:Locator x:Key = "locator"/>

Доступ к свойствам локатора:

<Window x:Class = "-------------.MainWindow"
        ----------------------------------
        DataContext = "{Binding MainVM, Source = {StaticResource locator}}">
    <Grid>

        <views:ConveyorControl x:Name = "binding"
                               Title = "PS04-04"
                               RestartCommand = "{Binding MCP06_VM.RestartCommand,
                                                        Source = {StaticResource locator}}"/>
        
    </Grid>
</Window>

P.S. Ответ не актуален, так как после дополнительных уточнений и просмотра Решения на GitHub выяснилось, что переключение представлений происходит с помощью пакета Prism. По сути, вопрос автора сводится к тому, как правильно использовать Prism.

Привет @EldHasp Да, я не понимаю, почему контекст данных не взаимодействует с «MCP06ViewModel» и вместо этого переходит в MainWindowViewModel. Я создал пользовательский элемент управления UserControl в папке компонентов. Ссылка на github находится здесь: github.com/MaganaLu/MCP_WPF. Еще раз благодарю вас за помощь!

DuckQuackington 23.08.2024 21:42

@DuckQuackington Я посмотрел твое решение. Оказывается, вы используете Prism и переключение страниц происходит через сервисы этого пакета. К сожалению, я не очень хорошо знаком с Призмой. Также советую сразу указывать информацию об использовании Prism в начале вашего вопроса. Было бы еще лучше, если бы вы указали это в названии темы.

EldHasp 23.08.2024 23:25

Также имейте в виду, что «переключение взглядов» не имеет ничего общего с исходным вопросом, на который уже был дан ответ. @Duck, пожалуйста, задайте новый вопрос, если у вас есть другие проблемы. Избегайте вопросов-хамелеонов. Не забудьте принять ответ на исходный вопрос. См. stackoverflow.com/help/someone-answers

Clemens 24.08.2024 10:36

Другие вопросы по теме