powered by simpleCommunicator - 2.0.34     © 2025 Programmizd 02
Форумы / WPF, Silverlight [игнор отключен] [закрыт для гостей] / Как победить баг с Grid.Row/Column ?!
2 сообщений из 2, страница 1 из 1
Как победить баг с Grid.Row/Column ?!
    #39966128
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Создал простое AP-свойство для упрощения привязок из шаблона элемента.
Вместо такой:
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
        <ItemsControl ItemsSource="{Binding Mode=OneWay, Source={StaticResource Points.Grid}}" 
                      ItemsPanel="{StaticResource Grid.Panel}">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="Point">
                    <Ellipse Fill="Coral"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Grid.Row" Value="{Binding Y}"/>
                    <Setter Property="Grid.Column" Value="{Binding X}"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>


Можно применять такую:
Код: xml
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
        <ItemsControl Grid.Row="1"
                      ItemsSource="{Binding Mode=OneWay, Source={StaticResource Points.Grid}}"
                      ItemsPanel="{StaticResource Grid.Panel}">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="Point">
                    <Ellipse Fill="LightBlue"
                             pa:Grid.Row="{Binding Y}"
                             pa:Grid.Column="{Binding X}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>



Само AP-свойство:
Код: 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.
	public static partial class Grid
	{
		public static int GetRow(FrameworkElement element)
		{
			return (int)element.GetValue(RowProperty);
		}

		public static void SetRow(FrameworkElement element, int value)
		{
			element.SetValue(RowProperty, value);
		}

		// Using a DependencyProperty as the backing store for Row.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty RowProperty =
			DependencyProperty.RegisterAttached("Row", typeof(int), typeof(Grid),
				new FrameworkPropertyMetadata
				(
					0,
					FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
					RowChanged
				));

		private static void RowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			if (!(d is FrameworkElement element))
				throw new ArgumentException("Должен быть  FrameworkElement", nameof(d));

			FrameworkElement parent;

			while ((parent = VisualTreeHelper.GetParent(element) as FrameworkElement) != null && !(parent is System.Windows.Controls.Grid))
				element = parent;

			if (parent is System.Windows.Controls.Grid grid)
				element.SetValue(System.Windows.Controls.Grid.RowProperty, (int)e.NewValue);
		}

		private static void GridLoaded(object sender, RoutedEventArgs e)
			=> ((System.Windows.Controls.Grid)sender).InvalidateMeasure();

		public static int GetColumn(FrameworkElement element)
		{
			return (int)element.GetValue(ColumnProperty);
		}

		public static void SetColumn(FrameworkElement element, int value)
		{
			element.SetValue(ColumnProperty, value);
		}

		// Using a DependencyProperty as the backing store for Column.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty ColumnProperty =
			DependencyProperty.RegisterAttached("Column", typeof(int), typeof(Grid),
				new FrameworkPropertyMetadata
				(
					0,
					FrameworkPropertyMetadataOptions.AffectsParentArrange | FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.BindsTwoWayByDefault,
					ColumnChanged
				));

		private static void ColumnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			if (!(d is FrameworkElement element))
				throw new ArgumentException("Должен быть  FrameworkElement", nameof(d));

			FrameworkElement parent;

			while ((parent = VisualTreeHelper.GetParent(element) as FrameworkElement) != null && !(parent is System.Windows.Controls.Grid))
				element = parent;

			if (parent is System.Windows.Controls.Grid grid)
				element.SetValue(System.Windows.Controls.Grid.ColumnProperty, (int)e.NewValue);
		}
	}



В свойстве ничего сложного.
С Canvas аналогичное работает "на Ура".
А с Grid возникают проблемы при присоединении коллекции с элементами или при добавлении первого элемента в коллекцию - элементы выводятся в Grid без учёта их позиции.
Хотя при просмотре в визуальном дереве и обозревателе свойств присоединённые свойства Grid.Row/Column заданы правильны.
И при малейших изменениях окна элементы встают по своим местам.

На мой взгляд, откровенный баг.
Но как с ним бороться?

