Skip to content

Commit 5cda498

Browse files
committed
Fix TreeDataGridTextCell changing TextCell.Value
`TreeDataGridTextCell` listens to property changes of `ITextCell.Value` to update its own `Value` property. The setter of `TreeDataGridTextCell.Value` updates `ITextCell.Text` to the string representation of the new value. This is done because the cell can be edited, and `TreeDataGridTextCell.Value` is bound to `TextBox.Text` two-way when the user wants to edit the cell. However, `TextCell<T>.Text` which the setter of `TreeDataGridTextCell.Value` will always set, doesn't respect the read-only status or the editing status of the cell: 1) `TextCell<T>.Value` updates. 2) `TreeDataGridTextCell` reacts and sets `TreeDataGridTextCell.Value` to the string representation of the new value. 3) The setter of `TreeDataGridTextCell.Value` will set `TextCell<T>.Text` to the string representation. 4) The setter of `TextCell<T>.Text` will try to convert the text representation of the new value as the new value... If it weren't for the fact that `RaiseAndSetIfChanged` does what it's supposed to do, which is only raise if changed, this would be a loop that goes on forever. Users will get an exception if `T` of `TextCell<T>` fails to convert: `Convert.ChangeType(value, typeof(T))`. This obviously isn't an issue when `T` is `string` but it does have issues for base types like numbers (localization issues), and custom types will almost always result in an exception.
1 parent d183b14 commit 5cda498

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

src/Avalonia.Controls.TreeDataGrid/Models/TreeDataGrid/TextCell.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@ public TextCell(
4949
public string? Text
5050
{
5151
get => _value?.ToString();
52-
set{
52+
set
53+
{
54+
if (IsReadOnly) return;
55+
5356
if (string.IsNullOrEmpty(value))
5457
{
5558
Value = default(T?);

tests/Avalonia.Controls.TreeDataGrid.Tests/Models/TextCellTests.cs

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
3+
using System.Globalization;
44
using System.Reactive.Subjects;
5-
using System.Text;
6-
using System.Threading.Tasks;
75
using Avalonia.Controls.Models.TreeDataGrid;
86
using Avalonia.Data;
97
using Avalonia.Headless.XUnit;
10-
using Avalonia.Media;
118
using Xunit;
129

1310
namespace Avalonia.Controls.TreeDataGridTests.Models
@@ -95,5 +92,38 @@ public void Modified_Value_Is_Not_Written_To_Binding_On_CancelEdit()
9592
Assert.Equal("initial", target.Value);
9693
Assert.Equal(new[] { "initial" }, result);
9794
}
95+
96+
[AvaloniaFact(Timeout = 10000)]
97+
public void Setting_Text_Does_Not_Change_ReadOnly_Value()
98+
{
99+
var binding = new BehaviorSubject<BindingValue<CustomValueObject>>(value: new CustomValueObject(100));
100+
var target = new TextCell<CustomValueObject>(binding, isReadOnly: true);
101+
102+
Assert.Equal(100, target.Value.Value);
103+
104+
// simulating TreeDataGridTextCell.OnModelPropertyChanged
105+
target.PropertyChanged += (sender, args) =>
106+
{
107+
if (args.PropertyName == nameof(ITextCell.Value))
108+
target.Text = target.Value.ToString();
109+
};
110+
111+
target.Value = new CustomValueObject(42);
112+
Assert.Equal(42, target.Value.Value);
113+
}
114+
115+
private readonly struct CustomValueObject
116+
{
117+
private readonly int _value;
118+
119+
public CustomValueObject(int value)
120+
{
121+
_value = value;
122+
}
123+
124+
public int Value => _value;
125+
126+
public override string ToString() => _value.ToString(CultureInfo.InvariantCulture);
127+
}
98128
}
99129
}

0 commit comments

Comments
 (0)