WPF DataGrid: отсутствует пустая строка

Я создаю окно WPF с DataGrid, и я хочу показать пустую строку «новый элемент» внизу сетки, которая позволяет мне добавлять новый элемент в сетку. По какой-то причине пустая строка не отображается в сетке моего окна. Вот разметка, которую я использовал для создания DataGrid:

<toolkit:DataGrid  x:Name="ProjectTasksDataGrid" 
                   DockPanel.Dock="Top" 
                   Style="{DynamicResource {x:Static res:SharedResources.FsBlueGridKey}}"
                   AutoGenerateColumns="False" 
                   ItemsSource="{Binding SelectedProject.Tasks}" 
                   RowHeaderWidth="0" 
                   MouseMove="OnStartDrag" 
                   DragEnter="OnCheckDropTarget" 
                   DragOver="OnCheckDropTarget" 
                   DragLeave="OnCheckDropTarget" 
                   Drop="OnDrop" 
                   InitializingNewItem="ProjectTasksDataGrid_InitializingNewItem">
    <toolkit:DataGrid.Columns>
        <toolkit:DataGridCheckBoxColumn HeaderTemplate="{DynamicResource {x:Static res:SharedResources.CheckmarkHeaderKey}}" Width="25" Binding="{Binding Completed}" IsReadOnly="false"/>
        <toolkit:DataGridTextColumn Header="Days" Width="75" Binding="{Binding NumDays}" IsReadOnly="false"/>
        <toolkit:DataGridTextColumn Header="Due Date" Width="75" Binding="{Binding DueDate, Converter={StaticResource standardDateConverter}}" IsReadOnly="false"/>
        <toolkit:DataGridTextColumn Header="Description" Width="*" Binding="{Binding Description}" IsReadOnly="false"/>
    </toolkit:DataGrid.Columns>
</toolkit:DataGrid>

Я не могу понять, почему не отображается пустая строка. Я пробовал очевидные вещи (IsReadOnly="false", CanUserAddRows="True"), но безуспешно. Есть идеи, почему пустая строка отключена? Спасибо за вашу помощь.


person David Veeneman    schedule 23.09.2009    source источник


Ответы (7)


Винсент Сибал разместил статья, описывающая, что требуется для добавления новых строк в DataGrid. Существует довольно много возможностей, и большая часть из них зависит от типа коллекции, которую вы используете для SelectedProject.Tasks.

Я бы рекомендовал убедиться, что Tasks не является коллекцией только для чтения и поддерживает один из требуемых интерфейсов (упомянутых в предыдущей ссылке), чтобы новые элементы можно было правильно добавлять с помощью DataGrid.

person Reed Copsey    schedule 23.09.2009
comment
На самом деле Задачи - это ObservableCollection ‹T›. Я выполнил тестовый проект, привязав сетку данных к той же коллекции, и пустая строка присутствует в нижней части сетки. Сообщение в блоге Винсента хорошее, но он говорит, что вам нужно реализовать IEditableObject, а это не так. Обычный ванильный DataGrid, связанный с ObservableCollection ‹T›, должен отображать пустую строку. См. codeproject.com/KB/WPF/MVVM_DataGrid.aspx. - person David Veeneman; 23.09.2009
comment
Большое спасибо, что помогли. Искренне. - person Alaa Jabre; 06.07.2013

У вас также должен быть конструктор по умолчанию для типа в коллекции.

person Community    schedule 30.01.2010
comment
Это ответ, который надо было проверить. ›.‹ Спасибо, случайный пользователь! - person mpen; 12.01.2011
comment
Да, это ответ на ту же проблему, что и у меня. - person Xenan; 25.04.2011
comment
Что, если коллекция имеет интерфейсный тип? ObservableCollection<IThing> - person Nick Strupat; 19.03.2013
comment
Я пропустил этот правильный ответ, потому что он находится на загрузке без кода. - person Danil; 28.04.2015
comment
Также следует отметить, что конструктор должен быть public. Кажется, что DataGrid не рассматривает internal или private конструкторы. - person Chris Altig; 12.03.2020

