i tried to create MenuItems of collection items - and failed. In detail: I have a simple class ClassA that defines a string-property 'HeadText'.
In my MainViewModel i defined an ObservableCollection property. The collection is filled with 3 items. Now in XAML i want to create MenuItems of these 3 items of type ClassA. I did the following:
<Window.Resources>
<CompositeCollection x:Key="CollA">
<ItemsControl ItemsSource="{Binding Path=MItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding HeadText}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</CompositeCollection>
</Window.Resources>
<Grid>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding Source={StaticResource CollA}}"/>
</Grid>
But all i get is an empty menu bar. Any ideas how i can do this?
The viewmodel and the class ClassA:
public class MainVM
{
public MainVM() {
_mItems.Add(new ClassA() { HeadText = "A" });
_mItems.Add(new ClassA() { HeadText = "B" });
_mItems.Add(new ClassA() { HeadText = "C" });
}
private ObservableCollection<ClassA> _mItems = new ObservableCollection<ClassA>();
public ObservableCollection<ClassA> MItems{
get { return _mItems; }
}
}
public class ClassA
{
public ClassA() { }
public String HeadText { get; set; }
}
Thanks in advance.
Edit:
If i write this, it works:
<Menu DockPanel.Dock="Top" ItemsSource="{Binding MItems}">
<Menu.ItemContainerStyle>
<Style TargetType="MenuItem" BasedOn="{StaticResource {x:Type MenuItem}}">
<Setter Property="Header" Value="{Binding HeadText}"/>
</Style>
</Menu.ItemContainerStyle>
</Menu>
But i want to do it the other way. And i'm interested why the other way does not work.
I found the answer here. My XAML first looked like this:
<Window.DataContext>
<local:MainVM/>
</Window.DataContext>
<Window.Resources>
<CollectionViewSource Source="{Binding Path=MItems}" x:Key="source"/>
</Window.Resources>
<StackPanel>
<Menu DockPanel.Dock="Top">
<Menu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding HeadText}"/>
</Style>
</Menu.ItemContainerStyle>
<Menu.ItemsSource>
<CompositeCollection>
<MenuItem Header="Static 1"/>
<MenuItem Header="Static 2"/>
<CollectionContainer Collection="{Binding Source={StaticResource source}}"/>
<MenuItem Header="Static 3"/>
</CompositeCollection>
</Menu.ItemsSource>
</Menu>
</StackPanel>
And the MainVM.cs and ClassA:
public class MainVM
{
public MainVM() {
_mItems.Add(new ClassA() { HeadText = "Dyn1" });
_mItems.Add(new ClassA() { HeadText = "Dyn2" });
_mItems.Add(new ClassA() { HeadText = "Dyn3" });
}
private ObservableCollection<ClassA> _mItems = new ObservableCollection<ClassA>();
public ObservableCollection<ClassA> MItems{
get { return _mItems; }
}
}
public class ClassA
{
public ClassA() { }
private string _text = "";
public String HeadText {
get { return _text; }
set { _text = value; }
}
}
This works fine but you have to notice that Visual Studio will give you BindingExpression errors in the case you really mix static and dynamic menu items.
The reason for this is that the XAML parser applies the specified ItemContainerStyle to the dynamic AND to the static menu items: it creates a new MenuItem and binds the HeadText property to the Header property of the newly created MenuItem. This is done for the dynamic menu items as well as for the static ones. Because there is no HeadText property in the MenuItem class, an error is displayed.
The application will not crash, but it's not a good solution. A workaround for this is to split the MenuBar like this:
<StackPanel>
<Grid DockPanel.Dock="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Menu Grid.Column="0">
<MenuItem Header="Static 1"/>
<MenuItem Header="Static 2"/>
</Menu>
<Menu Grid.Column="1">
<Menu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding HeadText}"/>
</Style>
</Menu.ItemContainerStyle>
<Menu.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource source}}"/>
</CompositeCollection>
</Menu.ItemsSource>
</Menu>
<Menu Grid.Column="2">
<MenuItem Header="Static 3"/>
</Menu>
</Grid>
</StackPanel>
Maybe there is a nicer solution. Let me know if this is so.
Related
I am using caliburn micro on my project. So far I have had no problems with ActivateItemAsync. Now, however, this method does not activate my ActiveItem. Now what my code looks like:
I have a DashboardMainView:
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Ribbon ...>
</Ribbon>
<Grid Grid.Row="1" Background="#E8E8E8">
<ContentControl x:Name="ActiveItem" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
</ContentControl>
</Grid>
Then in DashboardMainViewModel:
public class DashboardMainViewModel : Conductor<object>
{
private IDialogCoordinator dialogCoordinator;
public DashboardMainViewModel(IDialogCoordinator instance)
{
this.dialogCoordinator = instance;
ActivateItemAsync(new DashboardSummaryViewModel());
}
public async Task Execution()
{
await ActivateItemAsync(new BasicExecutionViewModel(DialogCoordinator.Instance));
}
So far everything works, but then BasicExecutionView is activated, where I have a DataGrid and MenuContext in it. And here comes the problem, when we right-click on the DataGrid, a menu pops up where, after selecting an interesting option, it should activate another view ... but it does not. My code looks like this:
<Grid >
<Viewbox Stretch="Fill">
<DataGrid
x:Name="BasicExecutionGrid"
CanUserSortColumns="True"
IsReadOnly="True"
CanUserAddRows="False"
FontSize="11"
Height ="800"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto" AutoGeneratingColumn="BasicExecutionGrid_AutoGeneratingColumn">
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock TextWrapping="Wrap"
Text="{Binding}"
HorizontalAlignment="Center"
VerticalAlignment="Center"></TextBlock>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="Background" Value="LightSteelBlue"/>
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Height" Value="45"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.ContextMenu>
<ContextMenu>
<MenuItem Header="Details"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ContextMenu}}, Path=DataContext.ActivateCellDetailsView}"
>
</MenuItem>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
</Viewbox>
</Grid>
In BasicExecutionViewModel I have:
class BasicExecutionViewModel : Conductor<object>
{
private IDialogCoordinator dialogCoordinator;
private RelayCommand activateCellDetailsView { get; set; }
public BasicExecutionViewModel(IDialogCoordinator instance)
{
this.dialogCoordinator = instance;
}
public ICommand ActivateCellDetailsView
{
get
{
if (activateCellDetailsView == null)
{
activateCellDetailsView = new RelayCommand(async p=> await ActivateCellView());
}
return activateCellDetailsView;
}
}
public async Task ActivateCellView()
{
await ActivateItemAsync(new CellDetailsExecutionViewModel());
}
}
The code comes to ActivateCellView () and activates it. I can see it in Output Window where ActiveItem takes the value of the CellDetailsExecutionViewModel () object, but BasicExecutionView is still displayed on the screen. What am I doing wrong? I guess it's something with either DataContext or parent-child issue ... please help :)
PS. I'm not a professional programmer I'm a hobbyist.
Solved
I solved the problem. My mistake was using Conductor<object> incorrectly. In DashboardMainViewModel. When I corrected on
DashboardMainViewModel: Conductor<object>.Collection.OneActive
ant the same in the BasicExecutionViewModel
BasicExecutionViewModel: Conductor<object>.Collection.OneActive
I also updated the code in the ActivateCellView() method to
public async Task ActivateCellView()
{
CellDetailsExecutionViewModel cellDetailsExecutionViewAcvtivate = new CellDetailsExecutionViewModel();
var parentConductor = (Conductor<object>.Collection.OneActive)(this.Parent);
await parentConductor.ActivateItemAsync(cellDetailsExecutionViewAcvtivate);
}
And everything works beautifully
I have a WPF Application in net core. I use prism and mahapps.
In MainWindow I have a Hamburger menu where I add HamburgerMenuGlyphItem via regions.
In VM for HamburgerMenuGlyphItemView, I set a label property but it doesn't work. The label property doesn't update on MenuItemControl. When I check the DataContext for the DeclarationMenuItemView in LiveVisualTree it says "DeclarationMenuItemView" not the ViewModel...
Here is the MainWindow with DataTemplate For HamburgerMenuItem
<mah:MetroWindow x:Class="PrismJPKEditor.Views.MainWindow"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
prism:ViewModelLocator.AutoWireViewModel="True"
xmlns:viewModels="clr-namespace:PrismJPKEditor.Modules.JPK.ViewModels;assembly=PrismJPKEditor.Modules.JPK"
xmlns:core="clr-namespace:PrismJPKEditor.Core;assembly=PrismJPKEditor.Core"
Title="{Binding Title}" Height="350" Width="525" >
<DockPanel>
<DockPanel.Resources>
<DataTemplate x:Key="MenuItemTemplate" DataType="{x:Type viewModels:MenuItemViewModel}">
<Grid x:Name="RootGrid"
Height="48"
Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type mah:HamburgerMenu}}, Path=CompactPaneLength}" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ContentControl Grid.Column="0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{Binding Icon}"
Focusable="False" />
<TextBlock Grid.Column="1"
VerticalAlignment="Center"
FontSize="16"
Text="{Binding Label}"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type mah:HamburgerMenu}}, Path=IsPaneOpen}" Value="False">
<Setter TargetName="RootGrid" Property="ToolTip" Value="{Binding ToolTip, Mode=OneWay}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</DockPanel.Resources>
<Menu DockPanel.Dock="Top">
<MenuItem Header="Plik">
<MenuItem Header="Otwórz"/>
<MenuItem Header="Zapisz"/>
<MenuItem Header="Zapisz jako"/>
<MenuItem Header="Zamknij"/>
</MenuItem>
</Menu>
<mah:HamburgerMenu DockPanel.Dock="Left" ItemTemplate="{StaticResource MenuItemTemplate}" OptionsItemTemplate="{StaticResource MenuItemTemplate}">
<mah:HamburgerMenu.ItemsSource>
<mah:HamburgerMenuItemCollection prism:RegionManager.RegionName="{x:Static core:RegionNames.HamburgerMenuRegion}"/>
</mah:HamburgerMenu.ItemsSource>
</mah:HamburgerMenu>
<!--<mah:HamburgerMenu DockPanel.Dock="Left" ItemTemplate="{StaticResource MenuItemTemplate}" prism:RegionManager.RegionName="{x:Static core:RegionNames.HamburgerMenuRegion}">
</mah:HamburgerMenu>-->
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.ContentRegion}"/>
</DockPanel>
</mah:MetroWindow>
Here is the MenuItemViewModel from MenuItemTemplate
public class MenuItemViewModel : BindableBase, IHamburgerMenuItemBase
{
private object _icon;
private object _label;
private object _toolTip;
private bool _isVisible = true;
public object Icon
{
get => _icon;
set => SetProperty(ref _icon, value);
}
public object Label
{
get => _label;
set => SetProperty(ref _label, value);
}
public object ToolTip
{
get => _toolTip;
set => SetProperty(ref _toolTip, value);
}
public bool IsVisible
{
get => _isVisible;
set => SetProperty(ref _isVisible, value);
}
}
And here is the ViewModel for MenuItemView when I initialize the Label property which should be working
public class DeclarationMenuItemViewModel : MenuItemViewModel
{
public DeclarationMenuItemViewModel()
{
Label = "Deklaracja";
}
}
And here is the MenuItemView
<mah:HamburgerMenuGlyphItem x:Class="PrismJPKEditor.Modules.JPK.HamburgerMenuItems.DeclarationMenuItemView"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:PrismJPKEditor.Modules.JPK.HamburgerMenuItems"
mc:Ignorable="d"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
>
</mah:HamburgerMenuGlyphItem>
Here is the Region adapter for the HamburgermenuItemCollection
public HamburgerMenuItemCollectionRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory) : base(regionBehaviorFactory)
{
}
protected override void Adapt(IRegion region, HamburgerMenuItemCollection regionTarget)
{
region.Views.CollectionChanged += (s, e) =>
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
foreach (HamburgerMenuGlyphItem element in e.NewItems)
{
regionTarget.Add(element);
}
}
};
}
protected override IRegion CreateRegion()
{
return new SingleActiveRegion();
}
}
Because the View DeclarationMenuItemView isn't in the Views folder, I have set
public void RegisterTypes(IContainerRegistry containerRegistry)
{
ViewModelLocationProvider.Register<DeclarationMenuItemView, DeclarationMenuItemViewModel>();
}
Here is the Module class
public class JPKModule : IModule
{
private readonly IRegionManager _regionManager;
public JPKModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_regionManager.RegisterViewWithRegion(RegionNames.HamburgerMenuRegion, typeof(HamburgerMenuItems.DeclarationMenuItemView));
//_regionManager.RegisterViewWithRegion(RegionNames.HamburgerMenuRegion, typeof(HamburgerMenuItems.SellMenuItem));
//_regionManager.RegisterViewWithRegion(RegionNames.HamburgerMenuRegion, typeof(HamburgerMenuItems.BuyMenuItem));
//_regionManager.RegisterViewWithRegion(RegionNames.HamburgerMenuRegion, typeof(TestView));
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
ViewModelLocationProvider.Register<DeclarationMenuItemView, DeclarationMenuItemViewModel>();
}
}
For some testing I changed the DataTemplate to
<DataTemplate x:Key="HamburgerMenuItem" DataType="{x:Type mah:HamburgerMenuGlyphItem}">
<DockPanel Height="48" LastChildFill="True">
<Grid x:Name="IconPart"
Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type mah:HamburgerMenu}}, Path=CompactPaneLength}"
DockPanel.Dock="Left">
<Image Margin="12"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Source="{Binding Glyph}" />
</Grid>
<TextBlock x:Name="TextPart"
VerticalAlignment="Center"
FontSize="16"
Text="{Binding Label}" />
</DockPanel>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type mah:HamburgerMenu}}, Path=PanePlacement}" Value="Right">
<Setter TargetName="IconPart" Property="DockPanel.Dock" Value="Right" />
<Setter TargetName="TextPart" Property="Margin" Value="8 0 0 0" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
And in the cs file of the View I set
public partial class DeclarationMenuItem : HamburgerMenuGlyphItem
{
public DeclarationMenuItem()
{
InitializeComponent();
Glyph = "M";
Label = "Test";
}
}
And from here Label is working ..
And when I add to the xaml file of the view the line
Label = "{Binding Label"}
I get following error:
Error 1 MainWindowViewModel Label DeclarationMenuItemView.Label String Label property not found on object of type MainWindowViewModel.
But VM for DeclarationMenuItem is DeclarationMenuItemViewModel not the
MainWindowViewModel
It seems to be straightforward to get the Version Number e.g.
string ver Assembly.GetExecutingAssembly().GetName().Version.ToString();
And displaying that string in an "About..." menu item should also be. But Google results all seem complex e.g.
https://stackoverflow.com/questions/2849265/how-to-pass-data-when-using-menuitem-itemcontainerstyle
https://stackoverflow.com/questions/21585828/menuitem-passing-selected-item-to-viewmodel-via-relaycommand-ala-mvvm-light-he
There must be a simple way to do this. Something along the lines of
Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-
compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Menu>
<MenuItem Header="File">
<MenuItem Header="Open"/>
<MenuItem Header="Close"/>
</MenuItem>
<MenuItem Header="About">
<MenuItem Header="Version"/>
</MenuItem>
<Grid>
</Grid>
</Menu>
</Window>
where the last header becomes "Version" + ver.
Or moving into the more complex
How do I dynamically bind and statically add MenuItems?
public partial class MainWindow : Window
{
private ObservableCollection<MyObject> _windows = new ObservableCollection<MyObject>();
public MainWindow()
{
InitializeComponent();
Windows.Add(new MyObject { Title = "Collection Item 1" });
Windows.Add(new MyObject { Title = "Collection Item 2" });
}
public ObservableCollection<MyObject> Windows
{
get { return _windows; }
set { _windows = value; }
}
}
public class MyObject
{
public string Title { get; set; }
}
<Window x:Class="WpfApplication8.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="233" Width="143" Name="UI">
<Window.Resources>
<CollectionViewSource Source="{Binding ElementName=UI, Path=Windows}" x:Key="YourMenuItems"/>
</Window.Resources>
<Grid DataContext="{Binding ElementName=UI}">
<Menu Height="24" VerticalAlignment="Top">
<MenuItem Header="_View" >
<MenuItem Header="Windows">
<MenuItem.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource YourMenuItems}}" />
<MenuItem Header="Menu Item 1" />
<MenuItem Header="Menu Item 2" />
<MenuItem Header="Menu Item 3" />
</CompositeCollection>
</MenuItem.ItemsSource>
<MenuItem.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Header" Value="{Binding Title}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</MenuItem>
</Menu>
</Grid>
(which doesn't display "Collection Item 1" etc for me)
Just for the record, here's an MWE which does the very simple thing I wanted.
using System.Collections.ObjectModel;
using System.Reflection;
using System.Windows;
namespace WpfApp2
{
public partial class MainWindow : Window
{
public ObservableCollection<MyMenuItem> _windows = new ObservableCollection<MyMenuItem>();
public MainWindow()
{
InitializeComponent();
string ver = Assembly.GetExecutingAssembly().GetName().Version.ToString();
MyMenuItem versionMenuItem = new MyMenuItem { Title = "Version " + ver };
Windows.Add(versionMenuItem);
}
public ObservableCollection<MyMenuItem> Windows
{
get { return _windows; }
set { _windows = value; }
}
}
public class MyMenuItem
{
public string Title { get; set; }
}
}
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp2"
Title="MainWindow" Height="233" Width="143" Name="UI">
<Window.Resources>
<CollectionViewSource Source="{Binding ElementName=UI, Path=Windows, FallbackValue=versionMenuItem, TargetNullValue=0}" x:Key="MyMenuItems" />
</Window.Resources>
<Grid DataContext="{Binding ElementName=UI}">
<Menu Height="24" VerticalAlignment="Top">
<MenuItem Header="_Version" >
<MenuItem Header="About">
<MenuItem.ItemsSource>
<CompositeCollection>
<CollectionContainer Collection="{Binding Source={StaticResource MyMenuItems}}" />
<MenuItem Header="Licensed To" />
</CompositeCollection>
</MenuItem.ItemsSource >
<MenuItem.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Header" Value="{Binding Title}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</MenuItem>
</Menu>
</Grid>
</Window>
<Application x:Class="WpfApp2.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp2"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style TargetType="MenuItem">
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
</Style>
</Application.Resources>
</Application>
Setting the FallbackValue to versionMenuItem to eliminate one of the Data issues.
<CollectionViewSource Source="{Binding ElementName=UI, Path=Windows, FallbackValue=versionMenuItem, TargetNullValue=0}" x:Key="MyMenuItems" />
I'm crap atn english, so sorry..
I'm trying to create dynamic menu items that display something based on a collection (a list of object), here is my xaml, you can see the "ListBox" and the "MenuItem".
As I said, I'm binding the header to the "name" of my object :
MainWindow.xaml
<Grid Margin="0,0,-8,-9" Background="Black" >
<DockPanel x:Name="LayoutRoot">
<Grid VerticalAlignment="Top">
<MediaElement x:Name="mediaElement" HorizontalAlignment="Center" Margin="0,0,60,30" VerticalAlignment="Top" MouseLeftButtonUp="mediaElement_MouseLeftButtonUp">
</MediaElement>
<Menu HorizontalAlignment="Left" Height="30" VerticalAlignment="Top" Width="525" IsMainMenu="True">
<MenuItem Header="Menu">
<ListBox x:Name="myList" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="461,-261,-1,106" Background="#FFFFE800" MouseDoubleClick="ListBox_Double_Click" Width="57">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<MenuItem Header="{Binding name}"> </MenuItem>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</MenuItem>
</Menu>
</Grid>
Here is my code in the c#, as you can see I bind "myList" (which is my ListBox) with the ItemsGetSource of list (which contains the things I want to display in my menu):
public MainWindow()
{
var list = new List<History>
{
new History() { name = "Guy1"},
new History() { name = "Guy2"},
new History() { name = "Guy3"},
new History() { name = "Guy4"},
};
InitializeComponent();
this.myList.ItemsSource = list;
}
And my class "History" (the "name" field is what I want to display)
namespace MediaPlayer
{
public class History
{
// "prop" puis "tab"
public String name { get; set; }
public String path { get; set; }
public int time { get; set; }
public override string ToString()
{
return name;
}
}
}
I think I can't use itemSource for my menuItems, maybe?
Here is an example that only uses MenuItems:
MainWindow.xaml
<Grid Margin="0,0,-8,-9" Background="Black" >
<DockPanel x:Name="LayoutRoot">
<Grid VerticalAlignment="Top">
<MediaElement x:Name="mediaElement" HorizontalAlignment="Center" Margin="0,0,60,30" VerticalAlignment="Top" MouseLeftButtonUp="mediaElement_MouseLeftButtonUp">
</MediaElement>
<Menu HorizontalAlignment="Left" Height="30" VerticalAlignment="Top" Width="525" IsMainMenu="True">
<MenuItem Header="Menu" x:Name="myList">
<MenuItem.ItemContainerStyle>
<Style>
<Setter Property="MenuItem.Header" Value="{Binding name}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</Menu>
</Grid>
</DockPanel>
</Grid>
The result looks like this:
Imagine multiple object types in one Listbox. Both have different look and presented information. If I add any of them in ObservableCollection it displays them fine in ListBox.
<DataTemplate DataType="{x:Type local:DogData}" >
<Grid...
</DataTemplate>
<DataTemplate DataType="{x:Type local:CatData}" >
<Grid...
</DataTemplate>
Now I want users to be able to press a button and switch view to see more detailed information and there templates which provide it
<DataTemplate x:Key="TemplateDogDataWithImages" >
<Grid...
</DataTemplate>
<DataTemplate x:Key="TemplateCatDataWithImages" >
<Grid...
</DataTemplate>
but I can assign only one
AnimalsListBox.ItemTemplate = this.Resources["TemplateDogDataWithImages"] as DataTemplate;
I don't want to have one and have a bunch of triggers in it.
I have researched DataTemplateSelectors
http://tech.pro/tutorial/807/wpf-tutorial-how-to-use-a-datatemplateselector
Is there a better way? Is there a way to switch default templates for each data type so I could avoid DataTemplateSelectors?
On click of the button how do I select TemplateDogDataWithImages and TemplateCatDataWithImages as default ones and cliking other button use TemplateDogDataSimple and TemplateCatDataSimple?
I think a selector is easier (less code), but if you really want to use triggers, maybe something like this?
namespace WpfApplication65
{
public abstract class NotifyBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string property)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
public abstract class DataBase : NotifyBase
{
bool m_showDetailed;
public bool ShowDetailed
{
get { return m_showDetailed; }
set
{
m_showDetailed = value;
NotifyPropertyChanged("ShowDetailed");
}
}
}
public class DogData : DataBase { }
public class CatData : DataBase { }
public partial class MainWindow : Window
{
public List<DataBase> Items { get; private set; }
public MainWindow()
{
Items = new List<DataBase>() { new DogData(), new CatData(), new DogData() };
DataContext = this;
InitializeComponent();
}
}
}
<Window x:Class="WpfApplication65.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="clr-namespace:WpfApplication65"
Title="MainWindow"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<DataTemplate DataType="{x:Type l:CatData}">
<DataTemplate.Resources>
<DataTemplate x:Key="basic">
<TextBlock Text="This is the basic cat view" />
</DataTemplate>
<DataTemplate x:Key="detailed">
<TextBlock Text="This is the detailed cat view" />
</DataTemplate>
</DataTemplate.Resources>
<Border BorderThickness="1"
BorderBrush="Red"
Margin="2"
Padding="2">
<StackPanel>
<ContentPresenter Name="PART_Presenter"
ContentTemplate="{StaticResource basic}" />
<CheckBox Content="Show Details"
IsChecked="{Binding ShowDetailed}" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ShowDetailed}"
Value="True">
<Setter TargetName="PART_Presenter"
Property="ContentTemplate"
Value="{StaticResource detailed}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
<DataTemplate DataType="{x:Type l:DogData}">
<DataTemplate.Resources>
<DataTemplate x:Key="basic">
<TextBlock Text="This is the basic dog view" />
</DataTemplate>
<DataTemplate x:Key="detailed">
<TextBlock Text="This is the detailed dog view" />
</DataTemplate>
</DataTemplate.Resources>
<Border BorderThickness="1"
BorderBrush="Blue"
Margin="2"
Padding="2">
<StackPanel>
<ContentPresenter Name="PART_Presenter"
ContentTemplate="{StaticResource basic}" />
<CheckBox Content="Show Details"
IsChecked="{Binding ShowDetailed}" />
</StackPanel>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding ShowDetailed}"
Value="True">
<Setter TargetName="PART_Presenter"
Property="ContentTemplate"
Value="{StaticResource detailed}" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</Window.Resources>
<ListBox ItemsSource="{Binding Items}" />
</Window>