Дано: WPF приложение для редактирования данных некой таблицы в некоторой базе данных.
Требуется: сделать так что бы это работало как задумано ( с помощью Visual Studio 2015 ).
Затык: После нажатия кнопки «Удалить» строка грида остаётся на месте — не удаляется.

Предупреждение


qw1 дал комент по изложенному материалу:
DataSet плохое решение для WPF-приложения.
И вообще, этот проект архитектурно ужасен — очень много code behind, лучше пользоваться чистым MVVM.

За загрузку данных из БД должен отвечать слой доступа к данным (DAL).
Данные грузятся в ObservableCollection, который назначается как ItemsSource у DataGrid.
Команды манипуляции с данными вызывают DAL для удаления записи из БД и одновременно убирают item из коллекции.

Вывод: Обновление DataGrid описанным в статье способом нельзя, но если очень хочется, то можно — просто испоганите архитектуру приложения.

Начало


Сделал всё по учебнику ( "Пошаговое руководство. Привязка элементов управления WPF к набору данных" ).
И всё работало и было хорошо, пока я не добавил кнопку «DELETE».
meteoStationsReferenceTableAdapter.Delete(recordIndex);

Корень зла


Пошаговое руководство ни чего о кнопке «Удалить» не говорило, её добавление стало причиной всех бед.
Начались странности:
  • открываешь приложение
  • в DataGrid автоматически подгружается табличка
  • нажимаешь кнопку «DELETE»
  • а ни чего не происходит!

Если переоткрыть приложение, то да, запись отсутствует — то есть запись была удалена, но DataGrid этого не отрисовал.

Поиск решения


Поискал у DataGrid в методах «Refresh» и на удивление ни чего похожего не нашёл.
Спросил у Гугла «refresh datagrid wpf c#».

Сбросить ItemsSource

1) How to refresh datagrid in WPF
myGrid.ItemsSource = null;
myGrid.ItemsSource = myDataSource;

Присваивания ItemsSource я ни где не писал, и я был без понятия где бы такое могло быть — такой вариант не годился.
<DataGrid x:Name="MeteoStationsReferenceDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding}" Margin="0,52,9.6,10" RowDetailsVisibilityMode="VisibleWhenSelected" CanUserReorderColumns="False">
	<DataGrid.Columns>
		<DataGridTextColumn x:Name="IdColumn" Binding="{Binding id}" Header="id" Width="SizeToHeader"/>
		<DataGridTextColumn x:Name="NameColumn" Binding="{Binding name}" Header="name" Width="SizeToHeader"/>
		<DataGridTextColumn x:Name="DescriptionColumn" Binding="{Binding description}" Header="description" Width="SizeToHeader"/>
	</DataGrid.Columns>
</DataGrid>

ItemsSource="{Binding}"

ни какого ItemsSource.

Обновить отображение

2) [WPF] Обновление DataGrid
Предлагается два варианта, первый:
this.TransactionsDataGrid.ItemsSource = null;
this.TransactionsDataGrid.ItemsSource = DataProvider.Instance.GetTransactions();

Где у меня DataProvider и что бы это такое могло быть я снова был без понятия.
Второй вариант:
CollectionViewSource.GetDefaultView(TransactionsDataGrid.ItemsSource).Refresh();

Это было уже понятней, я попробовал:
проверка решения
var meteoStationsReferenceDataGrid = this.MeteoStationsReferenceDataGrid;
if (meteoStationsReferenceDataGrid?.ItemsSource != null)
{
	var collectionView = CollectionViewSource.GetDefaultView(
		meteoStationsReferenceDataGrid.ItemsSource);
	collectionView?.Refresh();
}


Ни чего не изменилось — желанного результата не получилось, ок, продолжим поиски.
Сколько я не искал везде был вариант со сбросом и присваиванием ItemsSource. Ещё был вариант с "INotifyPropertyChanged Interface", но для меня это совсем дремучий лес.

Решение


Я продолжал поиски, пока Фортуна не улыбнулась мне, подсунув ссылку [RESOLVED] Refresh DataGridView after adding/deleting records, где было чёрным по белому написано:
Private Sub btnSave_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnSave.Click

'----------------------------------------------------------------------------

	
	Dim newRow = DirectCast(ProduseBindingSource.AddNew(), DataRowView)
	newRow("pret") = Pret.Text
	ProduseBindingSource.EndEdit()
	Me.ProduseTableAdapter.Update(Me.Database1DataSet1)
     
End Sub

И тут до меня доехало, что данные надо перечитать, в смысле залить по новой:
/* Удаляем запись */
meteoStationsReferenceTableAdapter.Delete(recordIndex);
/*
Флаг ClearBeforeFill наверное надо установить на инициализации формы
*/
meteoStationsReferenceTableAdapter.ClearBeforeFill = true ;
var meteoStationsReferenceDataSet = this._meteoStationsReferenceDataSet ;
if ( meteoStationsReferenceDataSet != null )
{
/* читаем данные */
	meteoStationsReferenceTableAdapter.Fill
		(
			meteoStationsReferenceDataSet.meteo_stations_reference ) ;
}

Проверил — получилось.
На мой вкус повторное чтение набора данных это более «цивилизованный» способ чем сброс и установка ItemsSource.
PS
С WPF я так и не подружился.

Исходники на GitHub

