使用组合框列交换数据网格中的项目?

时间:2019-04-13 15:36:15

标签: c# wpf datagrid

我有什么

我以MVC模式设置了UserControl,其中包含DataGridList的模型。我的List的项目比我的DataGrid所显示的要多,这就是为什么我一直在寻找一种构建方式,让用户自己选择自己想要的项目。

我需要什么

我的DataGrid应该能够执行以下所有操作:

  • 显示8个列表中的30个项
  • 轻松允许用户更改每一行的项目
  • 当用户更改DataGrid中的单元格值时更新主列表中的模型(以防我仅使用8个项目的辅助列表)

理想情况下应该是什么样子

当我尝试为Pen&Paper游戏DSA(黑眼)构建自定义字符表生成器时,我很乐意使DataGrid尽可能与它们的网格相同。

在这里您可以看到一个网格来选择武器技能。 第一列包含一个组合框,其中包含所有武器技能的名称。 从此框中选择一项技能后,该行将变为该技能的统计信息

DSAPIC

我以前只使用DevExpress GridControls,所以即使没有实现功能,我也很难进入“默认” DataGrid。

其他信息:

  • 我从/加载和保存我的字符表
  • 应该打印字符表

问题

我该如何按照我的想象来工作/您是否有其他/更轻松的解决方案?

1 个答案:

答案 0 :(得分:0)

两个景点:

  1. 如果要使更新在所有集合中可见,最简单的方法是确保所有集合都使用相同的引用来填充自身。
  2. 在相关说明中,多次显示同一条记录需要特别注意。更新可能看起来很奇怪,因为它们涉及几行。 LINQ查询可能会返回另一个预期的实例。但是,在这种情况下,我们只对显示唯一记录感兴趣,并且可以选择替换它们。

方法

向行的视图模型添加属性IsShown,以跟踪记录当前在数据网格中是否可见。我们可以使用RowStyle来强制执行此操作。

对基于数据网格CollectionView的组合框使用ItemsSource。如果未显示该记录,则应该可以从组合框中选择该记录。将过滤器添加到collectionview应该可以解决问题。

代码示例

WeaponViewModel.cs

public class WeaponViewModel : ViewModelBase
{
    private readonly string _name;
    private string abbreviation;
    private int damage;
    private bool isShown;

    public WeaponViewModel(string name, string abbreviation, int damage, bool isShown)
    {
        _name = name;
        this.Abbreviation = abbreviation;
        this.Damage = damage;
        this.IsShown = isShown;
    }

    public string Name => _name;

    public string Abbreviation
    {
        get { return abbreviation; }
        set
        {
            abbreviation = value;
            OnPropertyChanged();
        }
    }

    public int Damage
    {
        get { return damage; }
        set
        {
            damage = value;
            OnPropertyChanged();
        }
    }

    public bool IsShown
    {
        get { return isShown; }
        set
        {
            isShown = value;
            OnPropertyChanged();
        }
    }
}

MainViewModel.cs

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        Weapons = new ObservableCollection<WeaponViewModel>(GetWeapons());
        DropDownWeapons = (CollectionView)new CollectionViewSource { Source = Weapons }.View;
        DropDownWeapons.Filter = DropDownFilter;
    }

    #region DataGrid

    public ObservableCollection<WeaponViewModel> Weapons { get; }

    private WeaponViewModel currentWeapon;
    public WeaponViewModel CurrentWeapon
    {
        get { return currentWeapon; }
        set
        {
            currentWeapon = value;
            OnPropertyChanged();
        }
    }

    #endregion DataGrid

    #region ComboBox

    public CollectionView DropDownWeapons { get; } 

    private WeaponViewModel selectedWeapon;
    public WeaponViewModel SelectedWeapon
    {
        get { return selectedWeapon; }
        set
        {
            if (value != null)
            {
                selectedWeapon = value;
                ReplaceCurrentWith(selectedWeapon);
                OnPropertyChanged();
            }
        }
    }

    private void ReplaceCurrentWith(WeaponViewModel requestedWeapon)
    {
        currentWeapon.IsShown = false;
        requestedWeapon.IsShown = true;
        var currentWeaponIndex = Weapons.IndexOf(currentWeapon);
        var requestedWeaponIndex = Weapons.IndexOf(requestedWeapon);
        Weapons.Move(requestedWeaponIndex, currentWeaponIndex);
        DropDownWeapons.Refresh();
    }

    private bool DropDownFilter(object item)
    {
        var weapon = (WeaponViewModel)item;
        return weapon.IsShown == false;
    }

    #endregion ComboBox

    private static IList<WeaponViewModel> GetWeapons()
    {
        var weapons = new List<WeaponViewModel>
        {
            new WeaponViewModel("Assault Rifle", "AR", 30, true),
            new WeaponViewModel("Submachine Gun", "SM", 17, true),
            new WeaponViewModel("Revolver", "RV", 54, true),
            new WeaponViewModel("Shotgun", "AR", 30, true),
            new WeaponViewModel("Sniper", "SN", 63, true),
            new WeaponViewModel("Rocket Launcher", "RL", 300, true),
            new WeaponViewModel("Grenade Launcher", "GL", 200, true),
            new WeaponViewModel("Minigun", "MG", 20, true),
            new WeaponViewModel("Knife", "KN", 10, false),
            new WeaponViewModel("Baseball Bat", "BB", 6, false),
        };
        return weapons;
    }
}

MainWindow.xaml

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:vm="clr-namespace:WpfApp.ViewModels"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" WindowStartupLocation="CenterScreen">
    <Window.DataContext>
        <vm:MainViewModel/>
    </Window.DataContext>
    <DataGrid ItemsSource="{Binding Weapons}" SelectedItem="{Binding CurrentWeapon}"
              AutoGenerateColumns="False">
        <DataGrid.RowStyle>
            <Style TargetType="{x:Type DataGridRow}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsShown}" Value="False">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </DataGrid.RowStyle>
        <DataGrid.Columns>
            <DataGridTemplateColumn Header="Weapons">
                <DataGridTemplateColumn.CellEditingTemplate>
                    <DataTemplate>
                        <ComboBox DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                            ItemsSource="{Binding DropDownWeapons}"
                            SelectedItem="{Binding SelectedWeapon}"
                            DisplayMemberPath="Name"
                            IsEditable="False" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellEditingTemplate>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Name}" />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
            <DataGridTextColumn Header="Name" Binding="{Binding Name}" IsReadOnly="True" />
            <DataGridTextColumn Header="AB" Binding="{Binding Abbreviation}" />
            <DataGridTextColumn Header="Damage"  Binding="{Binding Damage}" />
        </DataGrid.Columns>
    </DataGrid>
</Window>