I'm attempting to setup a MenuItem
that will have a submenu of page numbers that can be selected. I want to bind the ItemsSource
to a list of page numbers (actually to the PageCount with a converter creating the list) and then bind the IsChecked
property of each MenuItem
in the sub-menu to the PageIndex. My problem is with the second binding as it too requires a converter, but that converter need to know the page number that the MenuItem
represents, but I cannot figure out how to pass that information to the converter. Here's what I've tried so far.
<MenuItem Header="_Goto Page"
ItemsSource="{Binding
Path=CurrentImage.PageCount,
Converter={StaticResource countToList}}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="IsCheckable"
Value="True"/>
<Setter Property="IsChecked"
Value="{Binding
ElementName=MainWindow,
Path=CurrentImage.PageIndex,
Mode=TwoWay,
Converter={StaticResource pageNumChecked},
ConverterParameter={Binding
RelativeSource={RelativeSource Self},
Path=Content}}"/>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
Of course the problem is that the ConverterParameter
cannot be bound as it is not a DependencyProperty
. So my question is how can I pass in the information I need or is there another way to do this.
Note: I already tried putting the MenuItem
s inside of a ListBox
which worked really well as far as the bindings are concerned, but caused the sub-menu to behave in a non-standard way. That is when you opened the sub-menu the entire ListBox
was treated as one MenuItem
.
Edit
So here's what I've gotten to work so far. I tried binding to a hidden ListBox
but when I bound the MenuItem.ItemsSource
to the 'ListBox.Items' I got the list of int
s instead of a list of ListBoxItem
s which I needed to get the IsSelected
property. So I ended up using Quartermeister's suggestion of using MultiBinding to get the IsChecked
property to bind to the PageIndex
in OneWay
mode. To handle the other direction I used an event handler on the Click
event. It's worth noting that at first I had IsCheckable
set to true
and was working with the Checked
event, but that resulted is some odd behaviors.
<MenuItem x:Name="GotoPageMenuItem" Header="_Goto Page"
ItemsSource="{Binding Path=CurrentImage.PageCount,
Converter={StaticResource countToList}}">
<MenuItem.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="IsCheckable"
Value="False"/>
<EventSetter Event="Click"
Handler="GotoPageMenuItem_Click"/>
<Setter Property="IsChecked">
<Setter.Value>
<MultiBinding Converter="{StaticResource pageNumChecked}"
Mode="OneWay">
<Binding RelativeSource="{RelativeSource FindAncestor,
AncestorType={x:Type Window}}"
Path="CurrentImage.PageIndex"
Mode="OneWay"/>
<Binding RelativeSource="{RelativeSource Self}"
Path="Header"
Mode="OneWay"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
And here's the GotoPageMenuItem_Click
code
private void GotoPageMenuItem_Click(object sender, RoutedEventArgs e)
{
var item = sender as MenuItem;
if (item != null)
{
//If the item is already checked then we don't need to do anything
if (!item.IsChecked)
{
var pageNum = (int)item.Header;
CurrentImage.PageIndex = (pageNum - 1);
}
}
}
Sounds like you are trying to build dynamic menus that control the checked state of each menu item.
I extended some code I wrote to build dynamic menus in WPF with the MVVM pattern and added the checked logic.
Here is the XAML:
<Menu DockPanel.Dock="Top">
<MenuItem ItemsSource="{Binding Commands}"
Header="_Item Container Style">
<MenuItem.ItemContainerStyle>
<Style TargetType="{x:Type MenuItem}">
<Setter Property="IsCheckable" Value="True"/>
<Setter Property="IsChecked" Value="{Binding Path=Checked}"/>
<Setter Property="Header" Value="{Binding Path=Text}" />
<Setter Property="Command" Value="{Binding Path=Command}" />
<Setter Property="CommandParameter" Value="{Binding Path=Parameter}" />
</Style>
</MenuItem.ItemContainerStyle>
</MenuItem>
</Menu>
Here is the View Model:
public class MainViewModel : ViewModelBase
{
public MainViewModel()
{
GoCommand = new DelegateCommand<object>(OnGoCommand, CanGoCommand);
LoadCommands();
}
private List<MyCommand> _commands = new List<MyCommand>();
public List<MyCommand> Commands
{
get { return _commands; }
}
private void LoadCommands()
{
MyCommand c1 = new MyCommand { Command = GoCommand, Parameter = "1", Text = "Menu1", Checked = true};
MyCommand c2 = new MyCommand { Command = GoCommand, Parameter = "2", Text = "Menu2", Checked = true };
MyCommand c3 = new MyCommand { Command = GoCommand, Parameter = "3", Text = "Menu3", Checked = false };
MyCommand c4 = new MyCommand { Command = GoCommand, Parameter = "4", Text = "Menu4", Checked = true };
MyCommand c5 = new MyCommand { Command = GoCommand, Parameter = "5", Text = "Menu5", Checked = false };
_commands.Add(c1);
_commands.Add(c2);
_commands.Add(c3);
_commands.Add(c4);
_commands.Add(c5);
}
public ICommand GoCommand { get; private set; }
private void OnGoCommand(object obj)
{
}
private bool CanGoCommand(object obj)
{
return true;
}
}
Here is the class that holds the commands:
public class MyCommand
{
public ICommand Command { get; set; }
public string Text { get; set; }
public string Parameter { get; set; }
public Boolean Checked { get; set; }
}
Can you do what you want using a MultiBinding?
<Setter Property="IsChecked">
<Setter.Value>
<MultiBinding Converter="{StaticResource pageNumChecked}">
<Binding ElementName="MainWindow" Path="CurrentImage.PageIndex" Mode="TwoWay"/>
<Binding RelativeSource="{RelativeSource Self}" Path="Content"/>
</MultiBinding>
</Setter.Value>
</Setter>
Have your pageNumChecked
converter implement IMultiValueConverter instead of IValueConverter and Convert will get an array with the result of each child binding. In this case, it would be a two-element array where the first element is your current input, PageIndex, and the second index is your current ConverterParameter, Content. If you want two-way binding, ConvertBack will need to return a two-element array, and you would return Binding.DoNothing
for the second parameter so that it does not try to update Content.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With