Search code examples
c#wpfbindingdatagrid

Display text instead of ID in WPF DataGrid


I have a table that includes integers that are ids linking to another table. For example, one field may be called StatusId, has a value of 4 and looks at tblStatuses to find the matching record.

I created a WPF DataGrid to put this data into, all working.

I created a CollectionViewSource to store the records from tblStatuses, all working.

I created a CellTemplate and CellEditingTemplate to show the field and when editing, show a dropdown with the current status which can then be changed. All working.

Where I am now stuck is to show the current status in its lookup text value in the column in the celltemplate, instead of just the id. i.e. display 'Current' instead of 4.

I have tried using the same ComboBox method in CellTemplate and modifying the ComboBox Template to show a label but it shows the wrong value. I'm not sure what else to try at the moment and google/so aren't helping, mostly because this is a difficult one to search.

<DataGrid Name="grdLearners" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Binding="{Binding Surname}" Header="Surname" />
            <DataGridTemplateColumn Header="Status">
               <DataGridTemplateColumn.CellTemplate>
                  <DataTemplate>
                     <TextBlock Text="{Binding StatusID}"></TextBlock> // displays id i.e. 4, need to display text i.e. 'Current'
                  </DataTemplate>
               </DataGridTemplateColumn.CellTemplate>
               <DataGridTemplateColumn.CellEditingTemplate>
                  <DataTemplate>
                     <ComboBox SelectedValue="{Binding StatusID}" SelectedValuePath="Status_ID" DisplayMemberPath="Status" ItemsSource="{Binding Source={StaticResource StatusesList}}"></ComboBox> // displays list of statuses and correctly shows/sets the id in the row
                  </DataTemplate>
               </DataGridTemplateColumn.CellEditingTemplate>
           </DataGridTemplateColumn>
         </DataGrid.Columns>
  </DataGrid>

Hoping someone can shed some light on this or point me in the right direction. Let me know if you need any other code posted.

CellEditingTemplate

Code for Collection

<Window.Resources>
    <CollectionViewSource x:Key="StatusesList"  CollectionViewType="ListCollectionView"/>
</Window.Resources>

Code-behind for setting data/collection:

AppDb db = new AppDb();
var learners = db.Learners.AsNoTracking().Take(40).ToList();            

var stats = db.StudentStatuses.AsNoTracking().ToList();

CollectionViewSource itemCollectionViewSource;
itemCollectionViewSource = (CollectionViewSource)(FindResource("StatusesList"));
itemCollectionViewSource.Source = stats;

var grid = grdLearners as DataGrid;
grid.ItemsSource = learners;

Solution

  • If you want to display the text, you should store it in your data object. You are currently only storing the id in the StatusID property. If you add a Status property to your data object, you could bind the SelectedItem property of the ComboBox in the CellEditingTemplate to this one:

    <TextBlock Text="{Binding Status.Status}"/>
    ...
    <ComboBox SelectedItem="{Binding Status}" DisplayMemberPath="Status" ItemsSource="{Binding Source={StaticResource StatusesList}}" />
    

    If you don't want to add another property to your data object, you could use a converter that looks up the value in the StatusesList:

    public class StatusConverter : IMultiValueConverter
    {
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            int statusId = (int)values[0];
            IEnumerable<TheStatus> statuses = (IEnumerable<TheStatus>)values[1];
    
            return statuses.FirstOrDefault(x => x.Status_ID == statusId)?.Status;
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    XAML:

    <DataGridTemplateColumn Header="Status">
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <TextBlock>
                    <TextBlock.Text>
                        <MultiBinding>
                            <MultiBinding.Converter>
                                <local:StatusConverter />
                            </MultiBinding.Converter>
                            <Binding Path="StatusID" />
                            <Binding Path="SourceCollection" Source="{StaticResource StatusesList}" />
                        </MultiBinding>
                    </TextBlock.Text>
                </TextBlock>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
        <DataGridTemplateColumn.CellEditingTemplate>
            <DataTemplate>
                <ComboBox SelectedValue="{Binding StatusID}" SelectedValuePath="Status_ID" DisplayMemberPath="Status" 
                          ItemsSource="{Binding Source={StaticResource StatusesList}}"></ComboBox>
            </DataTemplate>
        </DataGridTemplateColumn.CellEditingTemplate>
    </DataGridTemplateColumn>
    

    Note that you need to change TheStatus to whatever the name of your status type is in the converter.