RibbonMenuButton Binding item clicked to command - c#

I have a problem with the RibbonMenuButton. Currently I have :
<RibbonMenuButton Label="Meeting" Width="Auto" ToolTipDescription="Display requests on the agenda for the meeting selected" ToolTipTitle="Meeting"
LargeImageSource="pack://application:,,,/Resources/meeting.png"
ItemsSource="{Binding MeetingsAvailable}">
<RibbonMenuButton.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</RibbonMenuButton.ItemTemplate>
</RibbonMenuButton>
My MeetingsAvailable is actually a Dictionary<int, string>. This code is working, the RibbonMenuButtonis well displaying each Value of the dictionnary.
Now I'm trying to get back the Key of the MenuItem which has been clicked. My idea was to use a ICommand in my ViewModel and to bind an event to this command. But I don't really know how to get the event corresponding to clicking an Item in the RibbonMenuButton
Do someone have already did that ?
Thank you in advance.

You can data bind an ICommand to a RibbonMenuButton using the ItemContainerStyle property, like this:
<RibbonMenuButton Label="Meeting" ItemsSource="{Binding MeetingsAvailable}" ... >
<RibbonMenuButton.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"/>
</DataTemplate>
</RibbonMenuButton.ItemTemplate>
<RibbonMenuButton.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="Command" Value="{Binding DataContext.NameOfCommand,
RelativeSource={RelativeSource AncestorType={x:Type Views:View}}}" />
<Setter Property="CommandParameter" Value="{Binding Key}" />
</Style>
</RibbonMenuButton.ItemContainerStyle>
</RibbonMenuButton>

You have to create a command in your VM to which you can bind. Then you have to do a binding of the key of your dictionary to the command parameter so you can use it inside your commandfunction. Maybe you have to create an additional Button inside your DataTemplate.

Related

Multi-stage binding from ViewModel to Custom-Control to controls on ControlTemplate not working

I have a MVVM project with a View and a ViewModel in its DataContext.
In this project I have a class ComboBoxCustom which inherits from ComboBox. I define some additional functionality in my ComboBoxCustom class.
To this ComboBoxCustom class I assign a control template to define its appearance.
The (simplified) style defining the (simplified) control template looks like:
<Style TargetType="{x:Type lib:ComboBoxCustom}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type lib:ComboBoxCustom}">
<StackPanel>
<TextBlock Text="{TemplateBinding TextPropertyInComboBoxCustom}"/>
<ComboBox DataContext="{TemplateBinding DataContext}"
ItemsSource="{TemplateBinding ItemsSource}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
SelectedValuePath="{TemplateBinding SelectedValuePath}"
SelectedValue="{TemplateBinding SelectedValue}"
SelectedItem="{TemplateBinding SelectedItem}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Which resides in a ResourceDictionary. The real control template has some additional features which are left out since they are not relevant for the question.
I use this ComboBoxCustom control in my View using:
<lib:ComboBoxCustom ItemsSource="{Binding MyObservableCollectionOfMyObjects}"
TextPropertyInComboBoxCustom="MyText"
DisplayMemberPath="MyDescription"
SelectedValuePath="MyValue"
SelectedItem="{Binding SelectedMyObject, Mode=TwoWay}"/>
The view is ok, and all items get loaded in the ComboBox which I can select.
The problem is that when I select a different item in the ComboBox, the property SelectedMyObject in my ViewModel does not get updated and consequently its setter is not called. Therefore, the (correct) information about the selected object is not available in my ViewModel.
When I use <ComboBox .../> (without the TextPropertyInComboBoxCustom property) instead of <lib:ComboBoxCustom .../> everything works just fine but then I don't have the additional functionality defined in ComboBoxMessage which I need.
Can anyone tell me what is wrong and how to fix this issue so I can use ComboBoxMessage in my view? Preferably without breaking the MVVM pattern.
Thank you!
Thanx to ASh's comment and information in this post.
The problem is that the TemplateBinding is one way. Therefore, all information from the ViewModel can get into the controls in the template. But not the other way around.
The solution is to specify a normal binding as:
SelectedItem ="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"
Whichs does about the same as a TemplateBinding but is two way.
The control template has become:
<Style TargetType="{x:Type lib:ComboBoxCustom}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type lib:ComboBoxCustom}">
<StackPanel>
<TextBlock Text="{TemplateBinding TextPropertyInComboBoxCustom}"/>
<ComboBox DataContext="{TemplateBinding DataContext}"
ItemsSource="{TemplateBinding ItemsSource}"
DisplayMemberPath="{TemplateBinding DisplayMemberPath}"
SelectedValuePath="{TemplateBinding SelectedValuePath}"
SelectedValue="{TemplateBinding SelectedValue}"
SelectedItem ="{Binding SelectedItem, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I am not sure about the SelectedValue property though. With the template like this, it works both when I use the SelectedValue property or the SelectedItem property.
The Mode=TwoWay option can be omitted in the view since the default binding mode for SelectedItem is already two way. The view line becomes:
<lib:ComboBoxCustom ItemsSource="{Binding MyObservableCollectionOfMyObjects}"
TextPropertyInComboBoxCustom="MyText"
DisplayMemberPath="MyDescription"
SelectedValuePath="MyValue"
SelectedItem="{Binding SelectedMyObject}"/>
Bind SelectedValue to the property in ViewModel
SelectedValue="{Binding SelectedMyObject, Mode=TwoWay}"
in your lib:ComboBoxCustom

Assign command to a set of buttons in grid through style

Is it possible in silverlight to assign a same command to a set of buttons in a grid through style? or assign it at a common place instead of repeating?
I mean something like using this -
<Style TargetType="Button">
<Setter Property="Command" Value="{Binding Command1}"/>
</Style>
instead of -
<Button Command="{Binding Command1}"/>
<Button Command="{Binding Command1}"/>
<Button Command="{Binding Command1}"/>
...
<Button Command="{Binding Command1}"/>
It is possible. In a simple way you can add a Style to the Resources and use it as a StaticResource. Assuming that Command1 belongs to the ViewModel you are binding to the Window. If not, provide proper path to the Command1 to bind it properly.
<Window.Resources>
<Style x:Key="buttonCommandStyle" TargetType="Button">
<Setter Property="Command" Value="{Binding Path=DataContext.Command1}" />
</Style>
</Window.Resources>
Then use it as the Style for your Button
<Button Style="{StaticResource buttonCommandStyle}" />
What you are looking for is the ItemsControl.
<ItemsControl x:Name="MyItemsControl" ItemsSource="{Binding MyButtonItems}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Command1}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The Binding will be evaluated against each item, therefore you have several ways of providing the command instance:
you could place the same command instance as a public property in each itemInstance
you could set a source or relativeSource for the Binding to have it evaluated against the DataContext of the ItemsControl instead of against each item: {Binding Path=DataContext.Command1, ElementName=MyItemsControl}

Two way binding of IsChecked within a nested MenuItem

I have a nested context menu like this:
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}">
<MenuItem Header="Thing Count" ItemsSource="{Binding ThingsProvider}">
NB the Thing Count parent MenuItem with children from ThingsProvider.
ThingsProvider provides a List of ThingViewModel which contains properties Thing and IsChecked. I want to be able to control IsChecked from my main view model and from the user via the MenuItem but I have hit a problem; If I use an ItemContainerStyle like this.
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Path=Thing, Mode=OneWay}"/>
<Setter Property="IsChecked" Value="{Binding Path=IsChecked, UpdateSourceTrigger=PropertyChanged}"/>
</Style>
</MenuItem.ItemContainerStyle>
then (predictably I suppose as it's a style) it will observe the ViewModel's IsChecked but will not set it.
If I use an ItemTemplate like so
<MenuItem.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Thing, Mode=OneWay}"/>
</DataTemplate>
</MenuItem.ItemTemplate>
I can't get to the MenuItem's IsChecked property as I've only got the TextBlock. If I put a MenuItem in the ItemTemplate then I end up with nested MenuItems in my UI and it looks bad.
There must be a way to do this but I'm stumped at the moment, can anyone help?
Cheers
Rich

