Skip to content

Instantly share code, notes, and snippets.

@WamWooWam
Last active May 18, 2021 16:04
Show Gist options
  • Save WamWooWam/6b19b8355e3e3a8d605ac015c67f6093 to your computer and use it in GitHub Desktop.
Save WamWooWam/6b19b8355e3e3a8d605ac015c67f6093 to your computer and use it in GitHub Desktop.
A set of attached properties to stretch GridViewItem/ListViewItems to fill their parent fully.
using System;
using Windows.UI.Xaml; // or Microsoft.UI.Xaml
using Windows.UI.Xaml.Controls; // or Microsoft.UI.Xaml.Controls
namespace Unicord.Universal.Behaviours
{
public class ListViewBehaviour
{
/// <summary>
/// This property specifies the minium width for child items in an items wrap grid panel.
/// Setting this property o an non zero value will enable dynamic sizing of items so that
/// when items are wrapped the items control is always filled out horizontally
/// i.e. the width of items are increased to fill the empty space.
/// </summary>
public static readonly DependencyProperty MinItemWidthProperty =
DependencyProperty.RegisterAttached("MinItemWidth", typeof(double),
typeof(ListViewBehaviour), new PropertyMetadata(0.0d, OnMinItemWidthChanged));
/// <summary>
/// Only applicable when MinItemWidth is non zero. Typically the logic behind
/// MinItemWidth will only trigger if the number of items is more than or equal to
/// what a single row will accomodate. This property specifies that the layout logic
/// is also performed when there are less items than what a single row will accomodate.
/// </summary>
public static readonly DependencyProperty FillBeforeWrapProperty =
DependencyProperty.RegisterAttached("FillBeforeWrap", typeof(bool),
typeof(ListViewBehaviour), new PropertyMetadata(false));
/// <summary>
/// Returns the value of the FillBeforeWrap
/// </summary>
/// <param name="obj">The dependency-object whichs value should be returned</param>
/// <returns>The value of the property</returns>
public static bool GetFillBeforeWrap(DependencyObject obj)
{
return (bool)obj.GetValue(FillBeforeWrapProperty);
}
/// <summary>
/// Sets the value of the FillBeforeWrap
/// </summary>
/// <param name="obj">The dependency-object whichs value should be set</param>
/// <param name="value">The value which should be assigned to the property.</param>
public static void SetFillBeforeWrap(DependencyObject obj, bool value)
{
obj.SetValue(FillBeforeWrapProperty, value);
}
/// <summary>
/// Returns the value of the MinItemWidthProperty
/// </summary>
/// <param name="obj">The dependency-object whichs value should be returned</param>
/// <returns>The value of the property</returns>
public static double GetMinItemWidth(DependencyObject obj)
{
return (double)obj.GetValue(MinItemWidthProperty);
}
/// <summary>
/// Sets the value of the MinItemWidthProperty
/// </summary>
/// <param name="obj">The dependency-object whichs value should be set</param>
/// <param name="value">The value which should be assigned to the property.</param>
public static void SetMinItemWidth(DependencyObject obj, double value)
{
obj.SetValue(MinItemWidthProperty, value);
}
private static void OnMinItemWidthChanged(DependencyObject s, DependencyPropertyChangedEventArgs e)
{
if (s is ListViewBase f)
{
f.SizeChanged -= OnListViewSizeChanged;
if (((double)e.NewValue) > 0)
{
f.SizeChanged += OnListViewSizeChanged;
}
}
}
private static async void OnListViewSizeChanged(object sender, SizeChangedEventArgs e)
{
// using OwlCore;
// if (!await Flow.Debounce("ListViewBehaviour_OnListViewSizeChanged", TimeSpan.FromMilliseconds(50)))
// return;
// Unbox the sender.
var itemsControl = sender as ListViewBase;
// If the items panel is a wrap grid.
if (itemsControl.ItemsPanelRoot is ItemsWrapGrid itemsPanel)
{
// Get total size
var total = e.NewSize.Width - (itemsPanel.Margin.Left + itemsPanel.Margin.Right + itemsControl.Padding.Left + itemsControl.Padding.Right);
// Minimum item size.
var itemMinSize = Math.Min(total, (double)itemsControl.GetValue(MinItemWidthProperty));
// How many items can be fit whole.
var canBeFit = Math.Floor(total / itemMinSize);
// I could add logic that if the total items are less then the number of items that
// would fit then devide the total size by the number of items rather than the number
// of items that would actually fit.
if ((bool)itemsControl.GetValue(FillBeforeWrapProperty) &&
itemsControl.Items.Count > 0 &&
itemsControl.Items.Count < canBeFit)
{
canBeFit = itemsControl.Items.Count;
}
// Set the items Panel item width appropriately.
// Note you will need your container to stretch
// along with the items panel or it will look
// strange.
// <GridView.ItemContainerStyle>
// <Style TargetType="GridViewItem">
// <Setter Property="HorizontalContentAlignment" Value="Stretch" />
// <Setter Property="HorizontalAlignment" Value="Stretch" />
// </Style>
// </GridView.ItemContainerStyle>
itemsPanel.ItemWidth = total / canBeFit;
}
}
}
}
@WamWooWam
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment