powered by simpleCommunicator - 2.0.51     © 2025 Programmizd 02
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Когнитивный диссонанс
2 сообщений из 2, страница 1 из 1
Когнитивный диссонанс
    #39040333
Roman Mejtes
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Появилась у меня необходимость сделать ObservableStack<T> класс, в принципе задача рядовая, делается такой класс за пару минут или просто копируется из интернетов. Но проблема не о нём, а о том, с чем я столкнулся в процессе реализации интерфейса INotifyCollectionChanged.
Вот такой код я взял для примера:
Код: 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.
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.
65.
66.
67.
68.
69.
70.
<Window x:Class="WpfApplication33.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:wpfApplication33="clr-namespace:WpfApplication33"
        Title="MainWindow" Height="350" Width="525"
        x:Name="Window">
    <Window.Resources>
        <Style TargetType="{x:Type Button}">
            <Setter Property="Width" Value="100"/>
            <Setter Property="Height" Value="25"/>
            <Setter Property="Margin" Value="3"/>
        </Style>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition/>
            <RowDefinition Height="24"/>
        </Grid.RowDefinitions>
        <StackPanel Orientation="Horizontal" Grid.Row="0">
            <Button Click="PushButton_OnClick" Content="Push" />
            <Button Click="FakePushButton_OnClick" Content="Fake push" />
            <Button Click="PopButton_OnClick" Content="Pop" />
            <Button Click="ClearButton_OnClick" Content="Clear" />
            <Button Click="ViewButton_OnClick" Content="View Stack" />

        </StackPanel>
        <UniformGrid Columns="2" Grid.Row="1" >
            <ItemsControl ItemsSource="{Binding  Persons, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}"
                          ScrollViewer.VerticalScrollBarVisibility="Visible"
                          ScrollViewer.CanContentScroll="True"
                          Margin="5">
                <ItemsControl.ItemTemplate>
                    <DataTemplate DataType="{x:Type wpfApplication33:Person}">
                        <UniformGrid Columns="3">
                            <TextBlock Text="{Binding Name}"/>
                            <TextBlock Text="{Binding Age}"/>
                            <Border x:Name="FlashBorder" Background="Transparent">

                            </Border>
                        </UniformGrid>
                        <DataTemplate.Triggers>
                            <EventTrigger RoutedEvent="Control.Loaded">
                                <BeginStoryboard>
                                    <Storyboard TargetName="FlashBorder">
                                        <ColorAnimation Storyboard.TargetProperty="Background.(SolidColorBrush.Color)" From="Green" To="Blue" Duration="0:0:1" />
                                    </Storyboard>
                                </BeginStoryboard>

                            </EventTrigger>
                        </DataTemplate.Triggers>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <VirtualizingStackPanel/>
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.Template>
                    <ControlTemplate TargetType="{x:Type ItemsControl}">
                        <ScrollViewer>
                            <ItemsPresenter/>
                        </ScrollViewer>
                    </ControlTemplate>
                </ItemsControl.Template>
            </ItemsControl>
            <TextBlock x:Name="StackView" Margin="5"/>
        </UniformGrid>
    </Grid>
</Window>



Код: 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.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Data;

namespace WpfApplication33
{

    public class ObservableStack<T> : INotifyCollectionChanged, IEnumerable<T>
    {

        private readonly Stack<T> _stack = new Stack<T>();


        public void FakePush(T item)
        {

            //Реального добавления элемента не осуществляется
            var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item);
            OnCollectionChanged(arg);
        }