Наконец вернулся к этому. Я не собираюсь менять принятый ответ (зеленая галочка), но вот причина проблемы:

Моя модель представления обертывает классы домена для обеспечения инфраструктуры, необходимой WPF. Я написал статью на CodeProject о методе обертывания, который я использую. , который включает класс коллекции с двумя параметрами типа:

VmCollection<VM, DM>

где DM - это обернутый класс домена, а DM - это класс WPF, который оборачивает его.

Он утверждает, что по какой-то странной причине наличие второго параметра типа в классе коллекции приводит к тому, что WPF DataGrid становится недоступным для редактирования. Исправление состоит в том, чтобы исключить параметр второго типа.

Не могу сказать, почему это работает, только то, что работает. Надеюсь, это поможет кому-то еще в будущем.

person David Veeneman    schedule 28.10.2009

На мой взгляд, это ошибка в DataGrid. Ссылка Майка Бландфорда помогла мне чтобы наконец понять, в чем проблема: DataGrid не распознает тип строк, пока не будет привязан реальный объект. Строка редактирования не отображается, потому что сетка данных не знает типы столбцов. Вы могли бы подумать, что привязка строго типизированной коллекции будет работать, но это не так.

Чтобы расширить ответ Майка Бландфорда, вы должны сначала назначить пустую коллекцию, а затем добавить и удалить строку. Например,

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        // data binding
        dataGridUsers.ItemsSource = GetMembershipUsers();
        EntRefUserDataSet.EntRefUserDataTable dt = (EntRefUserDataSet.EntRefUserDataTable)dataGridUsers.ItemsSource;
        // hack to force edit row to appear for empty collections
        if (dt.Rows.Count == 0)
        {
            dt.AddEntRefUserRow("", "", false, false);
            dt.Rows[0].Delete();
        }
    }
person Brett    schedule 06.09.2011

Добавьте пустой элемент в свой ItemsSource, а затем удалите его. После этого вам, возможно, придется вернуть для CanUserAddRows значение true. Я прочитал это решение здесь : (Сообщения Джарри и Рика Роэнов)

У меня возникла эта проблема, когда я установил ItemsSource в DataTable's DefaultView, а представление было пустым. Столбцы были определены, поэтому он должен был их получить. Хех.

person Mike Blandford    schedule 27.11.2010
comment
Фу. Спасибо. Это сводит меня с ума. В конце концов я отказался от сущностей и перешел к типизированным наборам данных, и даже это не удалось. Уловка состоит в том, чтобы сначала назначить коллекцию, а затем затем манипулировать ею, добавляя и удаляя объект. - person Brett; 06.09.2011

Это случилось со мной, я забыл обновить инстанс, и для меня это был кошмар. как только я создал экземпляр коллекции в onviewloaded, проблема была решена.

`observablecollection<T> _newvariable = new observablecollection<T>();`

это решило мою проблему. надеюсь, что это может помочь другим

person krunal patel    schedule 02.08.2018

Для меня лучший способ реализовать редактируемый асинхронный DataGrid выглядит так:

Просмотреть модель:

 public class UserTextMainViewModel : ViewModelBase
{ 
    private bool _isBusy;
    public bool IsBusy
    {
        get { return _isBusy; }
        set
        {
            this._isBusy = value;
            OnPropertyChanged();
        }
    }




    private bool _isSearchActive;
    private bool _isLoading;


    private string _searchInput;
    public string SearchInput
    {
        get { return _searchInput; }
        set
        {
            _searchInput = value;
            OnPropertyChanged();

            _isSearchActive = !string.IsNullOrEmpty(value);
            ApplySearch();
        }
    }

    private ListCollectionView _translationsView;
    public ListCollectionView TranslationsView
    {
        get
        {
            if (_translationsView == null)
            {
                OnRefreshRequired();
            }

            return _translationsView;
        }
        set
        {
            _translationsView = value;
            OnPropertyChanged();
        }
    }