WPF Set focus on text box when item added to Listbox

I have a listbox with an Add button mapped to a command backed by a SelectedItem property in the VM.
When an item is added to the listbox i have the SelectedItem set to the new item so it has focus in the listbox. I'd like to have a textbox (data entry for that new item) to have focus.
I've been looking at event triggers but i havent seen a way to cross items but basically I think what i want is an event trigger for the listbox selection change to to set the focus on a text box.
how would i go about doing this?
As an example I have the following XAML code. This will add a Person (name and age property only)
Basically I want the txtName textbox to have focus when an item is selected in the listbox.
<StackPanel>
<TextBlock>Name</TextBlock>
<TextBox Text="{Binding NewPerson}"></TextBox>
<Button Command="{Binding AddPersonDelegateCommand}">Add</Button>
<Button>Remove</Button>
<ListBox ItemsSource="{Binding People}" DisplayMemberPath="Name"
SelectedItem="{Binding SelectedPerson}">
</ListBox>
<TextBox Name="txtName" Text="{Binding SelectedPerson.Name, Mode=TwoWay}"</TextBox>
<TextBox Name="txtAge" Text="{Binding SelectedPerson.Age, Mode=TwoWay}"></TextBox>
</StackPanel>
here is the xaml based trigger to set focus on the txtName TextBox when SelectedItem property is toggled from null
so idea here is that you set SelectedPerson property to null followed by the instance of newly created person object, that should do the trick and will set the focus to the desired TextBox
the limitation of this trigger is that you need to set SelectedPerson property null before you set to the new object, an attached behavior can solve that issue too, if this is not workable for you.
<StackPanel>
<TextBlock>Name</TextBlock>
<TextBox Text="{Binding NewPerson}"></TextBox>
<Button Command="{Binding AddPersonDelegateCommand}">Add</Button>
<Button>Remove</Button>
<ListBox DisplayMemberPath="Name"
x:Name="list"
SelectedItem="{Binding SelectedPerson}">
</ListBox>
<TextBox Name="txtName"
Text="{Binding SelectedPerson.Name, Mode=TwoWay}">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="FocusManager.FocusedElement"
Value="{Binding RelativeSource={RelativeSource Self}}" />
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItem,ElementName=list}"
Value="{x:Null}">
<Setter Property="FocusManager.FocusedElement"
Value="{x:Null}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<TextBox Name="txtAge"
Text="{Binding SelectedPerson.Age, Mode=TwoWay}"></TextBox>
</StackPanel>

Updating Values in the Control Template of the ComboBox

I implemented a column in a data grid that containes comboboxes. In order to display a text box in stead of a combobox when a list containes only one value, I used the solution from this post:
How to hide combobox toggle button if there is only one item?
However, when that one value in the list is changed, it is not updated in the text box. I have, of course, implemented INotifyPropertyChanged and it works as long as I have more than one item in the list (in other words, when the combobox is shown) but the value in the TextBlock is never updated.
Edit:
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Name="CList" ItemsSource="{Binding Values, UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding Path=SelectedValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedIndex="0" BorderBrush="Transparent"
Background="Transparent">
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}" >
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Items.Count, ElementName=CList}" Value="1">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBlock Text="{Binding Items[0], ElementName=CList}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
I can see you are binding to the item itself, instead of any property in it
so perhaps you many need to bind to the respective property of your data item
eg
<TextBlock Text="{Binding Items[0].MyProperty, ElementName=CList}" />
assuming your intended property is MyProperty
Note if there is no underlying property then you'll have to remove the item and add new one again to list in order to update the text block, in this scenario INotifyPropertyChanged will also not work

Categories