WPF Dynamic menu associated with the object? - wpf

I'd like to be able to create dynamic menus associated with certain object. Let’s say, I will have 3 listview container with one style where I also have a Menu. I need to generate different menu items from collection of the RoutetUICommands in relation on each listview. I was trying to solve this puzzle but took me a while and still have trouble making it work. I need to generate object specific menus, an unique menu for each listview. Any ideas are highly appreciated. Thank you!
XAML:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Microsoft_Windows_Themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"
x:Class="DynamicMenu.MainWindow"
x:Name="Window"
Title="MainWindow"
Width="640" Height="480">
<Window.Resources>
<Style x:Key="ListViewStyleTask" TargetType="{x:Type ListView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListView}">
<Grid>
<Menu x:Name="mainMenu">
<MenuItem x:Name="menuItem" Header="Tasks" ItemsSource="{Binding Commands}" >
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding}" />
<Setter Property="Header" Value="{Binding Path=Text}" />
<Setter Property="CommandParameter" Value="{Binding Path=Parameter}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</Menu>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<ListView x:Name="Container1" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Style="{DynamicResource ListViewStyleTask}">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
<ListView x:Name="Container2" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Style="{DynamicResource ListViewStyleTask}">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
<ListView x:Name="Container3" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Style="{DynamicResource ListViewStyleTask}">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
</Grid>
I have added some commands which I need to associate to 3 different Listviews:
public partial class MainWindow : Window
{
// Container 1
public static RoutedUICommand NameCommand = new RoutedUICommand("Name", "NameCommand", typeof(MainWindow));
public static RoutedUICommand StreetCommand = new RoutedUICommand("Street", "StreetCommand", typeof(MainWindow));
public static RoutedUICommand GroupCommand = new RoutedUICommand("Add to Group", "AddGroup", typeof(MainWindow));
// Container 2
public static RoutedUICommand ViewDetailsCommand = new RoutedUICommand("View Details", "ViewDetailsCommand", typeof(MainWindow));
// Container 3
public static RoutedUICommand StartCommand = new RoutedUICommand("Start", "StartCommand", typeof(MainWindow));
public static RoutedUICommand StopCommand = new RoutedUICommand("Stop", "StopCommand", typeof(MainWindow));
public static RoutedUICommand LoadCommand = new RoutedUICommand("Load", "LoadCommand", typeof(MainWindow));
public MainWindow()
{
this.InitializeComponent();
// Insert code required on object creation below this point.
}
}
}

