Thursday, October 27, 2022

XamDataGrid GotFocus event

I have a requirement to put a XamDataGrid into edit mode when it receives focus. The idea is that when a user tabs into a XamDataGrid I find the first editable cell and make it the active cell. Then I put the grid into edit mode. Sounds simple - of course it isn't.

My first attempt added a GotFocus handler but I found it was also being called as the user navigated around within the grid. Each cell raises a GotFocus event as it receives focus and, by default, this is bubbled up to the grid. There is no way to know why the event is being called (even OriginalSource isn't reliable).

Infragistics has a class called Infragistics.Windows.Helpers.FocusWithinManager. Their documentation is obtuse and the example is overly complex. It turns out to be quite simple to use (but not as simple as an event would be).

Start a new Visual Studio project (I'm using 2022). Make it a WPF App (.Net Framework) C# project and call it EditOnFocus.

We will start by subclassing the XamDataGrid. Add references to InfragisticsWPF4, InfragisticsWPF4.Editors, and InfragisticsWPF4.DataPresenter.


Add a new class called EditOnFocusXamDataGrid that inherits XamDataGrid. It needs a static constructor that registers a FocusWithinManager.

static EditOnFocusXamDataGrid()
{
    FocusWithinManager.RegisterType(typeof(EditOnFocusXamDataGrid), new System.Windows.PropertyChangedCallback(OnIsFocusWithinChanged));
}

The FocusWithinManager will call OnIsFocusWithinChanged whenever the focus changes. Let's write that method.

private static void OnIsFocusWithinChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    XamDataGrid g = (XamDataGrid)d;
    CellValuePresenter cvp;
 
    // We're a XamDataGrid and we just got focus
    if (g != null && !Convert.ToBoolean(e.OldValue) && Convert.ToBoolean(e.NewValue))
    {
        // Find the first editable cell
        foreach (DataRecord r in g.Records.Where(r => r is DataRecord).Cast<DataRecord>())
        {
            foreach (Cell c in r.Cells)
            {
                cvp = CellValuePresenter.FromCell(c);
                if (cvp != null && cvp.IsEditingAllowed)
                {
                    // Start editing the cell
                    g.ActiveCell = c;
                    g.ExecuteCommand(DataPresenterCommands.StartEditMode);
                    return;
                }
            }
        }
    }
}

This demonstration will display a text box, our new EditOnFocusXamDataGrid and another text box stacked on top of each other. When you tab from the top text box, the XamDatGrid will go into edit mode. The same will happen when you back-tab from the bottom text box.

Here's the XAML.

<Window x:Class="EditOnFocus.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:igDP="http://infragistics.com/DataPresenter"
        xmlns:local="clr-namespace:EditOnFocus"
        mc:Ignorable="d"
        DataContext="{Binding RelativeSource={RelativeSource Self}}"
        Title="MainWindow" Height="450" Width="800">
    <StackPanel Orientation="Vertical">
        <TextBox Width="100"/>
        <local:EditOnFocusXamDataGrid DataSource="{Binding Items}" Height="100">
            <local:EditOnFocusXamDataGrid.FieldLayoutSettings>
                <igDP:FieldLayoutSettings AutoGenerateFields="False"/>
            </local:EditOnFocusXamDataGrid.FieldLayoutSettings>
            <local:EditOnFocusXamDataGrid.FieldLayouts>
                <igDP:FieldLayout>
                    <igDP:FieldLayout.Fields>
                        <igDP:TextField Label="Code" Name="Code" AllowEdit="True" Width="60"/>
                        <igDP:TextField Label="Description" Name="Description" AllowEdit="False" Width="*"/>
                    </igDP:FieldLayout.Fields>
                </igDP:FieldLayout>
            </local:EditOnFocusXamDataGrid.FieldLayouts>
        </local:EditOnFocusXamDataGrid>
        <TextBox Width="100"/>
    </StackPanel>
</Window>

Here's the code behind.

using System;
using System.Collections.Generic;
using System.Windows;
 
namespace EditOnFocus
{
    public class cItem
    {
        public String Code { get; set; }
        public String Description { get; set; }
    }
 
    public partial class MainWindow : Window
    {
        public List<cItem> Items { get; set; } = new List<cItem> {
            new cItem {Code="Code A", Description="Description A"},
            new cItem {Code="Code B", Description="Description B"}
        };
 
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}

When you run the demonstration, click in the top text box and hit tab. You can see the grid goes into edit mode as soon as it receives focus.



Shift-tabbing from the bottom text box works too.



No comments:

Post a Comment