WPF - реализация IScrollInfo
Проблема: Окно, которое имеет ItemsControl с некоторыми элементами (скажем, Прямоугольники). В окне установлены MinWidth и MinHeight (например, 300). Мне нужно это, когда я изменяю размер окна, если у прямоугольников недостаточно места для отображения в 2 столбцах. И если в 2 столбцах до сих пор не хватает места для показа просмотра прокрутки.
Что я пробовал: 1. Создать расширенный ItemsControl:
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<local:MyGrid IsItemsHost="True" x:Name="PART_ItemsPanel" Initialized="OnItemsPanelInitialized" CanVerticallyScroll="True" CanHorizontallyScroll="True">
<local:MyGrid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</local:MyGrid.ColumnDefinitions>
<local:MyGrid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</local:MyGrid.RowDefinitions>
</local:MyGrid>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
Мой ItemsControl имеет в качестве ItemsPanelTemplate расширенную сетку:
открытый класс MyGrid: Grid, IScrollInfo { .... реализация IScrollInfo}
Я использую эту сетку, думая, что когда ItemsControl будет PrepareContainerForItemOverride(), я могу использовать это, чтобы разделить элементы в два столбца.
Идея "взята" из конференции.... но я не знаю, что делать дальше... У меня есть вопросы: когда я переопределяю Measure и Arrange для сетки данных, я устанавливаю положение элементов в DataGrid, но тогда это называется PrepareContainerForItemOverride()... тогда что? Я должен вычислить количество строк, которые я должен сделать? но если тогда я снова изменю размер окна...PrepareContainerForItemOverride() не будет вызываться...
Это проблема со мной... пожалуйста, дайте мне подсказку, если у некоторых из вас есть такая. Спасибо вам, ребята!
2 ответа
Я хотел поделиться решением, которое я принял... если кому-то это нужно.
Чтобы избежать реализации IScrollInfo, я изменил ItemsControl с элементом управления ListBox. Так что остается, что я переопределяю Arrange. В Arrange я вычисляю высоту элементов моего первого столбца и располагаю элементы в GridPanel. Я использую диспетчер, потому что расположение scrollViewer не всегда VALID, и я рассчитываю на него, чтобы получить видимую высоту.
<ListBox x:Uid="allListBox" x:Name="adminListBoxControl" ItemTemplate ="{DynamicResource LinkButtonItemTemplate}" Margin="15" BorderBrush="Transparent" Background="Transparent">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<local:GridPanel>
<local:GridPanel.ColumnDefinitions>
<ColumnDefinition Width="0.45*"/>
<ColumnDefinition Width="0.45*"/>
<ColumnDefinition Width="0.1*"/>
</local:GridPanel.ColumnDefinitions>
<local:GridPanel.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</local:GridPanel.RowDefinitions>
</local:GridPanel>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
protected override Size ArrangeOverride(Size finalSize)
{
Size arrangeSize = base.ArrangeOverride(finalSize);
lastItemInFirstColumn = -1;
double totalHeight = 0;
DispatcherPriority p = ScrollViewer.IsArrangeValid ? DispatcherPriority.Normal : DispatcherPriority.Input;
if (o != null)
o.Abort();
o = Dispatcher.BeginInvoke(p, (Action)(() =>
{
double fitHeight = ScrollViewer.ActualHeight;
foreach (UIElement child in this.InternalChildren)
{
if (totalHeight + child.DesiredSize.Height < fitHeight)
{
totalHeight += child.DesiredSize.Height;
lastItemInFirstColumn++;
}
else
{
if (lastItemInFirstColumn + 1 < InternalChildren.Count - (lastItemInFirstColumn + 1))
lastItemInFirstColumn++;
break;
}
}
//set items positions
ArrangeItemsInGrid();
}));
o.Completed += (s, e) => { o = null; };
return arrangeSize;
}
Мне кажется, что все, что вам нужно, это другая панель в вашем ItemsControl
, Может случиться так, что вам сойдет с рук использование встроенного WrapPanel
, но вы также можете написать свой собственный:
<ItemsControl ...>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<!-- put whatever panel that encapsulates your layout logic here, possibly your own -->
<WrapPanel/>
</ItemsPanelTemplate>
<ItemsControl.ItemsPanel>
</ItemsControl>