You need to define a structure to group the data as you need in your control template. Something like this,
public class CommandCollection {
public ObservableCollection<Command> Commands { get; set; }
}
public class Command {
public ICommand Action { get; set; }
public string Text { get; set; }
public string Parameter { get; set; }
}
Have 3 members of CommandCollection each one having its commands and then assign those as datacontext to the ListViews
Updated,
After declaring the above structure you declare 3 members,
public CommandCollection Container1Commands { get; set; }
public CommandCollection Container2Commands { get; set; }
public CommandCollection Container3Commands { get; set; }
Fill these members,
Container1Commands = new CommandCollection ();
Container1Commands.Commands = new ObservableCollection<CommandParameters> ();
Container1Commands.Commands.Add (new CommandParameters () { Action = NameCommand, Text = "Name" });
Container1Commands.Commands.Add (new CommandParameters () { Action = StreetCommand, Text = "Street" });
Container1Commands.Commands.Add (new CommandParameters () { Action = GroupCommand, Text = "Group" });
Container2Commands = new CommandCollection ();
Container2Commands.Commands = new ObservableCollection<CommandParameters> ();
Container2Commands.Commands.Add (new CommandParameters () { Action = ViewDetailsCommand, Text = "ViewDetails" });
Container3Commands = new CommandCollection ();
Container3Commands.Commands = new ObservableCollection<CommandParameters> ();
Container3Commands.Commands.Add (new CommandParameters () { Action = StartCommand, Text = "Start" });
Container3Commands.Commands.Add (new CommandParameters () { Action = StopCommand, Text = "Stop" });
Container3Commands.Commands.Add (new CommandParameters () { Action = LoadCommand, Text = "Load" });
Set data context,
this.DataContext = this;
this.Container1.DataContext = Container1Commands;
this.Container2.DataContext = Container2Commands;
this.Container3.DataContext = Container3Commands;
Update your control template to specify menu item command binding,
<Setter Property="Command" Value="{Binding Action}" />
Updated
XAML
<Window.Resources>
<Style x:Key="ListViewStyleTask" TargetType="{x:Type ListView}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListView}">
<Grid>
<Menu x:Name="mainMenu" >
<MenuItem x:Name="menuItem" Header="Tasks" ItemsSource="{Binding Commands}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Action}" />
<Setter Property="Header" Value="{Binding Path=Text}" />
<Setter Property="CommandParameter" Value="{Binding Path=Parameter}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</Menu>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid x:Name="LayoutRoot">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<ListView
Grid.Row="0" x:Name="Container1" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Style="{DynamicResource ListViewStyleTask}">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
<ListView
Grid.Row="1" x:Name="Container2" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Style="{DynamicResource ListViewStyleTask}">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
<ListView
Grid.Row="2" x:Name="Container3" HorizontalAlignment="Left" Height="100" VerticalAlignment="Top" Width="100" Style="{DynamicResource ListViewStyleTask}">
<ListView.View>
<GridView>
<GridViewColumn/>
</GridView>
</ListView.View>
</ListView>
</Grid>
Code behind
// Container 1
public static RoutedUICommand NameCommand = new RoutedUICommand ("Name", "NameCommand", typeof (Window1));
public static RoutedUICommand StreetCommand = new RoutedUICommand ("Street", "StreetCommand", typeof (Window1));
public static RoutedUICommand GroupCommand = new RoutedUICommand ("Add to Group", "AddGroup", typeof (Window1));
// Container 2
public static RoutedUICommand ViewDetailsCommand = new RoutedUICommand ("View Details", "ViewDetailsCommand", typeof (Window1));
// Container 3
public static RoutedUICommand StartCommand = new RoutedUICommand ("Start", "StartCommand", typeof (Window1));
public static RoutedUICommand StopCommand = new RoutedUICommand ("Stop", "StopCommand", typeof (Window1));
public static RoutedUICommand LoadCommand = new RoutedUICommand ("Load", "LoadCommand", typeof (Window1));
public Window1 () {
InitializeComponent ();
this.Loaded += new RoutedEventHandler (Window1_Loaded);
}
public CommandCollection Container1Commands { get; set; }
public CommandCollection Container2Commands { get; set; }
public CommandCollection Container3Commands { get; set; }
void Window1_Loaded (object sender, RoutedEventArgs e) {
Container1Commands = new CommandCollection ();
Container1Commands.Commands = new ObservableCollection<Command> ();
Container1Commands.Commands.Add (new Command () { Action = NameCommand, Text = "Name" });
Container1Commands.Commands.Add (new Command () { Action = StreetCommand, Text = "Street" });
Container1Commands.Commands.Add (new Command () { Action = GroupCommand, Text = "Group" });
Container2Commands = new CommandCollection ();
Container2Commands.Commands = new ObservableCollection<Command> ();
Container2Commands.Commands.Add (new Command () { Action = ViewDetailsCommand, Text = "ViewDetails" });
Container3Commands = new CommandCollection ();
Container3Commands.Commands = new ObservableCollection<Command> ();
Container3Commands.Commands.Add (new Command () { Action = StartCommand, Text = "Start" });
Container3Commands.Commands.Add (new Command () { Action = StopCommand, Text = "Stop" });
Container3Commands.Commands.Add (new Command () { Action = LoadCommand, Text = "Load" });
this.CommandBindings.Add (new CommandBinding (NameCommand, ExecuteNameCommand, CanExecuteNameCommand));
this.CommandBindings.Add (new CommandBinding (StreetCommand, ExecuteStreetCommand, CanExecuteStreetCommand));
this.CommandBindings.Add (new CommandBinding (GroupCommand, ExecuteGroupCommand, CanExecuteGroupCommand));
this.DataContext = this;
this.Container1.DataContext = Container1Commands;
this.Container2.DataContext = Container2Commands;
this.Container3.DataContext = Container3Commands;
}
private void ExecuteNameCommand (object inSender, RoutedEventArgs inE) {
MessageBox.Show ("Name command Executed");
}
private void CanExecuteNameCommand (object inSender, CanExecuteRoutedEventArgs inE) {
inE.CanExecute = true;
}
private void ExecuteStreetCommand (object inSender, RoutedEventArgs inE) {
MessageBox.Show ("Street command Executed");
}
private void CanExecuteStreetCommand (object inSender, CanExecuteRoutedEventArgs inE) {
inE.CanExecute = true;
}
private void ExecuteGroupCommand (object inSender, RoutedEventArgs inE) {
MessageBox.Show ("Group command Executed");
}
private void CanExecuteGroupCommand (object inSender, CanExecuteRoutedEventArgs inE) {
inE.CanExecute = true;
}
Other classes
public class CommandCollection {
public ObservableCollection<Command> Commands { get; set; }
}
public class Command {
public ICommand Action { get; set; }
public string Text { get; set; }
public string Parameter { get; set; }
}
I Hope now you get it working.
Updated for RoutedUICommand description,
The idea should be to have these menu items in the outer container (like shell) which will have other pages in it (like a frame/canvas), like for example if you see MS Visual Studio the menu items (Save) are part of the application shell and the files openeed are within the shell (shell has a container tabcontrol maybe, where the files are loaded as they are opened). So the routed commands (Save) are defined by the application shell and all the other pages inside the shell's container add those commands in there command binding collection (this.CommandBindings.Add(cmdname, actionname, predicatename)) so each page performs its own respective action and the command is invoked for them only when they are in focus.

