Что я пытаюсь выполнить sh
Я пытаюсь, чтобы мой вложенный пункт меню изменил отображаемый пользовательский элемент управления. В более технических терминах я пытаюсь:
- Получить событие щелчка, прикрепленное к вложенному
MenuItem
(из моего MyMenu.cs
файла - реализует INotifyPropertyChanged
), к ... - Использовать
RoutedEventHandler
(может быть из файла MyMenu.cs
? - реализует UserControl
), чтобы ... - Вызвать метод
SwitchScreen
(из моего файла MainWindow.cs
- реализует Window
)
Где я застреваю
Кажется, я не могу найти способ добавить событие щелчка в соответствующий пункт меню. Мой текущий лог c также требует, чтобы исходный отправитель передавался в качестве аргумента, чтобы я мог определить правильный MySubview
для отображения.
XAML Handler
Я попытался добавить Событие click в xaml выглядит следующим образом, но оно добавляет обработчик только к первому уровню элемента меню (не к вложенным элементам элемента меню).
<MenuItem ItemsSource="{Binding Reports, Mode=OneWay}" Header="Reports">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<EventSetter Event="Click" Handler="MenuItem_Click"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
C# Setter
Имею попытался добавить сеттер, предложенный в этом ответе , но я не могу создать событие щелчка от MyMenu.cs
до MyMenuUserControl.cs
.
Style style = new Style();
style.BasedOn = menuItem2.Style;
style.Setters.Add(new EventSetter( /* ??? */ ));
C# ICommand
Я пытался использовать ICommand
, предложил в этот ответ , но я не могу создать команду реле от MyMenu.cs
до MyMenuUserControl.cs
.
Возможно, я совершил что-то не так в одной из этих попыток, но теперь я перестал играть и готов бросить полотенце.
Примечания
Фактическая структура
На самом деле мой настоящий код имеет n-вложенные циклы foreach для генерации меню, и я удаляю уровень вложенности, если в он каждый перечисляемый (например, myObjects
) имеет только один элемент. Удаление уровня вложенности также перемещает событие щелчка вверх на один уровень. Мое окончательное меню может выглядеть примерно так:
Мои пункты меню:
- Элемент (menuItem1)
- Элемент (menuItem2)
- Элемент ( menuItem3) + событие клика
- Item (menuItem3) + событие клика
- Item (menuItem2) + событие клика (см. A )
- Item (menuItem1) + событие щелчка (см. B )
A : только один menuItem3 является вложенным, поэтому мы удаляем его (это избыточно) и перемещаем событие click до menuItem2.
B : только один menuItem2 является вложенным, и у него только один menuItem3. Оба удаляются, так как они избыточны, и мы перемещаем событие click, перемещаемое в menuItem1.
Именно поэтому я бы хотел сохранить создание пунктов меню в классе MyMenu
.
Другие предложения
Я мог бы поступить об этом совершенно неправильно, и я открыт для предложений, которые меняют то, как я это делаю.
Код
MyMenu.cs
Конструктор в этом классе генерирует пункты моего меню и его подменю. Здесь я пытаюсь добавить событие клика.
class MyMenu : INotifyPropertyChanged
{
private List<MenuItem> menuItems = new List<MenuItem>();
public List<MenuItem> MenuItems
{
get { return menuItem; }
set
{
menuItem = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
public List<Tuple<MyObject, MenuItem>> Map { get; private set; } = new List<Tuple<MyObject, MenuItem>>();
public MyMenu(List<MyObject> myObjects)
{
foreach(MyObject myObject in myObjects)
{
MenuItem menuItem1 = new MenuItem { Header = myObject.Name };
foreach(string s in myObject.Items)
{
MenuItem menuItem2 = new MenuItem { Header = s };
// Add click event to menuItem2 here
menuItem1.Items.Add(menuItem2);
Map.Add(new Tuple<MyObject, MenuItem>(myObject, menuItem2));
}
MenuItem.Add(menuItem1);
}
}
private void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
MyMenuUserControl.xaml
Пример минимального кода UserControl (используется атрибуты xmlns по умолчанию). MyMenuUserControl.xaml.cs
имеет только конструктор с InitializeComponent();
<UserControl>
<!-- xmlns default attributes in UserControl above removed for minimal code -->
<Menu>
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel VerticalAlignment="Stretch"/>
</ItemsPanelTemplate>
</Menu.ItemsPanel>
<MenuItem ItemsSource="{Binding MenuItems, Mode=OneWay}" Header="My menu items"/>
</Menu>
</UserControl>
MyDataContext.cs
Пример минимального кода (тот же код PropertyChangedEventHandler
и OnPropertyChanged()
, что и MyMenu.cs
). Конструктор просто устанавливает свойства Menu
и Subviews
.
class MyDataContext: INotifyPropertyChanged {private MyMenu menu; publi c MyMenu Menu {get {return menu; } set {menu = value; OnPropertyChanged (); }}
private List<MySubview> mySubviews;
public List<MySubview> MySubviews
{
get { return mySubviews; }
set
{
mySubviews = value;
OnPropertyChanged();
}
}
// ... rest of code removed to maintain minimal code
}
MainWindow.xaml.cs
Subview
содержит свойство типа MyObject
. Это позволяет мне использовать MyMenu
свойство Map
, чтобы определить, какое подпредставление отображать для данного клика MenuItem
. Да, сделать карту на карте MainWindow
может быть проще, однако логика c, которую я имею в MyMenu
, является минимальным примером (см. Примечания для получения дополнительной информации).
publi c частичный класс MainWindow: Window {publi c MainWindow () {InitializeComponent ();
// I get my data here
List<MyObject> myObjects = ...
List<MySubview> mySubviews = ...
DataContext = new MyDataContext(new MyMenu(myObjects), new MySubviews(mySubviews));
}
private void SwitchScreen(object sender, RoutedEventArgs e)
{
MyDataContext c = (MyDataContext)DataContext;
MyObject myObject = c.MyMenu.Map.Where(x => x.Item2.Equals(sender as MenuItem)).Select(x => x.Item1).First();
MySubview shownSubview = c.MySubviews.Where(x => x.MyObject.Equals(myObject)).First();
c.MySubviews.ForEach(x => x.Visibility = Visibility.Collapsed);
shownSubview.Visibility = Visibility.Visible;
}
}