Гость
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / DataGrid генерирует пустые строки / 7 сообщений из 7, страница 1 из 1
04.11.2014, 00:50
    #38794941
НемоКэп42
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
DataGrid генерирует пустые строки
Есть два потока - расчётный и поток интерфейса. В расчётном обновляется ObservableCollection, плюс есть ещё обработчик на событие CollectionChanged этой коллекции. Как я понял, этот обработчик работает в том же потоке, в котором произошло изменение ObservableCollection - т. е. в моём случае это расчётный поток. Поэтому, чтобы обновить интерфейс по обновлению ObservableCollection, байндинг на эту ObservableCollection не поможет - надо руками через диспетчер заполнять контрол.

И вот я написал код, в котором заполнение листбокса работает, а заполнение датагрида - нет. Датагрид показывает какие-то пустые узкие полоски. Я никак не могу понять, в чём дело.

Вот код, который можно сразу скопировать и запустить:

C#
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MyClass MyCls { get; set; }
        public ObservableCollection<MyClass> MyCollection { get; set; }

        public MainWindow()
        {
            InitializeComponent();


            MyCollection = new ObservableCollection<MyClass>();
            MyCollection.CollectionChanged += MyCollection_CollectionChanged;

            Thread t = new Thread(new ThreadStart(() =>
                {
                    for (int i = 0; i < 10; i++)
                    {
                        MyCollection.Add(new MyClass() { Integer = i, Str = "String" + i });
                        Thread.Sleep(500);
                    }
                }));

            t.Start();
        }

        void MyCollection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            Dispatcher.Invoke(
                () =>
                {
                    foreach (var item in e.NewItems)
                        dataGrid.Items.Add((MyClass)item);
                });
        }
    }

    public class MyClass
    {
        public int Integer { get; set; }
        public string Str { get; set; }
    }
}



XAML (можно раскомментить листбокс а закомментить датагрид, чтобы увидеть, что с листбоксом работает, а с гридом - нет)
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
<Window
  x:Class="WpfApplication1.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="MainWindow"
  Height="350"
  Width="525">
  <Grid>

    <!--<ListBox
      Name="dataGrid">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <StackPanel>
            <TextBlock
              Text="{Binding Path=Integer}" />
            <TextBlock
              Text="{Binding Path=Str}" />
          </StackPanel>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>-->

    <DataGrid
      Name="dataGrid">
      <DataGrid.ItemTemplate>
        <DataTemplate>
          <StackPanel>
            <TextBlock
              Text="{Binding Path=Integer}" />
            <TextBlock
              Text="{Binding Path=Str}" />
          </StackPanel>
        </DataTemplate>
      </DataGrid.ItemTemplate>
    </DataGrid>
    
  </Grid>
</Window>

...
Рейтинг: 0 / 0
04.11.2014, 00:54
    #38794942
НемоКэп42
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
DataGrid генерирует пустые строки
Свойство MyCls не нужно.
...
Рейтинг: 0 / 0
05.11.2014, 12:45
    #38796109
Roman Mejtes
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
DataGrid генерирует пустые строки
http://wpftutorial.net/DataGrid.html#manualColumns

в ItemTemplate на сколько я помню определяется шаблон в котором должен быть ContentPresenter, в котором уже будет контент строки состоящий из ячеек. Точно не помню, может ItemPresenter или как то еще.
...
Рейтинг: 0 / 0
05.11.2014, 15:21
    #38796429
Ilya81
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
DataGrid генерирует пустые строки
ItemsPresenter выводит коллекцию, ContentPresenter - один элемент. Но я не понял, в чём ж всё-таки проблема поставить binding на ItemsSource - иначе он вполне может и не использовать ItemTemplate, тогда нужно добавлять в Item'ы сами control'ы. Или попробуйте использовать вручную ItemContainerGenerator, хотя по мне это было б т. с. реализацией через кое-что.
...
Рейтинг: 0 / 0
06.11.2014, 21:26
    #38797792
НемоКэп42
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
DataGrid генерирует пустые строки
Roman Mejtes http://wpftutorial.net/DataGrid.html#manualColumns

в ItemTemplate на сколько я помню определяется шаблон в котором должен быть ContentPresenter, в котором уже будет контент строки состоящий из ячеек. Точно не помню, может ItemPresenter или как то еще.
Шаблон данных там, в ItemTemplate, дожен быть.

Ilya81ItemsPresenter выводит коллекцию, ContentPresenter - один элемент. Но я не понял, в чём ж всё-таки проблема поставить binding на ItemsSource - иначе он вполне может и не использовать ItemTemplate, тогда нужно добавлять в Item'ы сами control'ы. Или попробуйте использовать вручную ItemContainerGenerator, хотя по мне это было б т. с. реализацией через кое-что.
Можно и через байндинг, но только, как я уже сказал, событие изменения (и, соответственно, обработчик этого события) для ObservableCollection срабатывает в том потоке, который эту коллекцию изменил. Поэтому, если байндить MyCollection сразу на датагрид, то датагрид обновляться не будет.

Вобщем, как я понял, вариантов два:

1) обновлять вручную, как у меня в примере, но тогда надо вручную же и генерировать столбцы и прочее для датагрида, т. к. AutoGenerateColumns при ручном добавлении строк работать не будет;

2) сделать байндин, но при этом придумать механизм, который бы передавал изменения из потока, произвёдшего изменения, в поток GUI.

Я предпочёл последний вариант. При этом пришлось завести вторую коллекцию. Вообще, смотри http://stackoverflow.com/questions/26727848/datagrid-generates-empty-rows - мне подошёл последний вариант в правильном ответе. Ещё смотри первый комментарий под вопросом.

Как я понял, вариант со второй коллекцией подводит к шаблону MVVM, где рабочий поток, который изменяет MyCollection (в моём примере, или _MyCollection в примере по ссылке), работает с моделью (MyCollection принадлежит модели), а в потоке GUI сидит модель представления со своей коллекцией. Ну и по изменению коллекции модели, изменяется коллекция модели представления, а последняя уже, в свою очередь, автоматом изменяет представление (датагрид) через привязку.

Т. е. вот эта особенность датагрида WPF'овского (не хочет автогенерить строки и столбцы при добавлении данных "вручную") опять говорит о том, что WPF заточен именно на определённую работу, а не абы как. Про это и твердят в т. ч. его создатели - что WPF заточен на MVVM. Если применять что-то другое, то получится какашка.

Та же история с TreeView, например - если добавлять вручную элементы, то запаришься с ним работать, а если через MVVM, то всё как по маслу (только кода много получается) - http://www.codeproject.com/Articles/26288/Simplifying-the-WPF-TreeView-by-Using-the-ViewMode
...
Рейтинг: 0 / 0
06.11.2014, 21:28
    #38797796
НемоКэп42
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
DataGrid генерирует пустые строки
авторThis is yet another example of how WPF requires you to shift mental gears to make use of the platform appropriately. We aren’t in Kansas anymore, Toto.
Не дают работать абы как-нибудь! Прямо связывают по рукам и ногам, редиски!
...
Рейтинг: 0 / 0
07.11.2014, 14:05
    #38798527
Ilya81
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
DataGrid генерирует пустые строки
НемоКэп42 сделать байндин, но при этом придумать механизм, который бы передавал изменения из потока, произвёдшего изменения, в поток GUI.
Да ничего и придумывать не надо - если просто модификация коллекции из другого потока, то пишите:
Код: c#
1.
Application.Current.Dispatcher.Invoke(new Action(()=>MyCollection.Add(new MyClass() { Integer = i, Str = "String" + i })));


Если коллекция заменяется, вызываете событие интерфейса INotifyPropertyChanged.
...
Рейтинг: 0 / 0
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / DataGrid генерирует пустые строки / 7 сообщений из 7, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]