Я делаю меню в виде ленты.
Для достижения цели я создал три основных компонента.
Класс определяет каждый элемент
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 с той же командой, и она сработала.
Вещь, которую я пробовал
Обновлять
Я наследовал свой класс от 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.
Я протестировал его с помощью кнопки, и он работает, поэтому с виртуальной машиной все в порядке, проблема в пользовательском элементе управления, который не получает команду
Можете ли вы попробовать поставить new ImageButton в try ... catch, чтобы увидеть, где возникает исключение? Кроме того, разве ваш MenuComponentItems не должен наследовать от ContentView?
Я не могу поставить try catch, потому что он скажет что угодно, потому что он добавляет элемент, проблема в команде. кстати, почему мой MenuComponentItems должен наследовать от Contentview
Так когда именно возникает ошибка? Он добавляет элемент без проблем? Это когда вы вызываете команду?
И я не уверен насчет наследования ContentView, это было случайное предложение, но я не уверен, что оно действительно обязательно. Все равно стоило попробовать, ахах
Ну, я добавил то, что вы мне сказали, и исправил это, но это не запускает команду
Я проверил команду и она равна нулю, но почему
Можете ли вы рассказать, как именно вы привязываете конкретную команду к своему элементу? В вашем коде «использования» команда не упоминается. Кроме того, в одном из моих проектов я использую команду в пользовательском элементе управления, так же, как вы это делаете в своем MenuComponentItems с BindableProperty, за исключением того, что я использую ICommand вместо RelayCommand.
Я отредактировал его и дал вам больше контекста
Продолжим обсуждение.





Хорошо, попробовав несколько вещей, я действительно нашел обходной путь для вашей ситуации: кажется, команда не вызывалась, потому что при создании вашего 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.
Какая строка вызывает ошибку? Где привязка, где вы используете команду?