Related

WPF binding of property with multiple usercontrols level

I have two usercontrols (LoginView.xaml and DashboardView.xaml).
DashboardView.xaml is placed inside LoginView.xaml .
Now, my objective is initially DashboardView should be invisible and on login success it should be visible and the controls in stackpanel "loginSP" should be invisible.
LoginView.xaml is as follows :-
<UserControl x:Class="DashboardModule.LoginView"
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:DashboardModule"
xmlns:converter="clr-namespace:Matrix.Infrastructure.Framework.Utility;assembly=Matrix.Infrastructure"
mc:Ignorable="d"
x:Name="loginUC"
d:DesignHeight="450" d:DesignWidth="800" Background="Pink">
<UserControl.Resources>
<ResourceDictionary>
<converter:BooleanToVisibilityConverter x:Key="BoolVisibilityConverter"></converter:BooleanToVisibilityConverter>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel Margin="200">
<StackPanel x:Name="loginSP">
<TextBlock Text="Dashboard" ></TextBlock>
<TextBox x:Name="tbUserName" Height="25" Width="300" ></TextBox>
<PasswordBox Margin="2"></PasswordBox>
<Button Content="Login" Command="{Binding LoginCommand}" CommandParameter="{Binding Path=Text,ElementName=tbUserName}" Width="200"
></Button>
</StackPanel>
<ContentControl>
<ContentControl.Template>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid>
<Border >
<ContentPresenter/>
</Border>
</Grid>
</ControlTemplate>
</ContentControl.Template>
<local:DashboardView Visibility="{Binding DashboardVisible, Converter={StaticResource BoolVisibilityConverter}}"/>
</ContentControl>
</StackPanel>
DashboardView.xaml is as follows :-
<UserControl x:Class="DashboardModule.DashboardView"
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:DashboardModule"
xmlns:converter="clr-namespace:Matrix.Infrastructure.Framework.Utility;assembly=Matrix.Infrastructure"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" Background="BlanchedAlmond" x:Name="DashboardUC"
Visibility="{Binding Visibility,ElementName=rootGrd}">
<UserControl.Resources>
<ResourceDictionary>
<converter:BooleanToVisibilityConverter x:Key="BoolVisibilityConverter"></converter:BooleanToVisibilityConverter>
</ResourceDictionary>
</UserControl.Resources>
<Grid x:Name="rootGrd" ToolTip="{Binding MyName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Visibility="{Binding DashboardVisible,Converter={StaticResource BoolVisibilityConverter}}">
<TextBlock Text="{Binding MyName, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"></TextBlock>
</Grid>
My LoginViewModel.cs is as follows :--
private readonly IEventAggregator eventAggregator;
public DelegateCommand<string> LoginCommand { get; set; }
public DashboardViewModel DashborardVM { get; set; }
public LoginViewModel(IEventAggregator eventAggregator)
{
this.eventAggregator = eventAggregator;
DashborardVM = new DashboardViewModel();
LoginCommand = new DelegateCommand<string>(Login, IsValid);
}
private void Login(string obj)
{
DashborardVM.MyName = "My Name After Login";
DashborardVM.DashboardVisible = true;
//this.eventAggregator.GetEvent<ShellLayoutChangeEvent>().Publish("anindya");
}
private bool IsValid(string param)
{
if (param.Length > 0)
My code of DashboardViewModel.cs is as follows :-
private bool _dashboardVisible;
public bool DashboardVisible
{
get
{
return _dashboardVisible;
}
set
{
_dashboardVisible = value;
OnPropertyChanged("DashboardVisible");
}
}
private string myName;
public string MyName
{
get
{
return myName;
}
set
{
myName = value;
OnPropertyChanged("MyName");
}
}
public DashboardViewModel()
{
MyName = "anindya";
}
Now the output is :
1.Initially DashboardView is invisible .
2. On login success DashboardView is not getting visible although I am setting DashborardVM.DashboardVisible = true; on my loginviewmode.cs.
I am using Prism design pattern.
the following way I am registering the views and viewmodel.
public class ModuleDashboardModule: IModule
{
IUnityContainer _container;
IRegionManager _regionManager;
public ModuleDashboardModule(IUnityContainer container,
IRegionManager regionManager)
{
_container = container;
_regionManager = regionManager;
}
public void Initialize()
{
_container.RegisterType<ILoginView, LoginView>();
_container.RegisterType<ILoginViewModel, LoginViewModel>();
_container.RegisterType<IDashboardView, DashboardView>();
_container.RegisterType<IDashboardViewModel, DashboardViewModel>();
_regionManager.RegisterViewWithRegion(RegionNames.DashboardRegion,
typeof(LoginView));
_regionManager.RegisterViewWithRegion(RegionNames.DashboardRegion,
typeof(DashboardView));
}
}
this is my bootstrapper.cs class :--
public class BootStrapper : UnityBootstrapper,IDisposable
{
protected override DependencyObject CreateShell()
{
return Container.Resolve<Shell>();
}
protected override void InitializeShell()
{
base.InitializeShell();
App.Current.MainWindow = (Window)Shell;
App.Current.MainWindow.Show();
}
protected override void ConfigureModuleCatalog()
{
base.ConfigureModuleCatalog();
Type moduleAType = typeof(ModuleDetailsModule);
Type moduleNewForm = typeof(ModuleNewFormModule);
Type moduleToolbarType = typeof(ModuleToolbarModule);
Type moduleFooterType = typeof(ModuleFooterModule);
Type moduleDashboardType = typeof(ModuleDashboardModule);
Type moduleInvestmentType = typeof(ModuleInvestmentModule);
Type moduleInvestmentDetailsType = typeof(ModuleInvestmentDetailsModule);
ModuleCatalog.AddModule
(
new ModuleInfo()
{
ModuleName = moduleAType.Name,
ModuleType = moduleAType.AssemblyQualifiedName,
InitializationMode = InitializationMode.WhenAvailable
}
);
ModuleCatalog.AddModule
(
new ModuleInfo()
{
ModuleName = moduleNewForm.Name,
ModuleType = moduleNewForm.AssemblyQualifiedName,
InitializationMode = InitializationMode.WhenAvailable
}
);
ModuleCatalog.AddModule
(
new ModuleInfo()
{
ModuleName = moduleToolbarType.Name,
ModuleType = moduleToolbarType.AssemblyQualifiedName,
InitializationMode = InitializationMode.WhenAvailable
}
);
ModuleCatalog.AddModule
(
new ModuleInfo()
{
ModuleName = moduleFooterType.Name,
ModuleType = moduleFooterType.AssemblyQualifiedName,
InitializationMode = InitializationMode.WhenAvailable
}
);
ModuleCatalog.AddModule
(
new ModuleInfo()
{
ModuleName = moduleDashboardType.Name,
ModuleType = moduleDashboardType.AssemblyQualifiedName,
InitializationMode = InitializationMode.WhenAvailable
}
);
ModuleCatalog.AddModule
(
new ModuleInfo()
{
ModuleName = moduleInvestmentType.Name,
ModuleType = moduleInvestmentType.AssemblyQualifiedName,
InitializationMode = InitializationMode.WhenAvailable
}
);
ModuleCatalog.AddModule
(
new ModuleInfo()
{
ModuleName = moduleInvestmentDetailsType.Name,
ModuleType = moduleInvestmentDetailsType.AssemblyQualifiedName,
InitializationMode = InitializationMode.WhenAvailable
}
);
}
My LoginView.xaml.cs file is :
public partial class LoginView : UserControl,ILoginView
{
[InjectionConstructor]
public LoginView(LoginViewModel viewModel)
{
InitializeComponent();
this.ViewModel = viewModel;
}
public IViewModel ViewModel
{
get
{
return (IViewModel)DataContext;
}
set
{
DataContext = value;
}
}
}
My DashboardView.xaml.cs is as follows:
public partial class DashboardView : UserControl,IDashboardView
{
DashboardViewModel viewModel = new DashboardViewModel();
[InjectionConstructor]
public DashboardView()
{
InitializeComponent();
this.DataContext = viewModel;
//this.ViewModel = viewModel;
}
public IViewModel ViewModel
{
get
{
return (IViewModel)DataContext;
}
set
{
DataContext = value;
}
}
}
I am not getting where I am making mistake . Any help is appreciable .
I would try to avoid handling Visibility too much in a ViewModel.
Instead, expose a bool e.g. LoggedIn and bind to it in your View.
There you can use a Style/DataTrigger to change Visibilies accordingly.
<Style x:Key="LoginVisibilityStyle" TargetType="StackPanel">
<Style.Triggers>
<DataTrigger Binding="{Binding LoggedIn}" Value="false">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding LoggedIn}" Value="true">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
if you have to go for a different Controls DataContext i.e. ViewModel, you can do this:
="{Binding DataContext.LoggedIn, UpdateSourceTrigger=PropertyChanged, RelativeSource= {RelativeSource FindAncestor, AncestorType={x:Type views:MainView}}}"
This is all about setting the correct data context.
Seems you are not setting the datacontext for the dashboard view. Therefore the loginViewModel becomes the datacontext of the dashboardview. This is because the datacontext is inherited from the xaml's parent control if not set explicitly.
Try setting the data context for content control
<ContentControl DataContext="{Binding DashborardVM}">
<ContentControl.Template>
<ControlTemplate TargetType="{x:Type ContentControl}">
<Grid>
<Border >
<ContentPresenter/>
</Border>
</Grid>
</ControlTemplate>
</ContentControl.Template>
<local:DashboardView Visibility="{Binding DashboardVisible, Converter={StaticResource BoolVisibilityConverter}}"/>
</ContentControl>
Otherwise you will have to make all the bindings in the format
{Binding DashborardVM.DashboardVisible}
{Binding DashborardVM.property}
EDIT -
By the way if you are using prism please check whether the viewmodel is correctly registered so they get automatically assigned.
And probably setting the visibility as
<local:DashboardView Visibility="{Binding DashborardVM.DashboardVisible, Converter={StaticResource BoolVisibilityConverter}}"/>
will help because here the datacontext is LoginViewmodel

Adding Icon to Dynamic Menu item in WPF

I am trying to generate MenuItem dynamically.
How can I Bind that in Style?
Here is the code.
XAML
<Window.Resources>
<Image x:Key="Image.Icon"
Source="pack://application:,,,/DynamicMenu;component/icon.png"/>
</Window.Resources>
<DockPanel>
<Menu DockPanel.Dock="Top" ItemsSource="{Binding MenuItems}">
<Menu.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding Command}" />
<Setter Property="Icon">
<Setter.Value>
<Image Source="{Binding ImagePath}" Width="12" Height="12" />
</Setter.Value>
</Setter>
</Style>
</Menu.ItemContainerStyle>
<Menu.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:MenuItemViewModel}"
ItemsSource="{Binding Path=MenuItems}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Header}"/>
</StackPanel>
</HierarchicalDataTemplate>
</Menu.ItemTemplate>
</Menu>
<Grid>
</Grid>
</DockPanel>
ViewModel
public class MenuItemViewModel
{
private readonly ICommand _command;
public MenuItemViewModel()
{
_command = new CommandViewModel(Execute);
}
public string Header { get; set; }
public string Param1 { get; set; }
public string ImagePath { get; set; }
public ObservableCollection<MenuItemViewModel> MenuItems { get; set; }
public ICommand Command
{
get {return _command; }
}
private void Execute()
{
MessageBox.Show("Clicked at " + Header + Param1);
}
}
Command
public class CommandViewModel : ICommand
{
private readonly Action _action;
public CommandViewModel(Action action)
{
_action = action;
}
public void Execute(object o)
{
_action();
}
public bool CanExecute(object o)
{
return true;
}
public event EventHandler CanExecuteChanged
{
add { }
remove { }
}
}
I want to add a different icon for different MenuItem.
So I am planning to pass the icon file as MenuItemViewModel property.
Need a way to bind the icon property to the MenuItem.
Thanks.
I find a solution for my question:
<MenuItem Header="{Binding Path=Header}" Command="{Binding PresentationTripLegCommand}">
<MenuItem.Icon>
<Image Source="{Binding IconFileName}" Height="16" />
</MenuItem.Icon>
</MenuItem>

