Home > Mobile >  Bind two different classes into one Datagrid WPF
Bind two different classes into one Datagrid WPF

Time:11-29

I currently have 2 lists with different classes in them: List<Player> and List<Monster>.

I want to get these two lists in a single Datagrid as follows:enter image description here

The reason I need them to be in one Datagrid, is that I need to sort on Initiative, and show the order from high to low. The Datagrid also need to work with any number of players/monsters. The classes look as follows:

public partial class Player
    {
        public bool IsInParty { get; set; }
        public string Name { get; set; }
        public int Ac { get; set; }
        public string ArmorType { get; set; }
        public string[] Speed { get; set; }
        public int InitiativeBonus { get; set; }
        public string[] DmgVul { get; set; }
        public string[] DmgRes { get; set; }
        public string[] DmgImm { get; set; }
        public string[] CondImm { get; set; }
        public string[] Senses { get; set; }
        public string[] Languages { get; set; }
        public NameValuePair[] Conditions { get; set; }
        public int Id { get; set; }


    }
public partial class Monster
    {
        public BaseMonster Stats { get; set; }
        public int Id { get; set; }
        public string Name { get; set; }
        public int Hp { get; set; }
        public List<int> Damage { get; set; }
        public bool IsOverhealed => Hp > Stats.MaxHp;
        public bool IsBloody => Hp <= Stats.MaxHp / 2.0;
        public bool IsNearDeath => Hp <= Stats.MaxHp / 4.0;
        public bool IsDead => Hp <= 0;
        public List<NameValuePair> Conditions { get; set; }
    }
public partial class BaseMonster
    {
        public int DefaultId { get; set; }
        public string DefaultName { get; set; }
        public string Type { get; set; }
        public string Allignment { get; set; }
        public int Ac { get; set; }
        public string ArmorType { get; set; }
        public int MaxHp { get; set; }
        public string HitDice { get; set; }
        public string[] Speed { get; set; }
        public int Str { get; set; }
        public int Dex { get; set; }
        public int Con { get; set; }
        public int Int { get; set; }
        public int Wis { get; set; }
        public int Cha { get; set; }
        public string[] SavThrProf { get; set; }
        public string[] SkillProf { get; set; }
        public string[] DmgVul { get; set; }
        public string[] DmgRes { get; set; }
        public string[] DmgImm { get; set; }
        public string[] CondImm { get; set; }
        public string[] Senses { get; set; }
        public string[] Languages { get; set; }
        public string Challenge { get; set; }
        public NameValuePair[] Traits { get; set; }
        public NameValuePair[] Actions { get; set; }
        public NameValuePair[] LegendaryActions { get; set; }
        public string LairActions { get; set; }
        public string RegionalEffects { get; set; }
    }

Because I am relatively new to front-end and xaml, I have a little trouble how to take on this problem. Currently I've managed to get the following with some test data:

enter image description here

<Grid Grid.Row="2" Grid.Column="1">
            <DataGrid x:Name="creatureDatagrid">
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
                    <DataGridTextColumn Header="ID" Binding="{Binding Id}"/>
                    <DataGridTextColumn Header="AC" Binding="{Binding Ac}"/>
                    <DataGridTextColumn Header="HP" Binding="{Binding Hp}"/>

                </DataGrid.Columns>
            </DataGrid>
<Grid/>

As you can see, some values work fine, but in this case AC is not working, because AC in found in BaseMonster in the Monster class

CodePudding user response:

You can bind the DataGrid's ItemsSource to a list with some common interface. So instead of binding to List<Player> and List<Monster>:

List<ITableEntry>

Or you can try to use an abstract class instead.

CodePudding user response:

you can use PriorityBinding:

<DataGridTextColumn Header="AC">
    <DataGridTextColumn.Binding>
        <PriorityBinding>
            <Binding Path="Ac" />
            <Binding Path="Stats.Ac" />
        </PriorityBinding>
    </DataGridTextColumn.Binding>
</DataGridTextColumn>

CodePudding user response:

DataGrid columns are generated automatically based on the type of ItemsSource items. See this blogpost to get some more insights about this.

Since a DataGrid can only be bound to a single type / collection you first need to create a common interface which contains all common properties like

public interface ICreature
{
    int Id { get; set; }
    //Other common properties
}

If the items are not changed at runtime (otherwise use ObservableCollections), you can simply bind the DataGrid to the concatenated collections

public IEnumerable<ICreature> Creatures { get; } = Players.Concat(Monsters);

This will autogenerate all interface defined properties in your DataGrid. All additional properties, you need to define in XAML since they can't be autogenerated:

<DataGrid ItemsSource="{Binding Creatures}">
    <DataGrid.Columns>
        <DataGridTextColumn Header="CustomProperty" Binding="{Binding Name}"/>
        <!-- More custom properties -->
    </DataGrid.Columns>
</DataGrid>

Use PriorityBinding for columns accessing different properties:

<DataGridTextColumn Header="MixedProperty"
    <DataGridTextColumn.Binding>
        <PriorityBinding>
            <Binding Path="PlayersProperty"/>
            <Binding Path="MonstersProperty"/>
        </PriorityBinding>
    <DataGridTextColumn.Binding>
</DataGridTextColumn>

Note that autogeneration of columns also can be disabled to define all columns by your own.

  • Related