    private void ApplySearch()
    {
        var view = TranslationsView;

        if (view == null) return;

        if (!_isSearchActive)
        {
            view.Filter = null;
        }
        else if (view.Filter == null)
        {
            view.Filter = FilterUserText;
        }
        else
        {
            view.Refresh();
        }
    }

    private bool FilterUserText(object o)
    {
        if (!_isSearchActive) return true;

        var item = (UserTextViewModel)o;

        return item.Key.Contains(_searchInput, StringComparison.InvariantCultureIgnoreCase) ||
               item.Value.Contains(_searchInput, StringComparison.InvariantCultureIgnoreCase);
    }




    private ICommand _clearSearchCommand;
    public ICommand ClearSearchCommand
    {
        get
        {
            return _clearSearchCommand ??
                   (_clearSearchCommand =
                    new DelegateCommand((param) =>
                    {
                        this.SearchInput = string.Empty;

                    }, (p) => !string.IsNullOrEmpty(this.SearchInput)));
        }
    }

    private async void OnRefreshRequired()
    {
        if (_isLoading) return;

        _isLoading = true;
        IsBusy = true;
        try
        {
            var result = await LoadDefinitions();
            TranslationsView = new ListCollectionView(result);
        }
        catch (Exception ex)
        {
            //ex.HandleError();//TODO: Needs to create properly error handling
        }

        _isLoading = false;
        IsBusy = false;
    }


    private async Task<IList> LoadDefinitions()
    {
        var translatioViewModels = await Task.Run(() => TranslationRepository.Instance.AllTranslationsCache
        .Select(model => new UserTextViewModel(model)).ToList());
        return translatioViewModels;
    }


}

XAML:

<UserControl x:Class="UCM.WFDesigner.Views.UserTextMainView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
         xmlns:model="clr-namespace:Cellebrite.Diagnostics.Model.Entities;assembly=Cellebrite.Diagnostics.Model"
         xmlns:System="clr-namespace:System;assembly=mscorlib"
         xmlns:converters1="clr-namespace:UCM.Infra.Converters;assembly=UCM.Infra"
         xmlns:core="clr-namespace:UCM.WFDesigner.Core"
         mc:Ignorable="d"
         d:DesignHeight="300"
         d:DesignWidth="300">


<DockPanel>
    <StackPanel Orientation="Horizontal"
                DockPanel.Dock="Top"
                HorizontalAlignment="Left">


        <DockPanel>

            <TextBlock Text="Search:"
                       DockPanel.Dock="Left"
                       VerticalAlignment="Center"
                       FontWeight="Bold"
                       Margin="0,0,5,0" />

            <Button Style="{StaticResource StyleButtonDeleteCommon}"
                    Height="20"
                    Width="20"
                    DockPanel.Dock="Right"
                    ToolTip="Clear Filter"
                    Command="{Binding ClearSearchCommand}" />

            <TextBox Text="{Binding SearchInput, UpdateSourceTrigger=PropertyChanged}"
                     Width="500"
                     VerticalContentAlignment="Center"
                     Margin="0,0,2,0"
                     FontSize="13" />

        </DockPanel>
    </StackPanel>
    <Grid>
        <DataGrid ItemsSource="{Binding Path=TranslationsView}"
                  AutoGenerateColumns="False"
                  SelectionMode="Single"
                  CanUserAddRows="True">
            <DataGrid.Columns>
              <!-- your columns definition is here-->
            </DataGrid.Columns>
        </DataGrid>
        <!-- your "busy indicator", that shows to user a message instead of stuck data grid-->
        <Border Visibility="{Binding IsBusy,Converter={converters1:BooleanToSomethingConverter TrueValue='Visible', FalseValue='Collapsed'}}"
                Background="#50000000">
            <TextBlock Foreground="White"
                       VerticalAlignment="Center"
                       HorizontalAlignment="Center"
                       Text="Loading. . ."
                       FontSize="16" />
        </Border>
    </Grid>
</DockPanel>

This pattern allows to work with data grid in a quite simple way and code is very simple either. Do not forget to create default constructor for class that represents your data source.

person Mr.B    schedule 09.06.2016