如何在 xaml 中制作类似网格的列表框
How to make a grid-like listbox in xaml
我如何制作一个类似马赛克的列表框,可以像其他任何列表框一样输入数据,并且几乎可以适应任何屏幕分辨率?
我花了一段时间尝试为我的 Popcorn 时间移植到 Windows phone 8.1 / RT 构建一个类似马赛克的列表框,它可以简单地适应任何屏幕尺寸。在我解释我是怎么做到的之前,这里有一张截图:
可能有更简洁的方法,但这非常有效:
我从罗伊云的自定义面板开始 (https://social.msdn.microsoft.com/Forums/en-US/20691ca0-eae6-4c4e-b27e-3d5d37c83689/multicolumn-listbox?forum=silverlightnet)
编辑后,这是响应式设计版本:
public class MultiColumnPanel : Panel
{
public static readonly DependencyProperty ColumnWidthProperty = DependencyProperty.Register("ColumnWidth", typeof(double), typeof(MultiColumnPanel), new PropertyMetadata(0d));
public double ColumnWidth
{
get { return (double)this.GetValue(ColumnWidthProperty); }
set { this.SetValue(ColumnWidthProperty, value); }
}
protected override Size MeasureOverride(Size availableSize)
{
Size size = new Size();
Size finalSize = availableSize;
finalSize.Height = double.PositiveInfinity;
int i = 0;
while (i < this.Children.Count)
{
double tempHeight = 0d; for (int j = 0; j < (Convert.ToInt32(Window.Current.Bounds.Width / 160)); j++)
{
if ((i + j) >= this.Children.Count)
{
break;
}
UIElement element = this.Children[i + j];
if (element != null)
{
element.Measure(availableSize);
tempHeight = Math.Max(tempHeight, element.DesiredSize.Height);
}
}
size.Height += tempHeight;
i += (Convert.ToInt32(Window.Current.Bounds.Width / 160)); ;
}
size.Width = this.ColumnWidth * (Convert.ToInt32(Window.Current.Bounds.Width / 160)); return size;
}
protected override Size ArrangeOverride(Size finalSize)
{
UIElementCollection children = this.Children;
double heightDelta = 0d;
int i = 0;
while (i < this.Children.Count)
{
double tempHeight = 0d; for (int j = 0; j < (Convert.ToInt32(Window.Current.Bounds.Width / 160)); j++)
{
if ((i + j) >= this.Children.Count)
{
break;
}
UIElement element = children[i + j];
if (element != null)
{
Rect finalRect = new Rect(new Point(), finalSize); finalRect.X = j * this.ColumnWidth;
finalRect.Y += heightDelta;
tempHeight = Math.Max(tempHeight, element.DesiredSize.Height);
finalRect.Height = element.DesiredSize.Height;
finalRect.Width = Math.Max(this.ColumnWidth, element.DesiredSize.Width);
element.Arrange(finalRect);
}
}
heightDelta += tempHeight;
i += (Convert.ToInt32(Window.Current.Bounds.Width / 160));
}
return finalSize;
}
}
我将它除以 160,因为我想要在 480*800 屏幕上显示 3 列,但这完全取决于您。
现在这里是 xaml:
<ScrollViewer x:Name="scrollviewer" ViewChanged="OnScrollViewerViewChanged" Margin="0">
<Viewbox Margin="0,0,0,0">
<ListBox Name="lstView" ScrollViewer.VerticalScrollMode="Disabled" Background="Transparent" BorderThickness="0" Foreground="White" Tapped="lstView_Tapped" SelectionChanged="lstView_SelectionChanged" DataContextChanged="myscrollviewer_ViewChanged" ItemContainerStyle="{StaticResource ListBoxItemStyle1}" ScrollViewer.VerticalScrollBarVisibility="Visible" Margin="0">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<local:MultiColumnPanel ColumnWidth="132"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="ItemGrid" Width="132" Background="#00000000" Margin="0,0,0,0" Grid.ColumnSpan="2" Height="210" Tapped="ItemGrid_Tapped" HorizontalAlignment="Stretch">
<Grid.RenderTransform>
<TranslateTransform x:Name="ListBoxItemMove" />
</Grid.RenderTransform>
<Grid.Resources>
<EventTrigger x:Name="event" RoutedEvent="Grid.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard x:Name="listboxIN">
<DoubleAnimation Duration="00:00:01" Storyboard.TargetProperty="X" From="-100" To="0" Storyboard.TargetName="ListBoxItemMove">
<DoubleAnimation.EasingFunction>
<CircleEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Duration="00:00:01" From="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ItemGrid">
<DoubleAnimation.EasingFunction>
<CircleEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Grid.Resources>
<TextBlock Text="{Binding id}" Visibility="Collapsed"/>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Bottom" Margin="5,0,0,14" FontFamily="Segoe WP" FontSize="16" FontWeight="Thin" TextTrimming="CharacterEllipsis"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Year}" VerticalAlignment="Bottom" FontWeight="Black" FontFamily="Segoe WP" Opacity="0.25" Margin="5,0,0,0"/>
<Image Grid.Row="0" Source="{Binding ImagePath}" Width="112" Height="173" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5,0,0,0"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
动画不是必需的,但它使应用程序看起来更加精美。将列表框放在滚动查看器的视图框中非常重要。这是因为视图框需要适应不修改列数的小分辨率变化,如果您只是将列表框放在视图框中,它的高度将压扁整个控件,这样就不需要滚动.所以我禁用了列表框的内部滚动查看器,并在视图框上包裹了一个。
我希望这对没有经验的程序员(比如我)有所帮助,并且不会让你花费数小时研究我需要的一切来实现这个相对简单的控制。
我如何制作一个类似马赛克的列表框,可以像其他任何列表框一样输入数据,并且几乎可以适应任何屏幕分辨率?
我花了一段时间尝试为我的 Popcorn 时间移植到 Windows phone 8.1 / RT 构建一个类似马赛克的列表框,它可以简单地适应任何屏幕尺寸。在我解释我是怎么做到的之前,这里有一张截图:
可能有更简洁的方法,但这非常有效: 我从罗伊云的自定义面板开始 (https://social.msdn.microsoft.com/Forums/en-US/20691ca0-eae6-4c4e-b27e-3d5d37c83689/multicolumn-listbox?forum=silverlightnet) 编辑后,这是响应式设计版本:
public class MultiColumnPanel : Panel
{
public static readonly DependencyProperty ColumnWidthProperty = DependencyProperty.Register("ColumnWidth", typeof(double), typeof(MultiColumnPanel), new PropertyMetadata(0d));
public double ColumnWidth
{
get { return (double)this.GetValue(ColumnWidthProperty); }
set { this.SetValue(ColumnWidthProperty, value); }
}
protected override Size MeasureOverride(Size availableSize)
{
Size size = new Size();
Size finalSize = availableSize;
finalSize.Height = double.PositiveInfinity;
int i = 0;
while (i < this.Children.Count)
{
double tempHeight = 0d; for (int j = 0; j < (Convert.ToInt32(Window.Current.Bounds.Width / 160)); j++)
{
if ((i + j) >= this.Children.Count)
{
break;
}
UIElement element = this.Children[i + j];
if (element != null)
{
element.Measure(availableSize);
tempHeight = Math.Max(tempHeight, element.DesiredSize.Height);
}
}
size.Height += tempHeight;
i += (Convert.ToInt32(Window.Current.Bounds.Width / 160)); ;
}
size.Width = this.ColumnWidth * (Convert.ToInt32(Window.Current.Bounds.Width / 160)); return size;
}
protected override Size ArrangeOverride(Size finalSize)
{
UIElementCollection children = this.Children;
double heightDelta = 0d;
int i = 0;
while (i < this.Children.Count)
{
double tempHeight = 0d; for (int j = 0; j < (Convert.ToInt32(Window.Current.Bounds.Width / 160)); j++)
{
if ((i + j) >= this.Children.Count)
{
break;
}
UIElement element = children[i + j];
if (element != null)
{
Rect finalRect = new Rect(new Point(), finalSize); finalRect.X = j * this.ColumnWidth;
finalRect.Y += heightDelta;
tempHeight = Math.Max(tempHeight, element.DesiredSize.Height);
finalRect.Height = element.DesiredSize.Height;
finalRect.Width = Math.Max(this.ColumnWidth, element.DesiredSize.Width);
element.Arrange(finalRect);
}
}
heightDelta += tempHeight;
i += (Convert.ToInt32(Window.Current.Bounds.Width / 160));
}
return finalSize;
}
}
我将它除以 160,因为我想要在 480*800 屏幕上显示 3 列,但这完全取决于您。 现在这里是 xaml:
<ScrollViewer x:Name="scrollviewer" ViewChanged="OnScrollViewerViewChanged" Margin="0">
<Viewbox Margin="0,0,0,0">
<ListBox Name="lstView" ScrollViewer.VerticalScrollMode="Disabled" Background="Transparent" BorderThickness="0" Foreground="White" Tapped="lstView_Tapped" SelectionChanged="lstView_SelectionChanged" DataContextChanged="myscrollviewer_ViewChanged" ItemContainerStyle="{StaticResource ListBoxItemStyle1}" ScrollViewer.VerticalScrollBarVisibility="Visible" Margin="0">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<local:MultiColumnPanel ColumnWidth="132"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid x:Name="ItemGrid" Width="132" Background="#00000000" Margin="0,0,0,0" Grid.ColumnSpan="2" Height="210" Tapped="ItemGrid_Tapped" HorizontalAlignment="Stretch">
<Grid.RenderTransform>
<TranslateTransform x:Name="ListBoxItemMove" />
</Grid.RenderTransform>
<Grid.Resources>
<EventTrigger x:Name="event" RoutedEvent="Grid.Loaded">
<EventTrigger.Actions>
<BeginStoryboard>
<Storyboard x:Name="listboxIN">
<DoubleAnimation Duration="00:00:01" Storyboard.TargetProperty="X" From="-100" To="0" Storyboard.TargetName="ListBoxItemMove">
<DoubleAnimation.EasingFunction>
<CircleEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Duration="00:00:01" From="0" To="1" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ItemGrid">
<DoubleAnimation.EasingFunction>
<CircleEase EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
</Storyboard>
</BeginStoryboard>
</EventTrigger.Actions>
</EventTrigger>
</Grid.Resources>
<TextBlock Text="{Binding id}" Visibility="Collapsed"/>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Name}" VerticalAlignment="Bottom" Margin="5,0,0,14" FontFamily="Segoe WP" FontSize="16" FontWeight="Thin" TextTrimming="CharacterEllipsis"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Year}" VerticalAlignment="Bottom" FontWeight="Black" FontFamily="Segoe WP" Opacity="0.25" Margin="5,0,0,0"/>
<Image Grid.Row="0" Source="{Binding ImagePath}" Width="112" Height="173" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5,0,0,0"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
动画不是必需的,但它使应用程序看起来更加精美。将列表框放在滚动查看器的视图框中非常重要。这是因为视图框需要适应不修改列数的小分辨率变化,如果您只是将列表框放在视图框中,它的高度将压扁整个控件,这样就不需要滚动.所以我禁用了列表框的内部滚动查看器,并在视图框上包裹了一个。
我希望这对没有经验的程序员(比如我)有所帮助,并且不会让你花费数小时研究我需要的一切来实现这个相对简单的控制。