Привязка команды ретрансляции из класса к несоответствию CustomCommand

Я делаю меню в виде ленты.

Для достижения цели я создал три основных компонента.

  • Класс элемента компонента
  • Шаблон меню (XAML)
  • Шаблон меню (код)

Класс определяет каждый элемент

public class MenuComponentItems {

    public string ImageSource { get; set; }

    public string CommandParameter { get; set; }

    public string Section { get; set; }

    public IRelayCommand Command { get; set; }

}

затем мы определим пользовательский интерфейс

<Border
    Margin = "5,5,0,0"
    HeightRequest = "120"
    VerticalOptions = "StartAndExpand">
    <Grid
        RowSpacing = "5"
        VerticalOptions = "Center">
        <Grid.RowDefinitions>
            <RowDefinition Height = "Auto" />
            <RowDefinition Height = "Auto" />
        </Grid.RowDefinitions>

        <HorizontalStackLayout
            x:Name = "MenuItemsStack"
            BackgroundColor = "Bisque" />

        <Label
            Grid.Row = "1"
            HorizontalTextAlignment = "Center"
            Text = "{Binding SectionName, Source = {x:Reference MenuComponent}}" />
    </Grid>
</Border>

и, наконец, код элемента управления

public partial class MenuTemplate : ContentView {

public ObservableCollection<MenuComponentItems> MenuItems { get; set; } = [];

public MenuTemplate() {
        InitializeComponent();
        MenuItems.CollectionChanged += (sender, e) => UpdateMenu();
}

public static readonly BindableProperty SectionNameProperty = BindableProperty.Create(
    nameof(SectionName),
    typeof(string),
    typeof(MenuTemplate));

public string SectionName {
    get => (string)GetValue(SectionNameProperty);
    set => SetValue(SectionNameProperty, value);
}

private void UpdateMenu() {
    MenuItemsStack.Children.Clear();

    foreach (var menuItem in MenuItems) {
        var imageButton = new ImageButton {
            Source = menuItem.ImageSource,
            WidthRequest = 60,
            HeightRequest = 60,
            Margin = new Thickness(10, 0, 10, 0),
            Command = menuItem.Command,
            CommandParameter = menuItem.CommandParameter
        };

        MenuItemsStack.Children.Add(imageButton);
    }
}

Применение

<Grid RowDefinitions = "200, *">
    <HorizontalStackLayout>
        <controls:MenuTemplate SectionName = "ferwqfkrew">
            <controls:MenuTemplate.MenuItems>
                <models:MenuComponentItems
                    ImageSource = "dotnet_bot.png"
                    Section = "Section 1" />
                <models:MenuComponentItems
                    ImageSource = "mario.png"
                    Section = "Section 1" />
            </controls:MenuTemplate.MenuItems>
        </controls:MenuTemplate>
    </HorizontalStackLayout>
</Grid>

выход

Проблема

когда я создаю команду ретрансляции в своей модели представления

[RelayCommand]
public void MenuItemSelected(object param) {
    
    Debug.WriteLine("dcd");
}

и свяжи это, я получу

свойство, BindableProperty или событие, найденное для «Command», или несовпадение.

Чего я не знаю, так это того факта, что в моем классе у меня есть RelayCommand, и моя виртуальная машина тоже является RelayCommand.

Сначала я подумал, что это потому, что команда ImageButton является ICommand, а не RelayCommand, но я протестировал ImageButton с той же командой, и она сработала.

Вещь, которую я пробовал

  1. Использование ICommand вместо RelayCommand из набора инструментов Microsoft mvvm
  2. Создано свойство RelayCommand в самом элементе управления.

Обновлять

Я наследовал свой класс от ContentView.

public class MenuComponentItems : ContentView {

    public string ImageSource { get; set; }

    public string CommandParameter { get; set; }


    public static readonly BindableProperty CommandProperty = BindableProperty.Create(
        nameof(Command), typeof(RelayCommand), typeof(MenuComponentItems));

    public RelayCommand Command {
        get => (RelayCommand)GetValue(CommandProperty);
        set => SetValue(CommandProperty, value);
    }
}

но это не запускает команду.

Применение

public class MenuComponentItems : ContentView {

public string ImageSource { get; set; }

public string CommandParameter { get; set; }


public static readonly BindableProperty CommandProperty = BindableProperty.Create(
    nameof(Command), typeof(RelayCommand), typeof(MenuTemplate));

public RelayCommand Command {
    get => (RelayCommand)GetValue(CommandProperty);
    set => SetValue(CommandProperty, value);
}
<controls:MenuTemplate SectionName = "ferwqfkrew">
    <controls:MenuTemplate.MenuItems>
        <models:MenuComponentItems Command = "{Binding MenuItemSelectedCommand}"
            ImageSource = "dotnet_bot.png" />
public partial class MainViewModel : ObservableObject {
     
