I have a UserControl
set up in the MVC pattern with a DataGrid
and a List
of Models. My List
has more items than my DataGrid
can display, which is why I a looking for a way to build in a way to let my Users select the items they want to have visibily by themselves.
My DataGrid should be able to do all of the following:
As I am trying to build a custom charactersheet-generator for the Pen&Paper game DSA(the black eye), I'd be happy to make the DataGrid work as identical to their grid as possible.
Here you can see a grid to select weapon-skills. the first column contains a combobox with the names for all weapon-skills. On selecting a skill from this box the row changes to the stats of this skill
I previously only worked with DevExpress GridControls, so I struggle to get into 'default' DataGrids, even without implementing the functionality.
Additional Information:
How can I get this to work in the way I imagined it/ Do you have other/easier solutions for my problem?
Two points of interest:
Adding a property IsShown
to your row's viewmodel to track if the record is currently visible in the datagrid. We can enforce this using a RowStyle
.
Using a CollectionView
for the combobox that's based on the datagrid's ItemsSource
. If the record is not shown, it should be selectable from the combobox. Adding a filter to the collectionview should do the trick.
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>