Bind multiple cells value from datagrid column to a single textbox

2.1k views Asked by At

In my wpf project, I have a datagrid which is being populated by a dataset and contains some columns and many rows. I want to iterate through Column[1] Rows[i] (for example get the values inside the cells of column[1] for all the rows in the datagrid). My question is how can I bind these cells value to a single textbox ? I know using multibinding would be one of the way to achieve the solution but I have not found any help regarding multibinding a textbox through a datagrid. For example, I have read the following questions:

How to bind multiple values to a single WPF TextBlock?

How to use a MultiBinding on DataGridTextColumn?

Also, binding a single value is achievable and I have already done that. I would appreciate any help. Thanks in advance !!

My XAML:

<DataGrid x:Name="datagridbatch"
          FontSize="13.333" FontWeight="Normal"
          IsReadOnly="True"
          SelectionChanged="datagridbatch_SelectionChanged"  
          SelectionUnit="FullRow" SelectionMode="Single"
          VerticalAlignment="Top" HorizontalAlignment="Right"
          Height="615" Width="373" Margin="0,0,0,-582"
          CanUserResizeColumns="False" CanUserResizeRows="False"
          CanUserDeleteRows="False" CanUserAddRows="False"
          RowHeight="30"
          Grid.Row="5" Grid.Column="1"
          CanUserReorderColumns="False" CanUserSortColumns="False"
          ColumnHeaderHeight="25" ColumnWidth="*"
          ScrollViewer.CanContentScroll="True"
          ScrollViewer.VerticalScrollBarVisibility="Auto" />
<TextBox x:Name="input2"
         Margin="0,0,0,0" Width="490" Height="30"
         Grid.Row="0" Grid.Column="1"
         HorizontalAlignment="Left"
         Background="{DynamicResource {x:Static SystemColors.ControlLightLightBrushKey}}"
         FontSize="13.333" FontWeight="Normal"
         Text="{Binding SelectedItem.UNIQUEPART_ID, ElementName=datagridbatch}"
         BorderBrush="{DynamicResource {x:Static SystemColors.ControlLightLightBrushKey}}"
         FontFamily="Tahoma"
         IsReadOnlyCaretVisible="True"
         HorizontalScrollBarVisibility="Auto"
         ScrollViewer.CanContentScroll="True"/>
1

There are 1 answers

2
grek40 On

The idea is using a normal Binding with a converter that handles a collection of items. Using a MultiBinding doesn't really work out for a dynamic collection of binding source items. So whats needed:

  • A DataGrid with items, where each item contains a specific property
  • A TextBox
  • A Binding on the TextBox.Text property, where the SelectedItems from DataGrid are bound with a converter to create a single text
  • A Converter that takes an items collection and creates a string from the item properties
  • Some update logic to ensure updated text, when the selected items update

Lets start with the xaml, it can be pretty simple:

<Window.Resources>
    <local:ItemsToTextConverter x:Key="cItemsToTextConverter"/>
</Window.Resources>

<!-- your surrounding controls -->

<DataGrid x:Name="datagridbatch" SelectionChanged="datagridbatch_SelectionChanged"/>
<TextBox x:Name="input2" Text="{Binding ElementName=datagridbatch,Path=SelectedItems,Converter={StaticResource cItemsToTextConverter},Mode=OneWay}"/>

Note that the binding works only one way - its not as easy to distribute the string value back into multiple items as it is to compress the items into a single string.

The Converter needs to take an items collection and extract a string from the property:

public class ItemsToTextConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var items = value as IEnumerable;
        if (items != null)
        {
            // note: items may contain the InsertRow item, which is of different type than the existing items.
            // so the items collection needs to be filtered for existing items before casting and reading the property
            var items2 = items.Cast<object>();
            var items3 = items2.Where(x => x is MyItemType).Cast<MyItemType>();
            return string.Join(Environment.NewLine, items3.Select(x => x.UNIQUEPART_ID));
        }
        return string.Empty;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new InvalidOperationException();
    }
}

Also, DataGrid.SelectedItems will not automatically fire a binding update, when the selection changes, so you need to trigger a manual update in the selection change event handler:

void datagridbatch_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var binding = BindingOperations.GetBindingExpression(input2, TextBox.TextProperty);
    if (binding != null)
    {
        binding.UpdateTarget();
    }
}