    [RelayCommand]
    public void MenuItemSelected() {
    
        Debug.WriteLine("dcd");
    }
}

Я проверил MenuItemsStack.Children.Add(imageButton); и по какой-то причине команда имеет значение null.

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

Какая строка вызывает ошибку? Где привязка, где вы используете команду?

Jason 26.03.2024 17:43

Можете ли вы попробовать поставить new ImageButton в try ... catch, чтобы увидеть, где возникает исключение? Кроме того, разве ваш MenuComponentItems не должен наследовать от ContentView?

GChapX 26.03.2024 17:46

Я не могу поставить try catch, потому что он скажет что угодно, потому что он добавляет элемент, проблема в команде. кстати, почему мой MenuComponentItems должен наследовать от Contentview

Natash Dimas 26.03.2024 18:10

Так когда именно возникает ошибка? Он добавляет элемент без проблем? Это когда вы вызываете команду?

GChapX 26.03.2024 18:18

И я не уверен насчет наследования ContentView, это было случайное предложение, но я не уверен, что оно действительно обязательно. Все равно стоило попробовать, ахах

GChapX 26.03.2024 18:20

Ну, я добавил то, что вы мне сказали, и исправил это, но это не запускает команду

Natash Dimas 26.03.2024 18:20

Я проверил команду и она равна нулю, но почему

Natash Dimas 26.03.2024 18:23

Можете ли вы рассказать, как именно вы привязываете конкретную команду к своему элементу? В вашем коде «использования» команда не упоминается. Кроме того, в одном из моих проектов я использую команду в пользовательском элементе управления, так же, как вы это делаете в своем MenuComponentItems с BindableProperty, за исключением того, что я использую ICommand вместо RelayCommand.

GChapX 26.03.2024 18:32

Я отредактировал его и дал вам больше контекста

Natash Dimas 26.03.2024 18:46

Продолжим обсуждение.

GChapX 26.03.2024 18:51
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
10
105
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Хорошо, попробовав несколько вещей, я действительно нашел обходной путь для вашей ситуации: кажется, команда не вызывалась, потому что при создании вашего ImageButtonsBindingContext из MenuComponentItems было нулевым.

Обходной путь:

  • Установите общедоступный метод UpdateMenu и обновите BindingContext при итерации:
public void UpdateMenu() {
    MenuItemsStack.Children.Clear();

    foreach (var menuItem in MenuItems) {
        menuItem.BindingContext = this.BindingContext; // Somehow the MenuTemplate's BindingContext is correct, only item's one isn't set, we fix this here.
        var imageButton = new ImageButton {
            Source = menuItem.ImageSource,
            WidthRequest = 60,
            HeightRequest = 60,
            Command = menuItem.Command,
            CommandParameter = menuItem.CommandParameter
        };

        MenuItemsStack.Children.Add(imageButton);
    }
}
  • В вашем View дайте Имя вашему элементу управления, и после установки BindingContext представления и инициализации компонента вызовите UpdateMenu, чтобы принудительно привязать привязку:
public MainPage(MainViewModel mainViewModel) {
    BindingContext = mainViewModel;
    InitializeComponent();
    YourMainTemplate.UpdateMenu();
}

Не буду врать, я до сих пор не понимаю, в чем проблема и почему этот подход работает, но, по крайней мере, теперь он вызывает вашу команду!

Я также нашел решение.

private void UpdateMenu() {
    MenuItemsStack.Children.Clear();

    foreach (var menuItem in MenuItems) {
        var imageButton = new ImageButton {
            Source = menuItem.ImageSource,
            WidthRequest = 60,
            HeightRequest = 60,
        };

        imageButton.SetBinding(ImageButton.CommandProperty, new Binding("MenuItemSelectedCommand"));

        Binding parameter = new() {
        Source = imageButton
    };
    imageButton.SetBinding(ImageButton.CommandParameterProperty, parameter);

    MenuItemsStack.Children.Add(imageButton);
}

Вам просто нужно вызвать relayCommand своему viewModel.

Да, я также могу воспроизвести вашу проблему. Если вы добавите точки останова, вы можете обнаружить, что метод UpdateMenu в MenuTemplate вызывается до того, как значение свойства Command установлено. И когда свойство Command изменится, событие MenuItems.CollectionChanged не будет вызываться снова. Именно по этой причине свойство Command всегда имеет значение null.

Итак, я хочу вызвать UpdateMenu независимо от того, когда изменится значение свойства Command.

Для этого рекомендую обратиться к этой теме, ObservableCollection не замечает, когда Item в ней меняется (даже с INotifyPropertyChanged). Вам нужен FullyObservableCollection<> вместо ObservableCollection.

public partial class MenuTemplate : ContentView
{
    public FullyObservableCollection<MenuComponentItems> MenuItems { get; set; } = [];

    public MenuTemplate()
    {
        InitializeComponent();
        MenuItems.ItemPropertyChanged += (sender, e) => UpdateMenu();
    }
....

Используя приведенный выше код, независимо от того, когда изменится значение Commmand, будет вызван UpdateMenu(). ImageButton получит правильный Command, а также CommandParameter.

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