Комментарии (15)


  1. qw1
    11.03.2016 00:11
    +9

    Ещё был вариант с «INotifyPropertyChanged Interface», но для меня это совсем дремучий лес.

    С этим надо разбираться, нотификации это ключ к правильным интерфейсам без жёстких рефрешей и прочей ереси.
    Коллекция, привязанная к DataGrid, должна реализовывать INotifyCollectionChanged.

    В качестве DataSource вместо List<T> можно взять ObservableCollection<T>, при вставке/удалении элемента эта коллекция самостоятельно оповещает DataGrid через INotifyCollectionChanged


    1. SbWereWolf
      11.03.2016 00:47
      -7

      доступным для "эникейщиков" языком можно изложить?
      Я DataSource ни где ни как не задавал, как я его тип изменю на ObservableCollection ?

      самостоятельно оповещает

      звучит волшебно, но как это реализовать?
      пример wpf приложения на гите, как в нём применить ваше решение?


      1. leschenko
        11.03.2016 00:56
        +1

        DataProvider.Instance.GetTransactions(); должен возвращать не просто IList<что-то там>, а ObservableCollection<что-то там>.


        1. SbWereWolf
          11.03.2016 01:05
          -6

          я писал:

          Где у меня DataProvider и что бы это такое могло быть я снова был без понятия.

          и сейчас я напишу тоже самое:
          Где у меня DataProvider и что бы это такое могло быть я без понятия.

          Ваши подсказки мне не помогли, но тому кто поумней конечно пригодятся.


      1. qw1
        11.03.2016 01:09
        +1

        DataSet плохое решение для WPF-приложения.
        И вообще, этот проект архитектурно ужасен — очень много code behind, лучше пользоваться чистым MVVM.

        За загрузку данных из БД должен отвечать слой доступа к данным (DAL).
        Данные грузятся в ObservableCollection, который назначается как ItemsSource у DataGrid.
        Команды манипуляции с данными вызывают DAL для удаления записи из БД и одновременно убирают item из коллекции.


        1. SbWereWolf
          11.03.2016 01:15
          -3

          Так уже понятней. Но в данном случаи не применимо, потому что выполнено всё на автопилоте, без понимания сути действий — всё сделано как Микрософт прописал. Моего там кнопка "Удалить" и "Обновить".


          1. qw1
            11.03.2016 01:42

            Наверное, очень старое руководство. Всё сделано в стиле WinForms.
            В новом API WinRT, в UWP DataSet больше не поддерживается.


          1. lair
            11.03.2016 02:19
            +8

            Писать код без понимания сути действий — очень плохая практика.


  1. SbWereWolf
    11.03.2016 01:15

    .


  1. semmaxim
    11.03.2016 07:36
    +7

    Ещё был вариант с «INotifyPropertyChanged Interface», но для меня это совсем дремучий лес.

    Вы серьёзно?! Да без данного паттерна к WPF вообще подступать нельзя. В нём всё завязано на PropertyChanged / CollectionChanged. Почему Вы не WinForms взяли?


  1. Genoik
    11.03.2016 09:01

    я бы вам посоветовал прочитать вот тут все внимательно, благо расписано по шагам: http://howtowpf.ru/datagrid-mvvm/


  1. Seekeer
    11.03.2016 09:40
    +1

    ни где
    ни чего
    ни какого
    до меня доехало
    Скажите, для Вас русский — не родной? Иного объяснения подобным повторяющимся ошибкам я не вижу.

    Читая подобные темы понимаешь, что Хабр действительно скорее мёртв, чем жив. Особенно учитывая, что статья на главной.


    1. SbWereWolf
      11.03.2016 09:54
      -2

      пишите свои статьи, кто же вам мешает? свято место пусто не бывает, если вы его красотой не можете наполнить, то другие люди заваливают его мусором и грязью.

      у меня была проблема которую я решал в течении нескольких часов, и по результатам поиска я видел что я не один такой, поэтому я поделился своим опытом.
      Вам есть чем поделиться? Делитесь.
      Я к этой статье получил до чёрта минусов, но я рад что моё решение получило квалифицированную оценку, и у следующего человека который будет искать решение — будет масса подсказок.


      1. semmaxim
        11.03.2016 10:33
        +3

        Раз Вы выставили свою статью на всеобщее обозрение — будьте готовы, что её будут обсуждать и даже критиковать.
        Тем более Вы совершенно не разобрались в том, что используете. Да ещё и статью написали, в которой не рассказали, как именно надо делать, а рассказали, как делать не надо и как можно это "не надо" подпереть жутким костылём.


      1. Seekeer
        11.03.2016 11:39
        +4

        у меня была проблема которую я решал в течении нескольких часов
        Основная Ваша проблема не в том, что Вы чего-то не знаете, а в том, что не умеете искать. Нормально сформулированный поисковый запрос и вы уже через 5 минут читаете о INotifyCollectionChanged .

        я поделился своим опытом.
        Хабр это всё-таки не площадка для личных блогов и не любым своим опытом стоит делиться. Как минимум нужно убедиться в правильности того, чем вы хотите поделиться, а ещё лучше — в уникальности.
        Ну и прогнать хотя бы через примитивнейшую проверку грамотности.

        будет масса подсказок.
        Задав вопрос гуглу можно в течении нескольких минут получить намного больше подсказок..