最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - ListView of NumberBox with minimum constraint corrupts value when ordered - Stack Overflow

programmeradmin4浏览0评论

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
Add a comment  | 

2 Answers 2

Reset to default 0

You 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 ListViewItems accordingly. The ListView might reuse its available ListViewItems, which might have the Minimum set from its previous item, and bind the corresponding item.

发布评论

评论列表(0)

  1. 暂无评论