Consider this minimal example;
xaml:
<?xml version="1.0" encoding="utf-8" ?>
<Window
x:Class="NumberboxText.MainWindow"
xmlns=";
xmlns:x=";
xmlns:d=";
xmlns:local="using:NumberboxText"
xmlns:mc=";
Title="NumberboxText"
mc:Ignorable="d">
<StackPanel Orientation="Vertical">
<Button Click="Button_Click">Sort</Button>
<ListView ItemsSource="{x:Bind Items}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:TestItem">
<NumberBox
Value="{x:Bind Current, Mode=TwoWay}"
Minimum="{x:Bind Min}"
/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Window>
cs:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.WindowsRuntime;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Media.Capture.Frames;
namespace NumberboxText;
public class TestItem : INotifyPropertyChanged
{
private int _order;
private int _min;
private int _current;
public event PropertyChangedEventHandler? PropertyChanged;
public int Order
{
get => _order;
set => SetProperty(ref _order, value);
}
public int Min
{
get => _min;
set => SetProperty(ref _min, value);
}
public int Current
{
get => _current;
set => SetProperty(ref _current, value);
}
protected void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public sealed partial class MainWindow : Window
{
public readonly ObservableCollection<TestItem> Items = [];
public MainWindow()
{
this.InitializeComponent();
Items.Add(new TestItem { Order = 3, Min = 3, Current = 3 });
Items.Add(new TestItem { Order = 1, Min = 1, Current = 1 });
Items.Add(new TestItem { Order = 4, Min = 4, Current = 4 });
Items.Add(new TestItem { Order = 2, Min = 2, Current = 2 });
}
private void Button_Click(object sender, RoutedEventArgs e)
{
for (var i = Items.Count - 1; i >= 0; i--)
{
for (var j = 1; j <= i; j++)
{
if (Items[j - 1].Order > Items[j].Order)
{
Items.Move(j - 1, j);
}
}
}
}
}
When I click the sort button, I am getting this;
It looks like listview tries to change numberbox values in place, but it can't because of Minimum
constraint, causing the change on values.
Should I consider this a bug to report, or is there a way I can make this work?
Consider this minimal example;
xaml:
<?xml version="1.0" encoding="utf-8" ?>
<Window
x:Class="NumberboxText.MainWindow"
xmlns="http://schemas.microsoft/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft/expression/blend/2008"
xmlns:local="using:NumberboxText"
xmlns:mc="http://schemas.openxmlformats./markup-compatibility/2006"
Title="NumberboxText"
mc:Ignorable="d">
<StackPanel Orientation="Vertical">
<Button Click="Button_Click">Sort</Button>
<ListView ItemsSource="{x:Bind Items}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="local:TestItem">
<NumberBox
Value="{x:Bind Current, Mode=TwoWay}"
Minimum="{x:Bind Min}"
/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Window>
cs:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices.WindowsRuntime;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.Media.Capture.Frames;
namespace NumberboxText;
public class TestItem : INotifyPropertyChanged
{
private int _order;
private int _min;
private int _current;
public event PropertyChangedEventHandler? PropertyChanged;
public int Order
{
get => _order;
set => SetProperty(ref _order, value);
}
public int Min
{
get => _min;
set => SetProperty(ref _min, value);
}
public int Current
{
get => _current;
set => SetProperty(ref _current, value);
}
protected void SetProperty<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
field = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public sealed partial class MainWindow : Window
{
public readonly ObservableCollection<TestItem> Items = [];
public MainWindow()
{
this.InitializeComponent();
Items.Add(new TestItem { Order = 3, Min = 3, Current = 3 });
Items.Add(new TestItem { Order = 1, Min = 1, Current = 1 });
Items.Add(new TestItem { Order = 4, Min = 4, Current = 4 });
Items.Add(new TestItem { Order = 2, Min = 2, Current = 2 });
}
private void Button_Click(object sender, RoutedEventArgs e)
{
for (var i = Items.Count - 1; i >= 0; i--)
{
for (var j = 1; j <= i; j++)
{
if (Items[j - 1].Order > Items[j].Order)
{
Items.Move(j - 1, j);
}
}
}
}
}
When I click the sort button, I am getting this;
It looks like listview tries to change numberbox values in place, but it can't because of Minimum
constraint, causing the change on values.
Should I consider this a bug to report, or is there a way I can make this work?
Share Improve this question asked Nov 19, 2024 at 13:11 yasaryasar 13.8k30 gold badges97 silver badges170 bronze badges 3- Instead of moving items in the ObservableCollection, you could create a new sorted list and replace the existing items in the ObservableCollection. This would prevent the ListView from trying to update the NumberBox values in place. – user20561885 Commented Nov 19, 2024 at 13:30
- @Mellik Problem persists thay way, it probably re-uses underlying controls. – yasar Commented Nov 19, 2024 at 13:31
- You could create a small wrapper class which takes the item itself and the value and then fill a list of that wrapper class and then sort that. Then clear the original Items list and add the list to it – user20561885 Commented Nov 19, 2024 at 13:54
2 Answers
Reset to default 0You could create a wrapper class which takes the item itself and the value to sort.
public class ItemWrapper {
public TestItem Item { get; set; }
public int Value { get; set; }
public ItemWrapper(TestItem item)
{
Item = item;
Value = item.Order; // or any other property you want to track
}
}
Then fill a list with wrapperclass instances of the items.
private void Button_Click(object sender, RoutedEventArgs e)
{
var wrappedItems = Items.Select(item => new ItemWrapper(item)).ToList();
Then bubble sort that and clear the original Items list and add the list to it
for (var i = wrappedItems.Count - 1; i >= 0; i--)
{
for (var j = 1; j <= i; j++)
{
// Compare the Order property of the TestItem within the ItemWrapper
if (wrappedItems[j - 1].Item.Order > wrappedItems[j].Item.Order)
{
// Swap the ItemWrapper objects
var temp = wrappedItems[j - 1];
wrappedItems[j - 1] = wrappedItems[j];
wrappedItems[j] = temp;
}
}
}
Items.Clear();
// Add the sorted items back to the original collection
foreach (var wrappedItem in wrappedItems)
{
Items.Add(wrappedItem.Item);
}
}
This behavior seems to be based on the order of the XAML code.
In the case below, Minimum
is set after Value
:
<NumberBox
Value="{x:Bind Current, Mode=TwoWay}"
Minimum="{x:Bind Min}" />
Changing the order seems to fix you issue:
<NumberBox
Minimum="{x:Bind Min}"
Value="{x:Bind Current, Mode=TwoWay}" />
Note that changing the order on Items
, doesn't mean that the ListView
will change its ListViewItem
s accordingly. The ListView
might reuse its available ListViewItem
s, which might have the Minimum
set from its previous item, and bind the corresponding item.