I was wondering how can I get the "Selected" MenuItem from a Menu.
Basically, I want to get the "Selected" MenuItem so I can sort my ListBox.
Here is my XAML for the Menu.
<Menu>
<MenuItem Header="Sort by" ItemsSource="{Binding SortByOptions}"
*SelectedItem="{Binding GroupBy}"*/>
</Menu>
I Switched my ComboBox with a Menu, but in Menu, "SelectedItem" does not exist like in ComboBox. I was wondering how could I get what Item from menu was chosen.
C#
The ItemsSource Binding "SortByOptions" is an ObservableCollection of string who contains the options to sort.
The binding "GroupBy" is a String that is set each time the user chose another MenuItem.
I am searching to set the variable "GroupBy" every time the user chose another MenuItem.
Before, my ComboBox worked well.
SOLUTION
I needed to specify the style of the property "Command" and "CommandParameter" like this:
<Menu Layout="Text" Margin="10,0,0,0">
<MenuItem Header="Group by" ItemsSource="{Binding GroupByOptions}">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command"
Value="{Binding ViewModel.GroupCommand, RelativeSource={RelativeSource AncestorType={x:Type Views:MyView}}}" />
<Setter Property="CommandParameter" Value="{Binding}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</Menu>
Note that the CommandParameter is the actual "Header" chosen by the user. (This is what I was searching for) I did not know, but when you do {Binding} it takes the actual string.
And in my ViewModel, here is what it looks like:
private ICommand mSortCommand;
//Implement get and set with NotifyPropertyChanged for mSortableList
private ICollectionView mSortableList;
public ICommand SortCommand
{
get { return mSortCommand ?? (mSortCommand = new RelayCommand(SortMyList)); }
}
public void SortMyList(object sortChosen)
{
string chosenSort = sortChosen as string;
CampaignSortableList.SortDescriptions.Clear();
Switch(chosenSort){
"Sort my List"
}
CampaignSortableList.Refresh();
}
It works all fine now.
Related
Background:
I have a Datagrid with some Measurements and this Measurements we can Approve and Block.
Now we have for this a new Type, like "Cancelled". But this Type is only needed by Server and for displaying it to Customer.
But the Customer should not be able to select this "Cancelled" but the other 2 Types he should have to select.
The List get all different elements from Database (3 entries).
Firstly i tried to remove the Cancelled Item from the ApprovementCollection, but then it displayed a empty field instead of "Cancelled".
Question:
Is it possible, to disable only one of this 3 Items in the Bounded List of the Itemsource Property from the DataGridComboBoxColumn?
Disabled or Not Displayed in the Selection Menu is that what i have to do.
(Only "Freigabe" and "GESPERRT")
View:
<DataGridComboBoxColumn ClipboardContentBinding="{x:Null}"
DisplayMemberPath="ApprovementText"
Header="{x:Static trans:Translations.ClearenceHeader}"
ItemsSource="{Binding Source={StaticResource PossibleComponentMeasurementApprovements}}"
SelectedItemBinding="{Binding Approvement,
UpdateSourceTrigger=PropertyChanged}" />
Viewmodel:
private IEnumerable<ComponentMeasurementApprovement> possibleComponentMeasurementApprovements;
public IEnumerable<ComponentMeasurementApprovement> PossibleComponentMeasurementApprovements {
get { return possibleComponentMeasurementApprovements; }
set {
possibleComponentMeasurementApprovements = value;
OnPropertyChanged();
}
}
Thanks for your Help
This is possible writing a DataGridTemplateColumn for your cell instead of using the DataGridComboBoxColumn. Just add a property Enabled to your ComponentMeasurementApprovement class. This property indicates if the user is allowed to select the approvement.
Now create a new DataGridTemplateColumn containing a ComboBox as template. It is possible to bind IsEnabled of every ComboBox item to a proeprty by styling them via ItemContainerStyle.
Here is the code:
<DataGridTemplateColumn Header="CustomCell">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={x:Static local:ViewModel.PossibleComponentMeasurementApprovements}}"
DisplayMemberPath="ApprovementText"
SelectedItem="{Binding Approvement}">
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsEnabled" Value="{Binding Enabled}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Here is the result:
Since the second item is disabled, it's not possible to select it but an already selected item keeps selected.
I've put together what I thought was a context menu in an MVVM setting (I'm using WPF with XAML and C#, using MVVM). Only it's not working, which is why I'm here. I'm getting nothing in my context menu.
The XAML is supposed to call an ICommand in the code behind (or Relay Command since I'm using micro MVVM - same thing basically).
The first thing was to set up an object which the XAML could get the two needed values from - the Header and the Command. The item in question looks like this:
class ContextMenuVM : ObservableObject
{
public string Displayname { get; set; }
public RelayCommand ContextMenuCommand { get; set; }
}
So, rather simple there. These will be used for the bindings in the menu.
The view model here is called 'CharacterListViewModel' and contains an ObservableCollection if these ContextMenuVM objects. That looks like this:
private ObservableCollection<ContextMenuVM> _sceneAddMenu = new ObservableCollection<ContextMenuVM>();
public ObservableCollection<ContextMenuVM> SceneAddMenu
{
get { return _sceneAddMenu; }
set
{
if (_sceneAddMenu != value)
{
_sceneAddMenu = value;
RaisePropertyChanged("SceneAddMenu");
}
}
}
The ObservableCollection is populated, as follows:
foreach (Scene s in Database.Instance.Scenes)
{
SceneAddMenu.Add(new ContextMenuVM()
{
Displayname = s.SceneName, ContextMenuCommand = new RelayCommand(
() =>
{
MessageBox.Show("Clicked");
})
});
}
Just a test at the moment, but I can say through use of break points that SceneAddMenu contains four items after this code is run (as I would expect).
Well, that's kind of the background code. I suspect it works, although clearly something is broken. My suspicion is the XAML.
The context menu code itself is here:
<ContextMenu x:Key="CharacterMenu" ItemsSource="{Binding SceneAddMenu}">
<ContextMenu.ItemTemplate >
<DataTemplate DataType="MenuItem">
<MenuItem Header="Edit" Command="{Binding ContextMenuCommand}"></MenuItem>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
Ah, so the obvious problem would be that the data context is not properly set up. Well that's not the case because this context menu replaces another one which utilised a command in the view model (and that worked), so my assumption is that the view model is okay.
For the record, the previous context menu, which works, looked like this:
<ContextMenu x:Key="CharacterMenu">
<MenuItem Header="Edit" Command="{Binding EditCharacter}"/>
</ContextMenu>
And if I put that back in, it works. Since it has a binding to the view model, that would suggest that the data context is not the problem.
The Context menu itself is referenced a little later, like this:
<StackPanel Orientation="Horizontal" Margin="3" ContextMenu="{StaticResource CharacterMenu}">
But since that was also in before with the previous menu (i.e. when it worked), I only include it for completion's sake.
So the SceneAddMenu object (ObservableCollection) is populated. That seems to be fine. Somewhere between the XAML and the view model there must be a problem though. If I put a break point in the 'get' for SceneAddMenu and then right click on the item in question, the break point does not activate.
I am at a bit of a loss on this one. It's my first time creating a context menu using the MVVM method, so it's possible I missed out a step somewhere.
If you read all of this, thanks a lot. If I missed out any information, please let me know.
You shouldn't add a MenuItem to the ItemTemplate of a ContextMenu. You should define an ItemContainerStyle and bind to the Displayname and ContextMenuCommand properties of your class:
<ContextMenu x:Key="CharacterMenu" ItemsSource="{Binding SceneAddMenu}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Displayname}" />
<Setter Property="Command" Value="{Binding ContextMenuCommand} " />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
I'm following a strict MVVM pattern.
In my FlyoutControl, I've bound the following:
<controls:FlyoutsControl>
<controls:Flyout IsOpen="{Binding FlyoutIsOpen, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
CloseCommand="{Binding CloseFlyoutCommand}">
...
</controls:Flyout>
</controls:FlyoutsControl>
I have two scenarios, both which do not work:
Scenario 1:
I set FlyoutIsOpen to true in my ViewModel constructor, and I bind by CloseFlyoutCommand to a DelegateCommand which accepts a method that sets FlyoutIsOpen to false.
In this scenario, my view loads with the Flyout already open (as expected). However, when I click the Flyout close button, nothing happens unless I click it again. If I print the output of my method, I can confirm that the command sets FlyoutIsOpen to false, but for some reason, I require a second click (after FlyoutIsOpen is set to false) to actually close the Flyout.
Scenario 2:
I set FlyoutIsOpen to false (or uninitialized) in my constructor. I bind another button to a DelegateCommand which accepts a method that sets FlyoutIsOpen to true.
The view loads with the Flyout closed (as expected). However, when I click a button that I've bound to a method that sets FlyoutIsOpen to true, nothing happens and the Flyout does not appear.
Has anyone experienced similarly non-responsive issues with the FlyoutsControl? If so, how did you resolve it?
I also had problems binding the isOpen property, but I managed by styling the ItemContainer and binding IsOpen there instead of inside my Flyout. I don'T even need the CloseCommand anymore, setting Visible to false in my viewmodel closes the flyout.
The MainWindow.xaml where I define the ItemContainer Bindings:
<controls:MetroWindow.Flyouts>
<controls:FlyoutsControl ItemsSource="{Binding Flyouts}">
<controls:FlyoutsControl.Resources>
<view:FlyoutPositionConverter x:Key="FlyoutPositionConverter"/>
</controls:FlyoutsControl.Resources>
<controls:FlyoutsControl.ItemTemplate>
<DataTemplate DataType="{x:Type viewModel:SettingsViewModel}">
<view:SettingsFlyout/>
</DataTemplate>
</controls:FlyoutsControl.ItemTemplate>
<controls:FlyoutsControl.ItemContainerStyle>
<Style BasedOn="{StaticResource {x:Type controls:Flyout}}"
TargetType="{x:Type controls:Flyout}">
<Setter Property="Header"
Value="{Binding Header}" />
<Setter Property="IsOpen"
Value="{Binding Visible}" />
<Setter Property="Position"
Value="{Binding Position, Converter={StaticResource FlyoutPositionConverter}}" />
<Setter Property="IsModal"
Value="{Binding IsModal}" />
<Setter Property="Theme" Value="Accent" />
</Style>
</controls:FlyoutsControl.ItemContainerStyle>
</controls:FlyoutsControl>
</controls:MetroWindow.Flyouts>
The mainviewmodel has a ObservableCollection<IFlyoutViewModel> named Flyouts and contains all my flyout viewmodels. They of course have to implement IFlyoutViewModel:
using System.ComponentModel;
namespace MyApplication.ViewModel
{
internal interface IFlyoutViewModel : INotifyPropertyChanged
{
string Header { get; }
bool Visible { get; set; }
Position Position { get; set; }
bool IsModal { get; set; }
}
public enum Position
{
Top,
Left,
Right,
Bottom
}
}
The FlyoutPositionConverter is just a mapper between my position enum and the MahApps.Metro.Controls.Position because I didn't want to use the real positon in my viewmodel interface.
I realize my own Calendar.
I did a Generic.xaml (ResourceDictionnary) which contains my new control. I have a Calendar.class who implement :Control.
In my Calendar class I have a ObservableCollection<Day> _days. I put DataContext = this;
A Day contains my ObservableCollection<MyObject> ListeTache.
And Day.Class Implement INotifyPropertyChanged and have my event :
public event PropertyChangedEventHandler PropertyChanged;
But when I update my Listbox, I have to reload my calendar manually to see any changes.
Am I missing something ?
Thank you for the help.
My ObservableCollection<MyObject> :
public ObservableCollection<Tache> ListeTache
{
get { return this._listeTache; }
set
{
_listeTache = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("ListeTache"));
}
}
}
My Generic.xaml look like this :
<Grid x:Name="LayoutTache">
<ListBox x:Name="ListeTaches" ItemsSource="{Binding ListeTache,UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" FontSize="10" PreviewMouseDown="PreviewMouseDownClick_clear" MouseDoubleClick="doubleClic" IsSynchronizedWithCurrentItem="False">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding AffichageCalendrier}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Resources>
<ContextMenu x:Key="MonMenu">
<MenuItem Header="Supprimer" Click="MonMenuDel_Click" />
</ContextMenu>
</ListBox.Resources>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="AntiqueWhite"></Setter>
<Setter Property="ContextMenu" Value="{StaticResource MonMenu}"/>
</Trigger>
</Style.Triggers>
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="LightGreen" />
</Style.Resources>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
After somme reply :
How I can do that ? I have to add something like this in my Day.cs class :
_listeTache.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_listeTache_CollectionChanged);
void _listeTache_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
throw new NotImplementedException();
}
I never catch the event...
Thanks for all
When designing a custom control, it is customary not to set the DataContext to this... in fact, don't set it to anything as this enables it to be set from outside the control. Instead, you should reference your property from generic.xaml using a RelativeSource Binding:
<ListBox x:Name="ListeTaches" ItemsSource="{Binding ListeTache, RelativeSource={
RelativeSource AncestorType={x:Type YourXamlNamespacePrefix:Calendar}}}" ... />
It should also be noted that using UpdateSourceTrigger=PropertyChanged, Mode=TwoWay on an ItemsSource Binding is pointless as the ItemsControl cannot update the source collection.
If you still can't access the property, then you must either ensure that you correctly implement the INotifyPropertyChanged interface in Calendar.cs, or you can implement your ListeTaches property as a DependencyProperty instead.
UPDATE >>>
You've clearly done something wrong... it's really not that complicated. Follow the links that I provided to declare a DependencyProperty in your Calendar.cs class. Do not set the DataContext. Use the RelativeSource Binding that I showed you, correctly setting up the proper XAML Namespace... that's it!
Just one last thing... you did add a WPF Custom Control Library project into your application, didn't you? You need to have something like this in your Calendar class' static constructor:
static Calendar()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Calendar),
new FrameworkPropertyMetadata(typeof(Calendar)));
}
Perhaps it would help if you read through the Control Authoring Overview page on MSDN to ensure that you are doing it correctly?
UPDATE 2 >>>
Ok, so after noticing something in your comments, I think that I know what your problem is. #XAMlMAX asked you
have you tried removing that null check for your PropertyChanged in Listtache?
You replied
When I remove it, I catch TargetInvocationException.
I think that that's your problem... that means that your PropertyChanged event is null... that means that you have not attached a handler to it yet... it's not being used. Try attaching a handler to it and your ListeTache collection should display fine.
I have something like below. For MenuItem, here I am passing an object of that MenuItem as a CommandParameter. This works fine for me. My MenuItem holds a RadioButton and I want to use the MenuItem CommandParameter value for this RadioButton. Could anyone please help me how to do this. Thanks in Advance.
<MenuItem Header="Name"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Command="{Binding SortCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}">
<MenuItem.Icon>
<RadioButton VerticalAlignment="Center"
Margin="3"
IsChecked="True"
GroupName="sort"
Command="{Binding SortCommand}"
CommandParameter="..." />
</MenuItem.Icon>
</MenuItem>
Now Command is executing only when I select the MenuItem. I want to do the same when user selects the RadioButton also. Below is the code which I am using for this.
public void OnSortCommandExecuted(object menuItem)
{
MenuItem menu = menuItem as MenuItem;
if (menu != null)
{
((RadioButton)menu.Icon).IsChecked = !((RadioButton)menu.Icon).IsChecked;
this.eAggregator.GetEvent<ImagesSortedEvent>().Publish(menu.Header.ToString());
}
}
Like I said in the comments as well, it's not a good practise to pass on UI component as CommandParameter to ViewModel since ViewModel shouldn't know about View.
I would suggest you to have proper binding in ViewModel. Create a bool property in ViewModel and bind with IsChecked DP of radioButton. That ways you don't have to pass any CommandParameter from View, simply check the status of bool property from command execute method.
Now, that why MenuItem can't be accessed from RadioButton?
RadioButton doesn't lie in same Visual tree as that of MenuItem.
So, you can't use RelativeSource to travel upto MenuItem. Also ElementName binding won't work here since this to work both elements should lie in same Visual Tree.
You might find over net to use x:Reference in such cases where two elements doesn't lie in same Visual tree but that won't work here since it will create cyclic dependency.
Last thing, you have to resort with it to use Freezable class object to hold an instance of MenuItem and use that resource in your bindings.
First of all you need to define class deriving from Freezable:
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object),
typeof(BindingProxy));
}
and you can use it from XAML like this to pass MenuItem:
<MenuItem Header="Name"
x:Name="menuItem"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
Command="{Binding SortCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}}">
<MenuItem.Resources>
<local:BindingProxy x:Key="proxy"
Data="{Binding Source={x:Reference menuItem}}"/>
</MenuItem.Resources>
<MenuItem.Icon>
<RadioButton VerticalAlignment="Center"
Margin="3"
IsChecked="True"
GroupName="sort"
Command="{Binding SortCommand}"
CommandParameter="{Binding Data.CommandParameter,
Source={StaticResource proxy}}"/>
</MenuItem.Icon>
</MenuItem>
Ofcourse you need to declare local namespace in XAML.
PS - I would still insist to use first approach to define proper bindings in ViewModel.
UPDATE
If MenuItem is placed under ContextMenu, then RelativeSource binding won't be possible. Approach described above will work in that case.
But in case you are placing MenuItem directly as child of some control (like Menu), RelativeSource binding will work:
CommandParameter="{Binding CommandParameter,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=MenuItem}}"