简体   繁体   English

如何在 wpf 的上下文菜单中创建绑定项目的选择?

[英]How to create a selection of bound items in a context menu in wpf?

I'm currently working on a frontend for an emulator in WPF, and am having trouble with this seemingly basic function.我目前正在为 WPF 中的仿真器开发前端,并且在使用这个看似基本的 function 时遇到了麻烦。

I am trying to create a simple selection of items from within a context menu (in this case of an image).我正在尝试从上下文菜单中创建一个简单的项目选择(在这种情况下是图像)。 A picture explains it a little better:一张图解释得更好一点:

图片

I want only one at a time to be selectable/checkable, this data then sets a value in my model which is eventually written to an external file.我一次只希望一个可选择/可检查,然后此数据在我的 model 中设置一个值,该值最终写入外部文件。 In a prototype I had that bound to a combobox with two-way binding but that doesn't really make sense with this design.在原型中,我将它绑定到具有双向绑定的 combobox ,但这对这种设计没有任何意义。

I tried a couple of different approaches, but did not manage to figure out a proper solution (specifically how to control the isChecked of the non clicked items).我尝试了几种不同的方法,但没有找到合适的解决方案(特别是如何控制未点击项目的 isChecked )。

My context menu (xaml) looks like this:我的上下文菜单 (xaml) 如下所示:

<ContextMenu>
    <MenuItem Header="PCSX2 Version" ItemsSource="{Binding Versions}">
        <MenuItem.ItemContainerStyle>
            <Style TargetType="MenuItem">
                <Setter Property="StaysOpenOnClick" Value="True" />
                <Setter Property="BindingGroup" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MenuItem}}}" />
                <EventSetter Event="Click" Handler="SetVersion" />
            </Style>
        </MenuItem.ItemContainerStyle>
    </MenuItem>
    <MenuItem Header="Config" ItemsSource="{Binding Configs}" />
    <MenuItem Header="Create Config" BindingGroup="{Binding}" Click="ShowConfigWizard"/>
</ContextMenu>

And my model is shown below, not much works in the code behind ( SetVersion function) yet.我的 model 如下所示,在后面的代码( SetVersion函数)中还没有多少工作。 As you can see below, I tried a solution involving binding a Tuple but couldn't get that to work (the ischecked value never updated, despite the bound condition being no longer true).正如您在下面看到的,我尝试了一个涉及绑定元组的解决方案,但无法使其工作( ischecked值从未更新,尽管绑定条件不再为真)。

I also tried a radio button based solution (again couldn't get it to work), and just setting checked to false for all items from code behind (was unable to select the sibling menu items from the binding).我还尝试了一个基于单选按钮的解决方案(再次无法使其工作),并且只是将后面代码中的所有项目的检查设置为 false (无法 select 绑定中的同级菜单项)。

public class GameModel : ICloneable
{
    public string Game { get; set; }
    public string Path { get; set; }
    public IEnumerable<string> Versions { get; set; }
    public IEnumerable<Tuple<string, bool>> VersionsAndStates => Versions.Select(version => new Tuple<string, bool>(version, Version == version));
    public string Version { get; set; }
    public IEnumerable<string> Configs { get; set; }
    public string Config { get; set; }
    public string CoverPath { get; set; }

    object ICloneable.Clone() => Clone();
    public GameModel Clone() => (GameModel) MemberwiseClone();
}

As is typical I just posted asking about a solution then I figured it out I was so close, just a slight oversight on my part about how the ViewModel works.就像典型的那样,我刚刚发布了一个解决方案,然后我发现我已经很接近了,只是我对 ViewModel 的工作方式有一点疏忽。 I'll put my solution below in case anyone else has a similar problem.我会把我的解决方案放在下面,以防其他人有类似的问题。 The main change was in my model.主要变化在我的 model 中。

public class GameModel : ICloneable, INotifyPropertyChanged
{
    public string Game { get; set; }
    public string Path { get; set; }
    public IEnumerable<string> Versions { get; set; }
    public IEnumerable<Tuple<string, bool>> VersionsAndStates => Versions.Select(version => new Tuple<string, bool>(version, Version == version));
    private string version;
    public string Version 
    {
        get => version;
        set
        {
            version = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Version)));
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(VersionsAndStates)));
        }
    }
    public IEnumerable<string> Configs { get; set; }
    public string Config { get; set; }
    public string CoverPath { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;

    object ICloneable.Clone() => Clone();
    public GameModel Clone() => (GameModel) MemberwiseClone();
}

and my xaml和我的 xaml

    <ContextMenu>
        <MenuItem Header="PCSX2 Version" ItemsSource="{Binding VersionsAndStates}">
            <MenuItem.ItemContainerStyle>
                <Style TargetType="MenuItem">
                    <Setter Property="Header" Value="{Binding Item1}" />
                    <Setter Property="IsChecked" Value="{Binding Item2, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" />
                    <Setter Property="StaysOpenOnClick" Value="True" />
                    <Setter Property="BindingGroup" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type MenuItem}}}" />
                    <EventSetter Event="Click" Handler="SetVersion" />
                </Style>
            </MenuItem.ItemContainerStyle>
        </MenuItem>
        <MenuItem Header="Config" ItemsSource="{Binding Configs}" />
        <MenuItem Header="Create Config" BindingGroup="{Binding}" Click="ShowConfigWizard"/>
    </ContextMenu>

And then setting Version on the model to the selected header in my code behind然后在我后面的代码中将 model 上的版本设置为选定的 header

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM