0
votes

I have a WPF Datagrid with 3 columns as part of a window. The ItemsSource is set from an array of payment objects (grdPayments.ItemsSource = payments). When the focus is set to the grid in code behind I want the third cell in the first row to get focus.

Regardless of which of the following methods that I use, the 2nd row is selected and focused rather than the 1st.

This sets focus to the 3rd cell in the 2nd row:

grdPayments.Focus();
grdPayments.CurrentCell = new DataGridCellInfo(grdPayments.Items[0],grdPayments.Columns[2]);
grdPayments.SelectedCells.Add(dataGridCellInfo);
cell.Focus();
grdPayments.BeginEdit();

This sets focus to the 2nd row:

grdPayments.Focus();
grdPayments.SelectedIndex  = 0;
grdPayments.BeginEdit();

Can anyone tell me whats going on? The DataGrid XAML is below. my:NumberEntry is a custom control:

<DataGrid Name="grdPayments"
          AutoGenerateColumns="False"
          Background="#FF21B721"
          ItemsSource="{Binding}"
          SelectionUnit="Cell"
          SelectionMode="Single"
          Margin="5"
          VirtualizingPanel.IsVirtualizing="False">
      <DataGrid.Resources>
          <Style TargetType="{x:Type DataGridColumnHeader}">
              <Setter Property="Background" Value="#FF21B721"/>
          </Style>
      </DataGrid.Resources>
      <DataGrid.Columns>
          <DataGridTextColumn Width="240"
                              Binding="{Binding Path=Description}"
                              Header="Description"
                              IsReadOnly="True"
                              TextBlock.TextAlignment="Center">
              <DataGridTextColumn.CellStyle>
                  <Style TargetType="DataGridCell">
                      <Setter Property="Background" Value="{StaticResource clBr}"/>
                  </Style>
              </DataGridTextColumn.CellStyle>
          </DataGridTextColumn>
          <DataGridTemplateColumn Width="100" Header="Due">
              <DataGridTemplateColumn.CellTemplate>
                  <DataTemplate>
                      <TextBlock Width="100" Text="{Binding Path=Due, Converter={StaticResource CurrencyPadder}, ConverterParameter=10}" Background="{StaticResource clBr}" Focusable="False"/>
                  </DataTemplate>
              </DataGridTemplateColumn.CellTemplate>
          </DataGridTemplateColumn>
          <DataGridTemplateColumn Width="100" Header="Paid">
              <DataGridTemplateColumn.CellStyle>
                  <Style TargetType="DataGridCell">
                      <Setter Property="Background" Value="{StaticResource clBr}"/>
                  </Style>
              </DataGridTemplateColumn.CellStyle>
              <DataGridTemplateColumn.CellTemplate>
                  <DataTemplate>
                      <my:NumberEntry Decimals="2"
                                      LostFocus="NumberEditor_LostFocus"

                               PreviewKeyDown="NumberEditor_PreviewKeyDown"
                                      MaxLength="10"
                                      TextAlignment="Right"
                                      Text="{Binding Path=Paid, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
                  </DataTemplate>
              </DataGridTemplateColumn.CellTemplate>
          </DataGridTemplateColumn>
      </DataGrid.Columns>

3
To be clear, I want to select a single cell in the first row but can only manage to select either the entire second row or a cell in the second row. I have tried every solution that I have been able to find on the web to no avail. There has to be a way!Michael Bartmon
OK. With SelectionUnit=Fullrow and SelectionMode=Extended, the first row is selected. I still need to do two more things: 1) Select a single cell. 2) Place that cell in edit mode programmatically. It is frustrating that the WPF version of DataGrid makes such an obviously needed function so difficult.Michael Bartmon

3 Answers

0
votes

I'm 99% sure you need to remove:

SelectionUnit="Cell"
0
votes

To access to the row with SelectionUnit set to Cell you have to do:

grdPayments.SelectedItem = grdPayments.SelectedCells[0].item;

It works only if you can select only one cell at time (not in Extended mode).

0
votes

Making the WPF DataGrid behave the way that I want it to seems like a lost cause: it simply was not designed to behave according to my needs.

What I did instead was to dynamically construct a grid of cells in code behind. Each cell in a row can be minutely designed to do anything that I want.

Here is my final, and successful, code:

        Binding bind = null;
        for (int r = 1; r <= rows; r++)
        {
            bind = new Binding("Description");
            bind.Source = payments[r - 1];
            TextBlock col = new TextBlock();
            BindingOperations.SetBinding(col, TextBlock.TextProperty, bind);
            col.TextAlignment = TextAlignment.Left;
            col.VerticalAlignment = VerticalAlignment.Center;
            col.Width = 240;
            bdr = new Border();
            bdr.BorderBrush = System.Windows.Media.Brushes.Black;
            bdr.BorderThickness = new Thickness(2);
            Grid.SetColumn(bdr, 0);
            Grid.SetRow(bdr, r);
            bdr.Child = col;
            grdPayments.Children.Add(bdr);

            bind = new Binding("Due");
            bind.Source = payments[r - 1];
            bind.Converter = new MbcsCentral.CurrencyPaddingConverter();
            bind.ConverterParameter = "10";
            col = new TextBlock();
            BindingOperations.SetBinding(col, TextBlock.TextProperty, bind);
            col.TextAlignment = TextAlignment.Right;
            col.VerticalAlignment = VerticalAlignment.Center;
            col.HorizontalAlignment = HorizontalAlignment.Left;
            col.Width = 100;
            bdr = new Border();
            bdr.BorderBrush = System.Windows.Media.Brushes.Black;
            bdr.BorderThickness = new Thickness(2);
            Grid.SetColumn(bdr, 1);
            Grid.SetRow(bdr, r);
            bdr.Child = col;
            grdPayments.Children.Add(bdr);


            NumberEntry vi = new NumberEntry();
            bind = new Binding("Paid");
            bind.Source = payments[r - 1];
            bind.StringFormat = "######0.00";
            BindingOperations.SetBinding(vi, NumberEntry.TextProperty, bind);
            vi.MaxLength = 10;
            vi.Decimals = 2;
            vi.Width = 100;
            vi.LostFocus += NumberEditor_LostFocus;
            vi.PreviewKeyDown += PaidBlock_PreviewKeyDown;
            bdr = new Border();
            bdr.BorderBrush = System.Windows.Media.Brushes.Black;
            bdr.BorderThickness = new Thickness(2);
            Grid.SetColumn(bdr, 2);
            Grid.SetRow(bdr, r);
            bdr.Child = vi;
            grdPayments.Children.Add(bdr);
        }

grdPayments is now an empty Grid in XAML: a place holder. Each cell is made of a UiElement wrapped in a Border, bound to the data and set into the proper row/column in the Grid.

I don't know if I would do it this way for many more columns than needed here, but it is a simple and exceedingly workable solution to being able to have focus simply set in code behind to any cell and go into editing mode with no clicks required.

Not declarative as a WPF purist might prefer, but I learned very early in my programming career that what matters in the end is that it works.