Finding the list of checked item in list of lists

Let's say I have a questionnaire app, which consists of an ItemsControl with a list of controls each consisting of a Label and a ListBox. The items in each ListBox are checkboxes or radiobuttons or whatever.
My question is: When a checkbox is checked, how do I figure out which Question the checkbox applies to? Should I put a reference to the Question in the Tag property? If so, how would I do that?
The Tag binding code below doesn't work. It binds to the ListBoxItem. How do I bind it to the ItemsControl item?
MainWindow.xaml:
<Window x:Class="ListWithinListTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<Style x:Key="ConditionCheckBoxListStyle" TargetType="{x:Type ListBox}">
<Setter Property="SelectionMode" Value="Multiple" />
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<CheckBox IsChecked="{Binding IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
Click="CheckBoxClicked"
Tag="{Binding RelativeSource={RelativeSource TemplatedParent}}"
>
<ContentPresenter></ContentPresenter>
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ItemsControl Name="QuizControl" ItemsSource="{Binding QuizQuestions}" ScrollViewer.CanContentScroll="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10 0 10 10" VerticalAlignment="Top">
<Label Content="{Binding Text}" />
<ListBox ItemsSource="{Binding Options}"
DisplayMemberPath="Text"
Tag="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl}}"
Style="{StaticResource ConditionCheckBoxListStyle}"
/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
using System.Windows.Controls;
namespace ListWithinListTest
{
public class Option
{
public string Text { get; set; }
public bool IsSelected { get; set; } = false;
}
public class Question
{
public string Text { get; set; }
public Option[] Options { get; set; }
}
public class ViewModel
{
public Question[] QuizQuestions { get; set; }
public ViewModel()
{
QuizQuestions = new Question[] {
new Question { Text = "How are you?", Options = new Option[] { new Option { Text = "Good" }, new Option { Text = "Fine" } } },
new Question { Text = "How's your dog?", Options = new Option[] { new Option { Text = "Sleepy" }, new Option { Text = "Hungry" } } },
};
}
}
public partial class MainWindow : Window
{
private ViewModel viewModel;
public MainWindow()
{
InitializeComponent();
this.DataContext = viewModel = new ViewModel();
}
private void CheckBoxClicked(object sender, RoutedEventArgs e)
{
Question question = viewModel.QuizQuestions[???];
}
}
}
Ok, I messed with the Live Visual Tree window until, by process of elimination, I realized that the ListBox is what the Question is going to be bound to. I know now that's a big duh, but that's where I am with this. Then I used RelativeSource AncestorType to find it and the DataSource property to get the question:
MainWindow.xaml:
<CheckBox IsChecked="{Binding IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
Click="CheckBoxClicked"
Tag="{Binding RelativeSource={RelativeSource AncestorType=ListBox}}"
>
MainWindow.xaml.cs:
private void CheckBoxClicked(object sender, RoutedEventArgs e)
{
CheckBox checkBox = (CheckBox)sender;
ListBox listBox = (ListBox)checkBox.Tag;
Question question = (Question)listBox.DataContext;
Debug.WriteLine(question.Text);
}
You have binded QuizQuestions to the QuizControl, you can get it back from the ItemsSource property.
var questions = (Question[]) QuizControl.ItemsSource;
EDIT
It looks like you got the answer yourself, just another way I would like to suggest to your original question:
Create one more property to your Option class
public class Option
{
public string Text { get; set; }
public bool IsSelected { get; set; } = false;
public int Index{ get; set; }
}
And then add Index to each of your question options.
QuizQuestions = new Question[] {
new Question { Text = "How are you?", Options = new Option[] { new Option { Text = "Good", Index = 0 }, new Option { Text = "Fine", Index = 0 } } },
new Question { Text = "How's your dog?", Options = new Option[] { new Option { Text = "Sleepy", Index = 1 }, new Option { Text = "Hungry", Index = 1 } } },
};
In your CheckBox event you can get the Option Index
private void CheckBoxClicked(object sender, RoutedEventArgs e)
{
var s = (CheckBox)sender;
var op = (Option)s.Tag;
Question question = viewModel.QuizQuestions[op.Index];
}

