绑定 MenuItem 的 ItemsSource 和 IsChecked 以获取 WPF 中可检查项目的列表

发布于 2024-09-07 03:06:03 字数 3947 浏览 6 评论 0原文

我正在尝试设置一个 MenuItem ,它将有一个可以选择的页码子菜单。我想将 ItemsSource 绑定到页码列表(实际上是通过创建列表的转换器绑定到 PageCount),然后绑定每个 MenuItem 的 IsChecked 属性 在子菜单中添加到 PageIndex。我的问题是第二个绑定,因为它也需要转换器,但该转换器需要知道 MenuItem 表示的页码,但我无法弄清楚如何将该信息传递给转换器。这是我到目前为止所尝试过的。

<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>

当然,问题是 ConverterParameter 无法绑定,因为它不是 DependencyProperty。所以我的问题是如何传递我需要的信息或者是否有其他方法可以做到这一点。

注意:我已经尝试将 MenuItem 放入 ListBox 中,就绑定而言效果非常好,但导致子菜单以非- 标准方式。也就是说,当您打开子菜单时,整个 ListBox 被视为一个 MenuItem

编辑

这就是我到目前为止所做的工作。我尝试绑定到隐藏的 ListBox 但当我将 MenuItem.ItemsSource 绑定到“ListBox.Items”时,我得到了 int 列表而不是我需要获取 IsSelected 属性的 ListBoxItem 列表。因此,我最终采用了 Quartermeister 的建议,即使用 MultiBinding 来获取 IsChecked 属性,以在 OneWay 模式下绑定到 PageIndex。为了处理另一个方向,我在 Click 事件上使用了事件处理程序。值得注意的是,起初我将 IsCheckable 设置为 true 并使用 Checked 事件,但这导致了一些奇怪的行为。

<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>

这是 GotoPageMenuItem_Click 代码

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);
        }
    }
}

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 MenuItems 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 ints instead of a list of ListBoxItems 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);
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

汹涌人海 2024-09-14 03:06:03

听起来您正在尝试构建控制每个菜单项的选中状态的动态菜单。
我使用 MVVM 模式扩展了一些为在 WPF 中构建动态菜单而编写的代码,并添加了检查的逻辑。

这是 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>

这是视图模型:

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;
  }
}

这是保存命令的类:

  public class MyCommand
  {
     public ICommand Command { get; set; }
     public string Text { get; set; }
     public string Parameter { get; set; }
     public Boolean Checked { get; set; }
  }

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; }
  }
够钟 2024-09-14 03:06:03

您可以使用 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>

让您的 pageNumChecked 转换器实现 IMul​​tiValueConverter 而不是 IValueConverter,Convert 将获得一个包含每个子绑定结果的数组。在本例中,它将是一个双元素数组,其中第一个元素是当前输入 PageIndex,第二个索引是当前 ConverterParameter Content。如果您想要双向绑定,ConvertBack 将需要返回一个双元素数组,并且您将为第二个参数返回 Binding.DoNothing,以便它不会尝试更新 Content。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文