Skip to content

Commit

Permalink
fix(combobox)!: Make ComboBox inherit from Selector
Browse files Browse the repository at this point in the history
BREAKING CHANGE: ComboBox now inherits from Selector rather than ListViewBase, to match the UWP/WinUI inheritance hierarchy.
  • Loading branch information
davidjohnoliver committed Oct 19, 2021
1 parent 08a0c62 commit 63f35c3
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 46 deletions.
55 changes: 43 additions & 12 deletions src/Uno.UI/UI/Xaml/Controls/ComboBox/ComboBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@

namespace Windows.UI.Xaml.Controls
{
// Temporarily inheriting from ListViewBase instead of Selector to leverage existing selection and virtualization code
public partial class ComboBox : ListViewBase // TODO: Selector
public partial class ComboBox : Selector
{
public event EventHandler<object>? DropDownClosed;
public event EventHandler<object>? DropDownOpened;
Expand All @@ -62,7 +61,6 @@ public ComboBox()
{
ResourceResolver.ApplyResource(this, LightDismissOverlayBackgroundProperty, "ComboBoxLightDismissOverlayBackground", isThemeResourceExtension: true);

IsItemClickEnabled = true;
DefaultStyleKey = typeof(ComboBox);
}

Expand Down Expand Up @@ -188,15 +186,51 @@ private void OnPopupClosed(object? sender, object e)
IsDropDownOpen = false;
}

protected override void OnHeaderChanged(object oldHeader, object newHeader)

public object Header
{
get { return (object)this.GetValue(HeaderProperty); }
set { this.SetValue(HeaderProperty, value); }
}

public static DependencyProperty HeaderProperty { get; } =
DependencyProperty.Register(
"Header",
typeof(object),
typeof(ComboBox),
new FrameworkPropertyMetadata(
defaultValue: null,
options: FrameworkPropertyMetadataOptions.None,
propertyChangedCallback: (s, e) => ((ComboBox)s)?.OnHeaderChanged((object)e.OldValue, (object)e.NewValue)
)
);

private void OnHeaderChanged(object oldHeader, object newHeader)
{
base.OnHeaderChanged(oldHeader, newHeader);
UpdateHeaderVisibility();
}

protected override void OnHeaderTemplateChanged(DataTemplate oldHeaderTemplate, DataTemplate newHeaderTemplate)

public DataTemplate HeaderTemplate
{
get { return (DataTemplate)this.GetValue(HeaderTemplateProperty); }
set { this.SetValue(HeaderTemplateProperty, value); }
}

public static DependencyProperty HeaderTemplateProperty { get; } =
DependencyProperty.Register(
"HeaderTemplate",
typeof(DataTemplate),
typeof(ComboBox),
new FrameworkPropertyMetadata(
defaultValue: (DataTemplate?)null,
options: FrameworkPropertyMetadataOptions.ValueDoesNotInheritDataContext,
propertyChangedCallback: (s, e) => ((ComboBox)s)?.OnHeaderTemplateChanged((DataTemplate)e.OldValue, (DataTemplate)e.NewValue)
)
);

private void OnHeaderTemplateChanged(DataTemplate oldHeaderTemplate, DataTemplate newHeaderTemplate)
{
base.OnHeaderTemplateChanged(oldHeaderTemplate, newHeaderTemplate);
UpdateHeaderVisibility();
}

Expand Down Expand Up @@ -384,11 +418,6 @@ partial void OnIsDropDownOpenChangedPartial(bool oldIsDropDownOpen, bool newIsDr
OnDropDownOpened(args);

RestoreSelectedItem();

if (SelectedItem != null)
{
ScrollIntoView(SelectedItem);
}
}
else
{
Expand Down Expand Up @@ -560,8 +589,10 @@ public Size Measure(Size available, Size visibleSize)
// since the layouting on those platforms is not yet as aligned with UWP as on WASM/Skia, and in particular
// virtualizing panels aren't used in the ComboBox yet (#556 and #1133), we skip it for now
{
#pragma warning disable CS0162 // Unreachable code detected
child.HorizontalAlignment = HorizontalAlignment.Left;
child.VerticalAlignment = VerticalAlignment.Top;
#pragma warning restore CS0162 // Unreachable code detected
}
}

Expand Down
12 changes: 2 additions & 10 deletions src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,8 @@ partial void OnSelectionModeChangedPartial(ListViewSelectionMode oldSelectionMod

internal override void OnItemClicked(int clickedIndex)
{
// Note: don't call base.OnItemClicked(), because we override the default single-selection-only handling

var item = ItemFromIndex(clickedIndex);
if (IsItemClickEnabled)
{
Expand Down Expand Up @@ -849,15 +851,5 @@ private async Task LoadMoreItemsAsync(object incrementalSource, uint count)
TryLoadMoreItems();
}
}

/// <summary>
/// Is the ListView.managed implementation used on this platform?
/// </summary>
internal static bool UsesManagedLayouting =>
#if __IOS__ || __ANDROID__
false;
#else
true;
#endif
}
}
14 changes: 0 additions & 14 deletions src/Uno.UI/UI/Xaml/Controls/ListViewBase/ListViewBase.managed.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,6 @@ public partial class ListViewBase
{
private int PageSize => throw new NotImplementedException();

private protected override bool ShouldItemsControlManageChildren => !(ItemsPanelRoot is IVirtualizingPanel);

private protected override void Refresh()
{
base.Refresh();

if (VirtualizingPanel != null)
{
VirtualizingPanel.GetLayouter().Refresh();

InvalidateMeasure();
}
}

private void AddItems(int firstItem, int count, int section)
{
if (VirtualizingPanel != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,20 @@ internal SnapPointsType SnapPointsType
{
get
{
if (XamlParent == null)
var parentList = XamlParent as ListViewBase;
if (parentList == null)
{
return default;
}

if (ScrollOrientation == Orientation.Vertical)
{
return XamlParent.ScrollViewer.VerticalSnapPointsType;
return parentList.ScrollViewer.VerticalSnapPointsType;
}

else
{
return XamlParent.ScrollViewer.HorizontalSnapPointsType;
return parentList.ScrollViewer.HorizontalSnapPointsType;
}
}
}
Expand All @@ -104,19 +105,20 @@ internal SnapPointsAlignment SnapPointsAlignment
{
get
{
if (XamlParent == null)
var parentList = XamlParent as ListViewBase;
if (parentList == null)
{
return default;
}

if (ScrollOrientation == Orientation.Vertical)
{
return XamlParent.ScrollViewer.VerticalSnapPointsAlignment;
return parentList.ScrollViewer.VerticalSnapPointsAlignment;
}

else
{
return XamlParent.ScrollViewer.HorizontalSnapPointsAlignment;
return parentList.ScrollViewer.HorizontalSnapPointsAlignment;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ private protected VirtualizingPanelGenerator Generator

private ScrollViewer? ScrollViewer { get; set; }
internal ItemsControl? ItemsControl { get; set; }
public ListViewBase? XamlParent => ItemsControl as ListViewBase;
public ItemsControl? XamlParent => ItemsControl;

/// <summary>
/// Ordered record of all currently-materialized lines.
Expand Down Expand Up @@ -749,7 +749,7 @@ private void ScrapLayout()
/// </summary>
protected virtual Uno.UI.IndexPath? GetDynamicSeedIndex(Uno.UI.IndexPath? firstVisibleItem)
{
var lastItem = XamlParent?.GetLastItem();
var lastItem = ItemsControl?.GetLastItem();
if (lastItem == null ||
(firstVisibleItem != null && firstVisibleItem.Value > lastItem.Value)
)
Expand Down Expand Up @@ -1041,7 +1041,7 @@ protected bool ShouldInsertReorderingView(double extentOffset)
{
if (reorder.index is null)
{
reorder.index = XamlParent!.GetIndexPathFromItem(reorder.item);
reorder.index = ItemsControl!.GetIndexPathFromItem(reorder.item);
_pendingReorder = reorder; // _pendingReorder is a struct!
}

Expand Down
28 changes: 27 additions & 1 deletion src/Uno.UI/UI/Xaml/Controls/Primitives/Selector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,19 @@ internal override void OnItemsSourceGroupsChanged(object sender, NotifyCollectio

internal void OnItemClicked(SelectorItem selectorItem) => OnItemClicked(IndexFromContainer(selectorItem));

internal virtual void OnItemClicked(int clickedIndex) { }
internal virtual void OnItemClicked(int clickedIndex)
{
if (ItemsSource is ICollectionView collectionView)
{
//NOTE: Windows seems to call MoveCurrentTo(item); we set position instead to have expected behavior when you have duplicate items in the list.
collectionView.MoveCurrentToPosition(clickedIndex);

// The CollectionView may have intercepted the change
clickedIndex = collectionView.CurrentPosition;
}

SelectedIndex = clickedIndex;
}

protected override void OnItemsChanged(object e)
{
Expand Down Expand Up @@ -611,6 +623,7 @@ private protected override void Refresh()
{
base.Refresh();
_itemTemplatesThatArentContainers.Clear();
RefreshPartial();
}


Expand Down Expand Up @@ -776,5 +789,18 @@ bool CanScrollIntoView()

return !isItemsHostInvalid && isInLiveTree && !m_skipScrollIntoView && !m_inCollectionChange;
}

partial void RefreshPartial();


/// <summary>
/// Is the ListView.managed implementation used on this platform?
/// </summary>
internal const bool UsesManagedLayouting =
#if __IOS__ || __ANDROID__
false;
#else
true;
#endif
}
}
37 changes: 37 additions & 0 deletions src/Uno.UI/UI/Xaml/Controls/Primitives/Selector.managed.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#if UNO_REFERENCE_API || __MACOS__
using Uno.UI.Controls;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Uno.Extensions;
using System.Collections.Specialized;
using Uno.Extensions.Specialized;
using System.Diagnostics;
using Uno.UI;
using Uno.Disposables;
using Windows.UI.Xaml.Data;
using Uno.UI.DataBinding;
using Windows.Foundation.Collections;

namespace Windows.UI.Xaml.Controls.Primitives
{
partial class Selector
{
private protected IVirtualizingPanel VirtualizingPanel => ItemsPanelRoot as IVirtualizingPanel;

private protected override bool ShouldItemsControlManageChildren => !(ItemsPanelRoot is IVirtualizingPanel);

partial void RefreshPartial()
{
if (VirtualizingPanel != null)
{
VirtualizingPanel.GetLayouter().Refresh();

InvalidateMeasure();
}
}
}
}
#endif

0 comments on commit 63f35c3

Please sign in to comment.