Get Values in TextBox inside ListView

I am created a ListView with TextBox Control.I need to get values in TextBox.
After user typed on Textbox.I need to get whats the user typed.
<Window x:Class="LdiaryEditableListView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Ldiary Editable ListView Sample" Height="350" Width="300">
<StackPanel >
<ListView Name="listView">
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<EventSetter Event="Button.Click" Handler="Button_Click"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=FirstName}" Value="">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Width="100" Text="{Binding Path=FirstName}"/>
<TextBox Width="100" Text="{Binding Path=LastName}"/>
<Button Width="70" >Add</Button>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Width="100" Header="First Name" DisplayMemberBinding="{Binding Path=FirstName}"></GridViewColumn>
<GridViewColumn Width="100" Header="Last Name" DisplayMemberBinding="{Binding Path=LastName}"></GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
Iam created a function to get each element in textbox in listview.But it dows not work
foreach(DataRowView itm in lstvQualification.Items)
{
MessageBox.Show(itm[0].ToString());
}
I got solution for above my problem.
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace LdiaryEditableListView
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
ObservableCollection<Person> people;
public MainWindow()
{
InitializeComponent();
people = new ObservableCollection<Person>(){
new Person{FirstName="", LastName=""},
new Person{FirstName = "Ldiary", LastName="Translations"},
new Person{FirstName = "English", LastName="Japanese"}
};
listView.ItemsSource = people;
}
public class Person : INotifyPropertyChanged
{
private string _firstName;
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
}
}
private string _lastname;
public string LastName
{
get
{
return _lastname;
}
set
{
_lastname = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Person newPerson = people[0];
if (string.IsNullOrEmpty(newPerson.FirstName) || string.IsNullOrEmpty(newPerson.LastName))
{
newPerson.FirstName = "";
newPerson.LastName = "";
MessageBox.Show("Please provide both first name and last name!");
}
else
{
Person emptyPerson = new Person()
{
FirstName = "",
LastName = ""
};
people.Insert(0, emptyPerson);
}
listView.ItemsSource = null;
listView.ItemsSource = people;
}
}
}

