Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
eb4a629
fix for 12008
SuthiYuvaraj Sep 11, 2025
fc1ed97
Update Issue12008.cs
SuthiYuvaraj Sep 11, 2025
3c110bc
sample changes
SuthiYuvaraj Sep 11, 2025
b5556bf
commit for testcase changes
SuthiYuvaraj Sep 11, 2025
5f61f4c
fix for ios
SuthiYuvaraj Sep 16, 2025
189d300
Update ReorderableItemsViewDelegator.cs
SuthiYuvaraj Sep 16, 2025
b77d9ca
Update ReorderableItemsViewDelegator.cs
SuthiYuvaraj Sep 22, 2025
3b83122
CollectionView2 changes
SuthiYuvaraj Sep 26, 2025
5823e71
Update Issue12008.cs
SuthiYuvaraj Oct 10, 2025
b245a0e
Update Issue12008.cs
SuthiYuvaraj Oct 10, 2025
0842820
Commit for review changes
SuthiYuvaraj Feb 20, 2026
6717d1c
Add GitHub Actions workflow to run evaluate-pr-tests via Copilot CLI …
PureWeen Mar 25, 2026
720a9d4
Allow fork PRs to auto-trigger evaluate-pr-tests workflow (#34655)
PureWeen Mar 25, 2026
53e6575
Add regression test for #34713: Binding with Converter and x:DataType…
StephaneDelcroix Mar 28, 2026
23e0a2a
Add merge flow: net11.0 → next release branch (#34626)
PureWeen Mar 30, 2026
cdead27
Fix Flyout memory leak (#34485)
pictos Apr 1, 2026
59b36c2
[spec] Add `maui device list` command spec to CLI design doc (#34277)
rmarinho Apr 2, 2026
148b9aa
Fix x:Key values not escaped in source-generated C# string literals (…
StephaneDelcroix Apr 2, 2026
74a6940
[AI] Sample: Detail page, semantic search, streaming, and UI polish (…
mattleibow Apr 2, 2026
794a9fa
Add standalone code-review skill with maintainer-sourced review rules…
PureWeen Apr 2, 2026
64871a0
Merge branch 'main' into fix-12008
SuthiYuvaraj Apr 6, 2026
1fcc87d
Merge branch 'inflight/current' into fix-12008
kubaflo Apr 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,16 @@ public bool OnItemMove(int fromPosition, int toPosition)
return false;
}

if (fromItemIndex < 0 || fromItemIndex >= fromList.Count)
{
return false;
}

if (toItemIndex < 0 || toItemIndex > toList.Count)
{
return false;
}

if (fromList != null && toList != null)
{
var fromItem = fromList[fromItemIndex];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ public override int GetMovementFlags(RecyclerView recyclerView, RecyclerView.Vie

public override bool OnMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)
{
if (viewHolder.ItemViewType != target.ItemViewType)
var sourceItemViewType = viewHolder.ItemViewType;

if (sourceItemViewType == ItemViewType.Header || sourceItemViewType == ItemViewType.Footer
|| sourceItemViewType == ItemViewType.GroupHeader || sourceItemViewType == ItemViewType.GroupFooter)
{
return false;
}
Expand Down
225 changes: 225 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue12008.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
using System.Collections.ObjectModel;
using System.ComponentModel;

namespace Maui.Controls.Sample.Issues;

[Issue(IssueTracker.Github, 12008, "CollectionView Drag and Drop Reordering Can't Drop in Empty Group", PlatformAffected.Android | PlatformAffected.iOS | PlatformAffected.macOS)]
public partial class Issue12008 : ContentPage
{
private Label statusLabel;
private CollectionView collectionView;
private Button btnCreateEmptyGroup;
public Issue12008()
{
this.BindingContext = new Issue12008ViewModel();
var grid = new Grid
{
RowDefinitions = new RowDefinitionCollection
{
new RowDefinition { Height = GridLength.Auto },
new RowDefinition { Height = GridLength.Star }
}
};

// Top StackLayout
var stackLayout = new StackLayout
{
Padding = 10
};

var titleLabel = new Label
{
Text = "Drag and Drop Test - Empty Groups",
FontSize = 18,
FontAttributes = FontAttributes.Bold,
Margin = new Thickness(0, 0, 0, 10)
};

var instructionsLabel = new Label
{
Text = "Instructions: Try dragging items between groups, including into empty groups.",
FontSize = 14,
Margin = new Thickness(0, 0, 0, 10)
};

btnCreateEmptyGroup = new Button
{
Text = "Create Empty Group",
AutomationId = "CreateEmptyGroupButton12008"
};
btnCreateEmptyGroup.Clicked += OnCreateEmptyGroupClicked;

statusLabel = new Label
{
Text = "Status: Ready",
FontSize = 12,
AutomationId = "StatusLabel12008"
};

stackLayout.Children.Add(titleLabel);
stackLayout.Children.Add(instructionsLabel);
stackLayout.Children.Add(btnCreateEmptyGroup);
stackLayout.Children.Add(statusLabel);

Grid.SetRow(stackLayout, 0);
grid.Children.Add(stackLayout);

// CollectionView
collectionView = new CollectionView
{
IsGrouped = true,
CanReorderItems = true,
CanMixGroups = true,
AutomationId = "CollectionView12008"
};
collectionView.SetBinding(ItemsView.ItemsSourceProperty, "GroupedItems");

// Group Header Template
collectionView.GroupHeaderTemplate = new DataTemplate(() =>
{
var headerGrid = new Grid
{
BackgroundColor = Colors.LightBlue,
Padding = 10,
ColumnDefinitions = new ColumnDefinitionCollection
{
new ColumnDefinition { Width = GridLength.Star },
new ColumnDefinition { Width = GridLength.Auto }
}
};

var headerLabel = new Label
{
FontAttributes = FontAttributes.Bold,
FontSize = 16
};
headerLabel.SetBinding(Label.TextProperty, "GroupName");
headerLabel.SetBinding(AutomationIdProperty, new Binding("GroupName", stringFormat: "GroupHeader12008{0}"));

var countLabel = new Label
{
FontSize = 12
};
countLabel.SetBinding(Label.TextProperty, new Binding("Count", stringFormat: "Count: {0}"));
countLabel.SetBinding(AutomationIdProperty, new Binding("GroupName", stringFormat: "GroupCount12008{0}"));

headerGrid.Children.Add(headerLabel);
headerGrid.Children.Add(countLabel);
Grid.SetColumn(headerLabel, 0);
Grid.SetColumn(countLabel, 1);
return headerGrid;
});

// Item Template
collectionView.ItemTemplate = new DataTemplate(() =>
{
var itemGrid = new Grid
{
Padding = 10,
BackgroundColor = Colors.LightGray,
Margin = new Thickness(2)
};

var itemLabel = new Label
{
FontSize = 14
};
itemLabel.SetBinding(Label.TextProperty, "Name");
itemLabel.SetBinding(AutomationIdProperty, new Binding("Name", stringFormat: "Item12008{0}"));

itemGrid.Children.Add(itemLabel);
return itemGrid;
});

Grid.SetRow(collectionView, 1);
grid.Children.Add(collectionView);

Content = grid;
}


private void OnCreateEmptyGroupClicked(object sender, EventArgs e)
{
if (this.BindingContext is Issue12008ViewModel viewModel)
{
viewModel.CreateEmptyGroup();
statusLabel.Text = "Status: Empty group created";
}
}
}

public class Issue12008ViewModel : INotifyPropertyChanged
{
private ObservableCollection<Issue12008Group> _groupedItems;

public ObservableCollection<Issue12008Group> GroupedItems
{
get => _groupedItems;
set
{
_groupedItems = value;
OnPropertyChanged();
}
}

public Issue12008ViewModel()
{
InitializeData();
}

private void InitializeData()
{
GroupedItems = new ObservableCollection<Issue12008Group>
{
new Issue12008Group("GroupA", new List<Issue12008Item>
{
new Issue12008Item("ItemA1"),
new Issue12008Item("ItemA2"),
new Issue12008Item("ItemA3")
}),
new Issue12008Group("GroupB", new List<Issue12008Item>
{
new Issue12008Item("ItemB1"),
new Issue12008Item("ItemB2")
}),
new Issue12008Group("GroupC", new List<Issue12008Item>
{
new Issue12008Item("ItemC1")
})
};
}

public void CreateEmptyGroup()
{
var emptyGroup = new Issue12008Group("EmptyGroup", new List<Issue12008Item>());
GroupedItems.Add(emptyGroup);
}

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

public class Issue12008Group : ObservableCollection<Issue12008Item>
{
public string GroupName { get; }

public Issue12008Group(string groupName, IEnumerable<Issue12008Item> items) : base(items)
{
GroupName = groupName;
}
}

public class Issue12008Item
{
public string Name { get; }

public Issue12008Item(string name)
{
Name = name;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#if TEST_FAILS_ON_WINDOWS // The test fails on Windows because Drag and Drop with grouping is not supported on Windows
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue12008 : _IssuesUITest
{
public Issue12008(TestDevice testDevice) : base(testDevice)
{
}

public override string Issue => "CollectionView Drag and Drop Reordering Can't Drop in Empty Group";

[Test]
[Category(UITestCategories.CollectionView)]
public void EmptyGroupCreationShouldWork()
{
App.WaitForElement("CreateEmptyGroupButton12008");
App.Tap("CreateEmptyGroupButton12008");

App.WaitForElement("GroupHeader12008EmptyGroup");
App.WaitForElement("StatusLabel12008");
var statusText = App.WaitForElement("StatusLabel12008").GetText();
Assert.That(statusText, Does.Contain("Empty group created"));
}

[Test]
[Category(UITestCategories.CollectionView)]
public void DragItemIntoEmptyGroupShouldSucceed()
{
App.WaitForElement("CreateEmptyGroupButton12008");
App.Tap("CreateEmptyGroupButton12008");
App.WaitForElement("GroupHeader12008EmptyGroup");
App.WaitForElement("Item12008ItemA1");
App.DragAndDrop("Item12008ItemA1", "GroupHeader12008EmptyGroup");
App.WaitForElement("GroupCount12008EmptyGroup");
var groupCountLabel = App.WaitForElement("GroupCount12008EmptyGroup");
var countText = groupCountLabel.GetText();
Assert.That(countText, Is.EqualTo("Count: 1"), "Item was not moved into the empty group");
}
}
#endif
Loading