Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
[UWP] CollectionView SelectionMode (#6629) fixes #3172 fixes #6489 fixes
Browse files Browse the repository at this point in the history
 #5844 fixes #5625

* UWP CollectionView SelectionMode

* Null check

* SelectItems get property

* ItemTemplatePair type check

* Avoid mutually trigger OnSelectionChange

* Extended -> Multiple + Avoid creating new SelectedItems
  • Loading branch information
humhei authored and rmarinho committed Jun 26, 2019
1 parent 3782a5d commit 3ac5b51
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 20 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,4 @@ tools/
!tools/mdoc/**/*
caketools/
*.binlog
.ionide/**
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Xamarin.Forms.Internals;
using Xamarin.Forms.Platform.UAP;

namespace Xamarin.Forms.Platform.UWP
{
public class CollectionViewRenderer : SelectableItemsViewRenderer
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@

namespace Xamarin.Forms.Platform.UWP
{
public class CollectionViewRenderer : ViewRenderer<CollectionView, ItemsControl>
public class ItemsViewRenderer : ViewRenderer<CollectionView, ListViewBase>
{
IItemsLayout _layout;
CollectionViewSource _collectionViewSource;

protected ItemsControl ItemsControl { get; private set; }
protected ListViewBase ListViewBase { get; private set; }

protected override void OnElementChanged(ElementChangedEventArgs<CollectionView> args)
{
Expand All @@ -42,7 +42,7 @@ protected override void OnElementPropertyChanged(object sender, PropertyChangedE
}
}

protected virtual ItemsControl SelectLayout(IItemsLayout layoutSpecification)
protected virtual ListViewBase SelectLayout(IItemsLayout layoutSpecification)
{
switch (layoutSpecification)
{
Expand All @@ -59,7 +59,7 @@ protected virtual ItemsControl SelectLayout(IItemsLayout layoutSpecification)

protected virtual void UpdateItemsSource()
{
if (ItemsControl == null)
if (ListViewBase == null)
{
return;
}
Expand All @@ -74,7 +74,7 @@ protected virtual void UpdateItemsSource()
// The ItemContentControls need the actual data item and the template so they can inflate the template
// and bind the result to the data item.
// ItemTemplateEnumerator handles pairing them up for the ItemContentControls to consume

_collectionViewSource = new CollectionViewSource
{
Source = TemplatedItemSourceFactory.Create(itemsSource, itemTemplate),
Expand All @@ -87,25 +87,25 @@ protected virtual void UpdateItemsSource()
{
Source = itemsSource,
IsSourceGrouped = false
};
};
}

ItemsControl.ItemsSource = _collectionViewSource.View;
ListViewBase.ItemsSource = _collectionViewSource.View;
}

protected virtual void UpdateItemTemplate()
{
if (Element == null || ItemsControl == null)
if (Element == null || ListViewBase == null)
{
return;
}

var formsTemplate = Element.ItemTemplate;
var itemsControlItemTemplate = ItemsControl.ItemTemplate;
var itemsControlItemTemplate = ListViewBase.ItemTemplate;

if (formsTemplate == null)
{
ItemsControl.ItemTemplate = null;
ListViewBase.ItemTemplate = null;

if (itemsControlItemTemplate != null)
{
Expand All @@ -120,7 +120,7 @@ protected virtual void UpdateItemTemplate()
// TODO hartez 2018/06/23 13:47:27 Handle DataTemplateSelector case
// Actually, DataTemplateExtensions CreateContent might handle the selector for us

ItemsControl.ItemTemplate =
ListViewBase.ItemTemplate =
(Windows.UI.Xaml.DataTemplate)Windows.UI.Xaml.Application.Current.Resources["ItemsViewDefaultTemplate"];

if (itemsControlItemTemplate == null)
Expand All @@ -130,7 +130,7 @@ protected virtual void UpdateItemTemplate()
}
}

static ItemsControl CreateGridView(GridItemsLayout gridItemsLayout)
static ListViewBase CreateGridView(GridItemsLayout gridItemsLayout)
{
var gridView = new FormsGridView();

Expand All @@ -153,7 +153,7 @@ static ItemsControl CreateGridView(GridItemsLayout gridItemsLayout)
return gridView;
}

static ItemsControl CreateHorizontalListView()
static ListViewBase CreateHorizontalListView()
{
// TODO hartez 2018/06/05 16:18:57 Is there any performance benefit to caching the ItemsPanelTemplate lookup?
// TODO hartez 2018/05/29 15:38:04 Make sure the ItemsViewStyles.xaml xbf gets into the nuspec
Expand All @@ -174,7 +174,7 @@ void LayoutOnPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == GridItemsLayout.SpanProperty.PropertyName)
{
if (ItemsControl is FormsGridView formsGridView)
if (ListViewBase is FormsGridView formsGridView)
{
formsGridView.MaximumRowsOrColumns = ((GridItemsLayout)_layout).Span;
}
Expand All @@ -188,14 +188,14 @@ void SetUpNewElement(ItemsView newElement)
return;
}

if (ItemsControl == null)
if (ListViewBase == null)
{
ItemsControl = SelectLayout(newElement.ItemsLayout);
ListViewBase = SelectLayout(newElement.ItemsLayout);

_layout = newElement.ItemsLayout;
_layout.PropertyChanged += LayoutOnPropertyChanged;

SetNativeControl(ItemsControl);
SetNativeControl(ListViewBase);
}

UpdateItemTemplate();
Expand Down Expand Up @@ -337,7 +337,7 @@ async Task AnimateTo(ListViewBase list, object targetItem, ScrollToPosition scro
// TODO hartez 2018/10/05 17:23:23 The animated scroll works fine vertically if we are scrolling to a greater Y offset.
// If we're scrolling back up to a lower Y offset, it just gives up and sends us to 0 (first item)
// Works fine if we disable animation, but that's not very helpful

scrollViewer.ChangeView(position.Value.X, position.Value.Y, null, false);

//if (scrollToPosition == ScrollToPosition.End)
Expand All @@ -350,7 +350,7 @@ async Task AnimateTo(ListViewBase list, object targetItem, ScrollToPosition scro
//}
//else
//{

//}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
using System;
using System.ComponentModel;
using System.Linq;
using Windows.UI.Xaml.Controls;
using UWPListViewSelectionMode = Windows.UI.Xaml.Controls.ListViewSelectionMode;

namespace Xamarin.Forms.Platform.UWP
{
public class SelectableItemsViewRenderer : ItemsViewRenderer
{
SelectableItemsView _selectableItemsView;

protected override void OnElementChanged(ElementChangedEventArgs<CollectionView> args)
{
var oldListViewBase = ListViewBase;
if (oldListViewBase != null)
{
oldListViewBase.ClearValue(ListViewBase.SelectionModeProperty);
oldListViewBase.SelectionChanged -= OnNativeSelectionChanged;
}

if (args.OldElement != null)
{
args.OldElement.SelectionChanged -= OnSelectionChanged;
}

base.OnElementChanged(args);
_selectableItemsView = args.NewElement;

if (_selectableItemsView != null)
{
_selectableItemsView.SelectionChanged += OnSelectionChanged;
}

var newListViewBase = ListViewBase;

if (newListViewBase != null)
{
newListViewBase.SetBinding(ListViewBase.SelectionModeProperty,
new Windows.UI.Xaml.Data.Binding
{
Source = _selectableItemsView,
Path = new Windows.UI.Xaml.PropertyPath("SelectionMode"),
Converter = new SelectionModeConvert(),
Mode = Windows.UI.Xaml.Data.BindingMode.TwoWay
});

newListViewBase.SelectionChanged += OnNativeSelectionChanged;
}
UpdateNativeSelection();
}

void UpdateNativeSelection()
{
switch (ListViewBase.SelectionMode)
{
case UWPListViewSelectionMode.None:
break;
case UWPListViewSelectionMode.Single:
ListViewBase.SelectionChanged -= OnNativeSelectionChanged;
if (_selectableItemsView != null)
{
if (_selectableItemsView.SelectedItem == null)
{
ListViewBase.SelectedItem = null;
}
else
{
ListViewBase.SelectedItem =
ListViewBase.Items.First(item =>
{
if (item is ItemTemplatePair itemPair)
{
return itemPair.Item == _selectableItemsView.SelectedItem;
}
else
{
return item == _selectableItemsView.SelectedItem;
}
});

}
}
ListViewBase.SelectionChanged += OnNativeSelectionChanged;
break;
case UWPListViewSelectionMode.Multiple:
ListViewBase.SelectionChanged -= OnNativeSelectionChanged;
ListViewBase.SelectedItems.Clear();
foreach (var nativeItem in ListViewBase.Items)
{
if (nativeItem is ItemTemplatePair itemPair && _selectableItemsView.SelectedItems.Contains(itemPair.Item))
{
ListViewBase.SelectedItems.Add(nativeItem);
}
else if (_selectableItemsView.SelectedItems.Contains(nativeItem))
{
ListViewBase.SelectedItems.Add(nativeItem);
}
}
ListViewBase.SelectionChanged += OnNativeSelectionChanged;
break;
case UWPListViewSelectionMode.Extended:
break;
default:
break;
}

}

void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
UpdateNativeSelection();
}

void OnNativeSelectionChanged(object sender, Windows.UI.Xaml.Controls.SelectionChangedEventArgs e)
{
if (Element != null)
{
switch (ListViewBase.SelectionMode)
{
case UWPListViewSelectionMode.None:
break;
case UWPListViewSelectionMode.Single:
var selectedItem =
ListViewBase.SelectedItem is ItemTemplatePair itemPair ? itemPair.Item : ListViewBase.SelectedItem;
Element.SelectionChanged -= OnSelectionChanged;
Element.SetValueFromRenderer(SelectableItemsView.SelectedItemProperty, selectedItem);
Element.SelectionChanged += OnSelectionChanged;
break;
case UWPListViewSelectionMode.Multiple:
Element.SelectionChanged -= OnSelectionChanged;

_selectableItemsView.SelectedItems.Clear();
var selectedItems =
ListViewBase.SelectedItems
.Select(a =>
{
var item = a is ItemTemplatePair itemPair1 ? itemPair1.Item : a;
return item;
})
.ToList();

foreach (var item in selectedItems)
{
_selectableItemsView.SelectedItems.Add(item);
}

Element.SelectionChanged += OnSelectionChanged;
break;

case UWPListViewSelectionMode.Extended:
break;

default:
break;
}
}
}

class SelectionModeConvert : Windows.UI.Xaml.Data.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var formSelectionMode = (SelectionMode)value;
switch (formSelectionMode)
{
case SelectionMode.None:
return UWPListViewSelectionMode.None;
case SelectionMode.Single:
return UWPListViewSelectionMode.Single;
case SelectionMode.Multiple:
return UWPListViewSelectionMode.Multiple;
default:
return UWPListViewSelectionMode.None;
}
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
{
var uwpListViewSelectionMode = (UWPListViewSelectionMode)value;
switch (uwpListViewSelectionMode)
{
case UWPListViewSelectionMode.None:
return SelectionMode.None;
case UWPListViewSelectionMode.Single:
return SelectionMode.Single;
case UWPListViewSelectionMode.Multiple:
return SelectionMode.Multiple;
case UWPListViewSelectionMode.Extended:
return SelectionMode.None;
default:
return SelectionMode.None;
}
}
}

}
}
4 changes: 3 additions & 1 deletion Xamarin.Forms.Platform.UAP/Xamarin.Forms.Platform.UAP.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="AccessibilityExtensions.cs" />
<Compile Include="CollectionView\ItemsViewRenderer.cs" />
<Compile Include="CollectionView\SelectableItemsViewRenderer.cs" />
<Compile Include="Extensions\ImageExtensions.cs" />
<Compile Include="FormsCheckBox.cs" />
<Compile Include="IImageVisualElementRenderer.cs" />
<Compile Include="ImageButtonRenderer.cs" />
<Compile Include="CollectionViewRenderer.cs" />
<Compile Include="CollectionView\CollectionViewRenderer.cs" />
<Compile Include="FormsCancelButton.cs" />
<Compile Include="AlertDialog.cs" />
<Compile Include="IDontGetFocus.cs" />
Expand Down

0 comments on commit 3ac5b51

Please sign in to comment.