Binding/Triggering "Select all"-CheckBox ComboBoxItem in WPF

I'm trying to make a WPF CustomControl CheckComboBox with a "Select All" item in addition to a user defined list of items. When "Select All" is selected, all items in the list should be checked accordingly. How can I act to the "Select All" item being clicked? I have tried a lot of things, but the property "SelectAll" in the CheckComboBox.cs is never entered.
This is my current code.
Generic.xaml
<Style TargetType="{x:Type local:CheckComboBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CheckComboBox}">
<ComboBox SelectedItem="{TemplateBinding SelectedItem}"
SelectedValue="{TemplateBinding SelectedValue}"
SelectedValuePath="{TemplateBinding SelectedValuePath}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
IsTextSearchEnabled="{TemplateBinding IsTextSearchEnabled}"
ItemTemplate="{TemplateBinding ItemTemplate}"
x:Name="InnerComboBox" >
<ComboBox.Resources>
<ResourceDictionary>
<CheckBox x:Key="allItem" Content="All" IsChecked="{Binding SelectAll, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
<CollectionViewSource x:Key="items" Source="{Binding ComboBoxItems, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
</ResourceDictionary>
</ComboBox.Resources>
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem Content="{Binding Source={StaticResource allItem}}"/>
<CollectionContainer Collection="{Binding Source={StaticResource items}}" />
</CompositeCollection>
</ComboBox.ItemsSource>
</ComboBox>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" Content="{Binding Text}" VerticalAlignment="Center" />
</StackPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
CheckComboBox.cs
public class CheckComboBox : ComboBox
{
public class CheckComboBoxItem
{
public CheckComboBoxItem(bool isSelected, string text)
{
IsSelected = isSelected;
Text = text;
}
public bool IsSelected { get; set; }
public string Text { get; set; }
}
static CheckComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CheckComboBox), new FrameworkPropertyMetadata(typeof(CheckComboBox)));
}
public static readonly DependencyProperty ComboBoxItemsProperty =
DependencyProperty.Register("ComboBoxItems", typeof (ObservableCollection<CheckComboBoxItem>), typeof (CheckComboBox), new PropertyMetadata(default(ObservableCollection<CheckComboBoxItem>)));
public ObservableCollection<CheckComboBoxItem> ComboBoxItems
{
get { return (ObservableCollection<CheckComboBoxItem>) GetValue(ComboBoxItemsProperty); }
set { SetValue(ComboBoxItemsProperty, value); }
}
public static readonly DependencyProperty SelectAllProperty =
DependencyProperty.Register("SelectAll", typeof (bool), typeof (CheckComboBox), new PropertyMetadata(default(bool)));
public bool SelectAll
{
get { return (bool) GetValue(SelectAllProperty); }
set
{
foreach (var item in ComboBoxItems)
{
item.IsSelected = value;
}
SetValue(SelectAllProperty, value);
}
}
}
}
Setting test data:
ObservableCollection<CheckComboBox.CheckComboBoxItem> checkComboBoxItems = new ObservableCollection<CheckComboBox.CheckComboBoxItem>();
checkComboBoxItems.Add(new CheckComboBox.CheckComboBoxItem(false, "Generation 0"));
checkComboBoxItems.Add(new CheckComboBox.CheckComboBoxItem(true, "Generation 1"));
checkComboBoxItems.Add(new CheckComboBox.CheckComboBoxItem(false, "Generation 2"));
checkComboBox1.ComboBoxItems = checkComboBoxItems;
Edit:
Replaced the SelectAll DependencyProperty in CheckComboBox.cs with the following code, but OnSelectAll is not entered. The SelectAll combobox does not trigger the binding for some reason.
public static readonly DependencyProperty SelectAllProperty =
DependencyProperty.Register("SelectAll",
typeof (bool),
typeof (CheckComboBox),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnSelectAll)));
private static void OnSelectAll(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CheckComboBox checkComboBox = (CheckComboBox)d;
foreach (var item in checkComboBox.ComboBoxItems)
{
item.IsSelected = (bool) e.NewValue;
}
}
public bool SelectAll
{
get { return (bool) GetValue(SelectAllProperty); }
set { SetValue(SelectAllProperty, value); }
}
Finally figured out how to trigger the "SelectAll" property. Notice the:
<ComboBoxItem>
<CheckBox ... />
</ComboBoxItem>
Generic.xaml
...
<ComboBox.Resources>
<ResourceDictionary>
<CollectionViewSource x:Key="items" Source="{Binding ItemsSource, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
</ResourceDictionary>
</ComboBox.Resources>
<ComboBox.ItemsSource>
<CompositeCollection>
<ComboBoxItem>
<CheckBox Content="All" IsChecked="{Binding SelectAll, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}" />
</ComboBoxItem>
<CollectionContainer Collection="{Binding Source={StaticResource items}}" />
</CompositeCollection>
</ComboBox.ItemsSource>
...
CheckComboBox.cs
public class CheckComboBox : ComboBox
{
public class CheckComboBoxItem : ModelBase
{
public CheckComboBoxItem(bool isSelected, string text)
{
IsSelected = isSelected;
Text = text;
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set { Set(() => IsSelected, ref _isSelected, value); }
}
private string _text;
public string Text
{
get { return _text; }
set { Set(() => Text, ref _text, value); }
}
}
static CheckComboBox()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CheckComboBox), new FrameworkPropertyMetadata(typeof(CheckComboBox)));
}
public static readonly DependencyProperty SelectAllProperty =
DependencyProperty.Register("SelectAll",
typeof (bool),
typeof (CheckComboBox),
new FrameworkPropertyMetadata(false,
FrameworkPropertyMetadataOptions.AffectsRender,
new PropertyChangedCallback(OnSelectAll)));
private static void OnSelectAll(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CheckComboBox checkComboBox = (CheckComboBox)d;
IEnumerable<CheckComboBoxItem> items = (IEnumerable<CheckComboBoxItem>) checkComboBox.ItemsSource;
foreach (var item in items)
{
item.IsSelected = (bool) e.NewValue;
}
}
public bool SelectAll
{
get { return (bool) GetValue(SelectAllProperty); }
set { SetValue(SelectAllProperty, value); }
}
}
Now I just have to figure out how to automatically de-select the "Select All" check box when another item is de-selected.

Resources