        public void Push(T item)
        {
            _stack.Push(item);
            var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item);
            OnCollectionChanged(arg);
        }

        public T Pop()
        {
            var pop = _stack.Pop();
            var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, pop, _stack.Count);
            OnCollectionChanged(arg);
            return pop;
        }

        private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (CollectionChanged != null) CollectionChanged(this, e);
        }

        public T Peek()
        {
            var peek = _stack.Peek();
            return peek;
        }

        public void Clear()
        {
            _stack.Clear();
            var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
            OnCollectionChanged(arg);
        }

        
        public event NotifyCollectionChangedEventHandler CollectionChanged;


        public IEnumerator<T> GetEnumerator()
        {
            return _stack.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

    }

    public class Person
    {
        public string Name { set; get; }
        public int Age { set; get; }
        
        public override string ToString()
        {
            return string.Format("{0}, {1}", Name, Age);
        }

    }

    public partial class MainWindow : Window
    {

        public ObservableStack<Person> Persons { set; get; }
        private ICollectionView _personsView;

        public MainWindow()
        {
            InitializeComponent();
            Persons = new ObservableStack<Person>();
            _personsView = CollectionViewSource.GetDefaultView(Persons);
        }

        private int _count;

        private void PushButton_OnClick(object sender, RoutedEventArgs e)
        {
            Persons.Push(GetNewPerson("Real person"));
        }

        private void PopButton_OnClick(object sender, RoutedEventArgs e)
        {
            Persons.Pop();
        }

        private void ViewButton_OnClick(object sender, RoutedEventArgs e)
        {
            StackView.Text = "View:\r\n" + 
                string.Join("\r\n", _personsView.Cast<Person>()) +
                "\r\nStack:\r\n" +
                string.Join("\r\n", Persons);
        }

        private void FakePushButton_OnClick(object sender, RoutedEventArgs e)
        {
            Persons.FakePush(GetNewPerson("Fake person"));
        }

        private Person GetNewPerson(string name)
        {
            var person = new Person
            {
                Name = name + " " + _count,
                Age = _count
            };
            _count++;
            return person;
        }

        private void ClearButton_OnClick(object sender, RoutedEventArgs e)
        {
            Persons.Clear();
        }
    }
}


что собственно меня смутило.
Если вы посмотрите на метод FakePush() из класса ObservableStack, то там реального добавления элемента в стек нет, если только возбуждение события на добавление элемента к коллекции. При этом в самом UI элемент который мы передали в событие появляется.
Совершенно не важно добавляем мы в модели представления в коллекцию\стек\что угодно какой то объект или не добавляем. Достаточно просто передать его событии CollectionChanged, что мы добавляем этот элемент.
После чего элемент начинает отображаться в ItemsControl, при это виртуализация О_о отлично работает.

Если получить View'у этой коллекции, то она содержит все элементы (реальные и "фейковые") при этом имеет собственный список этих элементов, а не просто перечислитель какой то коллекции...
...
Рейтинг: 0 / 0
Когнитивный диссонанс
    #39040440
Сон Веры Павловны
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Реальный возвращаемый тип ICollectionView _personsView - это MS.Internal.Data.EnumerableCollectionView из PresentationFramework. Этот класс - прямой наследник CollectionView , и в своем конструкторе передает IEnumerable source (т.е. ваш ObservableStack<T>) в конструктор родителя (CollectionView), где source проверяется на предмет реализации INotifyCollectionChanged, при положительном результате проверки (и еще нескольких) на source навешивается NotifyCollectionChangedEventHandler, в обработчике которого вызывается виртуальный метод CollectionView.ProcessCollectionChanged, переопределенный в EnumerableCollectionView, и в этом переопределенном методе как раз заполняется внутренняя копия source типа ObservableCollection<object> ( см. здесь ). Эта копия (завернутая в ListCollectionView) и служит источником данных для всех методов и свойств реализации ICollectionView.
Проверка:
Код: c#
1.
2.
3.
4.
5.
6.
7.
8.
9.
private void FakePushButton_OnClick(object sender, RoutedEventArgs e)
{
  Persons.FakePush(GetNewPerson("Fake person"));
  Console.WriteLine((
    _personsView.GetType()
    .GetField("_snapshot", BindingFlags.NonPublic | BindingFlags.Instance)
    .GetValue(_personsView) as IEnumerable<object>
    ).Count());
}


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


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