Полный демо-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.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
<Window x:Class="AttachedPropertiesWPF.BindParentWind"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:AttachedPropertiesWPF"
        mc:Ignorable="d"
        Title="BindParentWind" Height="450" Width="800"
        xmlns:pa="clr-namespace:AttachedProperties;assembly=AttachedProperties">
    <Window.Resources>
        <x:Array x:Key="Points.Grid" Type="Point">
            <Point X="1" Y="0"/>
            <Point X="0" Y="2"/>
            <Point X="2" Y="1"/>
        </x:Array>
        <ItemsPanelTemplate x:Key="Grid.Panel">
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition/>
                    <RowDefinition/>
                    <RowDefinition/>
                </Grid.RowDefinitions>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                    <ColumnDefinition/>
                </Grid.ColumnDefinitions>
            </Grid>
        </ItemsPanelTemplate>
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <ItemsControl ItemsSource="{Binding Mode=OneWay, Source={StaticResource Points.Grid}}" 
                      ItemsPanel="{StaticResource Grid.Panel}">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="Point">
                    <Ellipse Fill="Coral"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
            <ItemsControl.ItemContainerStyle>
                <Style>
                    <Setter Property="Grid.Row" Value="{Binding Y}"/>
                    <Setter Property="Grid.Column" Value="{Binding X}"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
        <ItemsControl Grid.Row="1"
                      ItemsSource="{Binding Mode=OneWay, Source={StaticResource Points.Grid}}"
                      ItemsPanel="{StaticResource Grid.Panel}">
            <ItemsControl.ItemTemplate>
                <DataTemplate DataType="Point">
                    <Ellipse Fill="LightBlue"
                             pa:Grid.Row="{Binding Y}"
                             pa:Grid.Column="{Binding X}"/>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Window>



[youtube=
YouTube Video
...
Рейтинг: 0 / 0
Как победить баг с Grid.Row/Column ?!
    #39967614
Eld Hasp
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
После долгих поисков, причина оказалась в особенностях работы ItemsControl и Grid.
Элементы коллекции из источника ItemsControl создаются в одной задаче.
И им изначально задаётся позиция из стиля элемента.
Если в этой же задаче изменять позицию, то Grid в логике своих AP свойств не отрабатывает это новое значение.

Почему так сделано - надо слишком глубоко лезть в реализацию Grid.
Пока такой необходимости у меня нет.
Возможно (наиболее вероятно), это связанно с тем, что позиция элемента может влиять на размеры строки и колонки.
И обработка позиции происходит в два прохода.
Сначала измерение размеров элемента и установка размеров строк и колонок.
А потом выделение контейнеров ячеек и установка в них элементов.

Найденное мною решение (в том числе из обсуждения https://stackoverflow.com/questions/62202419/how-to-defeat-a-bug-with-grid-row-column-in-itemspaneltemplate) - это асинхронная установка значений через обращение к Dispatcher.BeginInvoke().

Код: 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.
    public static partial class Grid
    {
        public static int GetRow(FrameworkElement element)
        {
            return (int)element.GetValue(RowProperty);
        }
 
        public static void SetRow(FrameworkElement element, int value)
        {
            element.SetValue(RowProperty, value);
        }
 
        // Using a DependencyProperty as the backing store for Row.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RowProperty =
            DependencyProperty.RegisterAttached("Row", typeof(int), typeof(Grid),
                new PropertyMetadata(0, RowChanged));
 
        private static void RowChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is FrameworkElement element))
                throw new ArgumentException("Должен быть  FrameworkElement", nameof(d));
 
            FrameworkElement parent;
 
            while ((parent = VisualTreeHelper.GetParent(element) as FrameworkElement) != null && !(parent is System.Windows.Controls.Grid))
                element = parent;
 
            if (parent is System.Windows.Controls.Grid grid)
                element.Dispatcher.BeginInvoke((Action<FrameworkElement, DependencyProperty, object>)SetValueAsync, element, System.Windows.Controls.Grid.RowProperty, (int)e.NewValue);
        }
 
        private static void SetValueAsync(FrameworkElement element, DependencyProperty property, object value)
            => element.SetValue(property, value);
 
        public static int GetColumn(FrameworkElement element)
        {
            return (int)element.GetValue(ColumnProperty);
        }
 
        public static void SetColumn(FrameworkElement element, int value)
        {
            element.SetValue(ColumnProperty, value);
        }
 
        // Using a DependencyProperty as the backing store for Column.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ColumnProperty =
            DependencyProperty.RegisterAttached("Column", typeof(int), typeof(Grid),
                new PropertyMetadata(0, ColumnChanged));
 
        private static void ColumnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is FrameworkElement element))
                throw new ArgumentException("Должен быть  FrameworkElement", nameof(d));
 
            FrameworkElement parent;
 
            while ((parent = VisualTreeHelper.GetParent(element) as FrameworkElement) != null && !(parent is System.Windows.Controls.Grid))
                element = parent;
 
            if (parent is System.Windows.Controls.Grid grid)
                element.Dispatcher.BeginInvoke((Action<FrameworkElement, DependencyProperty, object>)SetValueAsync, element, System.Windows.Controls.Grid.ColumnProperty, (int)e.NewValue);
        }
    }



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


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