Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

New Item Placeholder in WPF DataGrid Disappears

Tags:

wpf

datagrid

I have an invalid new item row in my DataGrid. I change the focus to another element, which clears thee backing collection of this datagrid. This causes the new item placeholder (the empty row at the bottom of the datagrid) to disappear.

How do I get it to reappear?

like image 726
kevindaub Avatar asked Nov 04 '22 16:11

kevindaub


2 Answers

If you're clearing the collection such that the DataGrid shows no rows at all, then it sounds like you're facing the same issue as discussed here:

WPF DataGrid: Blank Row Missing

Basically, the issue is that the DataGrid doesn't know what object type it is binding to, and therefore can't generate the columns for the new item placeholder (for example, it doesn't know whether to provide a blank cell for the name of a person, the delivery date of an order, or the hair colour of a pet, etc). One of the answers in the discussion linked above is to add a dummy object then remove it - that worked for me.

like image 196
Michael Avatar answered Nov 15 '22 08:11

Michael


I wasn't able to make the "delete item/reinsert item" fix work for my app. While troubleshooting this issue, I noticed the DataGrid's built-in DeleteCommand would 1) delete the selected row, even for a DataGrid with a bound ItemsSource and 2) not make the NewItemPlaceHolder row disappear if the DeleteCommand was fired on the row while it was in edit mode!

Here's my app's DataGrid, which has a DataGridTemplateColumn containing a button that uses the buil-in DeleteCommand. Note: The row must be selected to enable the button, then clicking the button deletes the row. I'm sure you could subclass DataGrid's OnCanExecuteDelete and modify this behavior, however.

<local:MyDataGrid           
ItemsSource="{Binding Companies}"           
AutoGenerateColumns="False" 
RowHeaderWidth="20"
x:Name="dg">
<DataGrid.Columns>
    <DataGridTemplateColumn Header="Delete">
        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <Button Content="X" 
                        Command="DataGrid.DeleteCommand"/>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn>
    <DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
</DataGrid.Columns>

And as a bonus, here's the subclassed DataGrid that was giving me the same problem you're (or were - this is an old post) facing. It includes code I borrowed from another StackOverflow user that enables one click editing of any DataGridCell.

public class MyDataGrid : DataGrid
{
    public MyDataGrid()
    {
        this.GotFocus += DataGrid_CellGotFocus;                             
    }

    protected override void OnExecutedDelete(ExecutedRoutedEventArgs e)
    {
        // Temporarily remove the GotFocus eventhandler to prevent
        // the datagrid from firing BeginEdit after a row is deleted.
        this.GotFocus -= DataGrid_CellGotFocus;                                         
        base.OnExecutedDelete(e);
        this.GotFocus += DataGrid_CellGotFocus;                             
    }

    private void DataGrid_CellGotFocus(object sender, RoutedEventArgs e)
    {
        // Lookup for the source to be DataGridCell
        if (e.OriginalSource.GetType() == typeof(DataGridCell))
        {
            // Starts the Edit on the row;
            DataGrid grd = (DataGrid)sender;
            grd.BeginEdit(e);

            //Control control = e.OriginalSource as DataGridCell;
            Control control = GetFirstChildByType<Control>(e.OriginalSource as DataGridCell);
            if (control != null)
            {                   
                control.Focus();
            }
        }
    }

    /// <summary>
    /// Returns the first visual tree child item matching T.
    /// </summary>
    /// <param name="prop"></param>
    /// <returns></returns>
    private T GetFirstChildByType<T>(DependencyObject prop) where T : DependencyObject
    {
        // Begin a loop thru all visual tree items for the DependencyObject arg.
        for (int i = 0; i < VisualTreeHelper.GetChildrenCount(prop); i++)
        {
            // Loop to the next visual tree item if the current item is null.
            DependencyObject child = VisualTreeHelper.GetChild((prop), i) as DependencyObject;
            if (child == null)
                continue;

            // Try casting the current item to T. If the cast works, return the visual tree
            // item as T. 
            T castedProp = child as T;
            if (castedProp != null)
                return castedProp;

            // If this code line is reached, the cast failed. Recursively call this method.
            castedProp = GetFirstChildByType<T>(child);

            if (castedProp != null)
                return castedProp;
        }
        // If this code line is reached, no visual tree items in prop match T. Return null.
        return null;
    }   
}
like image 28
David Alan Condit Avatar answered Nov 15 '22 10:11

David Alan Condit