[C] Lowest specificity for propagated values (#17648)

IsEnabled value is propagated on parenting. Do this with the special
(fromHandler) specificity, to allow styling

- fixes #17597
This commit is contained in:
Stephane Delcroix 2023-11-20 22:43:26 +01:00 коммит произвёл GitHub
Родитель 6f6d1929d3
Коммит 2cdf7234bb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 103 добавлений и 18 удалений

Просмотреть файл

@ -124,19 +124,29 @@ namespace Microsoft.Maui.Controls
if (bpcontext == null)
return;
var original = bpcontext.Values.GetSpecificityAndValue().Value;
var newValue = bpcontext.Values.GetClearedValue();
var changed = !Equals(original, newValue);
var original = bpcontext.Values.GetSpecificityAndValue();
if (original.Key == SetterSpecificity.FromHandler)
bpcontext.Values.Remove(SetterSpecificity.FromHandler);
var newValue = bpcontext.Values.GetClearedValue(specificity);
var changed = !Equals(original.Value, newValue);
if (changed)
{
property.PropertyChanging?.Invoke(this, original, newValue);
property.PropertyChanging?.Invoke(this, original.Value, newValue);
OnPropertyChanging(property.PropertyName);
}
bpcontext.Values.Remove(specificity);
//there's some side effect implemented in CoerceValue (see IsEnabled) that we need to trigger here
if (property.CoerceValue != null)
property.CoerceValue(this, newValue);
if (changed)
{
OnPropertyChanged(property.PropertyName);
property.PropertyChanged?.Invoke(this, original, newValue);
property.PropertyChanged?.Invoke(this, original.Value, newValue);
}
}

Просмотреть файл

@ -23,7 +23,7 @@ namespace Microsoft.Maui.Controls
else
{
// support normal/code properties
self.SetValue(property, value);
self.SetValue(property, value, SetterSpecificity.FromHandler);
}
}

Просмотреть файл

@ -91,26 +91,24 @@ namespace Microsoft.Maui.Controls
}
/// <summary>
/// Called by ClearValueCore, returns what the value would be if cleared
/// Called by ClearValueCore, returns what the top value would be if cleared
/// </summary>
public object? GetClearedValue()
public object? GetClearedValue(SetterSpecificity clearedSpecificity)
{
if (_values is not null)
{
return _values.Count >= 2 ? _values[_values.Keys[_values.Count - 2]] : null;
var index = _values.IndexOfKey(clearedSpecificity);
if (index == _values.Count -1) //last value will be cleared
return _values.Count >= 2 ? _values[_values.Keys[_values.Count - 2]] : null;
return _values.Last().Value;
}
// Fast path should return the "lower" value
if (_first is not null && _second is not null)
{
if (_second.Value.Key.CompareTo(_first.Value.Key) >= 0)
{
return _first.Value.Value;
}
else
{
if (_first.Value.Key == clearedSpecificity)
return _second.Value.Value;
}
return _first.Value.Value;
}
else if (_first is not null)
{

Просмотреть файл

@ -63,9 +63,10 @@ namespace Microsoft.Maui.Controls.Core.UnitTests
{
var list = new SetterSpecificityList();
list.SetValue(SetterSpecificity.DefaultValue, nameof(SetterSpecificity.DefaultValue));
Assert.Equal(nameof(SetterSpecificity.DefaultValue), list.GetClearedValue());
Assert.Equal(nameof(SetterSpecificity.DefaultValue), list.GetClearedValue(SetterSpecificity.DefaultValue));
list.SetValue(SetterSpecificity.ManualValueSetter, nameof(SetterSpecificity.ManualValueSetter));
Assert.Equal(nameof(SetterSpecificity.DefaultValue), list.GetClearedValue());
Assert.Equal(nameof(SetterSpecificity.DefaultValue), list.GetClearedValue(SetterSpecificity.ManualValueSetter));
}
}
}

Просмотреть файл

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Microsoft.Maui.Controls.Xaml.UnitTests"
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Maui17597">
<StackLayout>
<Entry x:Name="Test_Entry" Text="Remove Text To Disable Button"/>
<Button x:Name="button" Text="Test Button" Margin="0,10,0,0" >
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="True"/> <!-- Does not seem to honor this statement at all -->
<Style.Triggers>
<DataTrigger Binding="{Binding Source={x:Reference Test_Entry}, Path=Text.Length}" TargetType="Button" Value="0">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackLayout>
</ContentPage>

Просмотреть файл

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Controls.Core.UnitTests;
using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Devices;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.UnitTests;
using Microsoft.Maui.Dispatching;
using NUnit.Framework;
namespace Microsoft.Maui.Controls.Xaml.UnitTests;
public partial class Maui17597 : ContentPage
{
public Maui17597() => InitializeComponent();
public Maui17597(bool useCompiledXaml)
{
//this stub will be replaced at compile time
}
[TestFixture]
class Test
{
[SetUp]
public void Setup()
{
Application.SetCurrentApplication(new MockApplication());
DispatcherProvider.SetCurrent(new DispatcherProviderStub());
}
[TearDown] public void TearDown() => AppInfo.SetCurrent(null);
[Test]
public void DataTriggerInStyle([Values(false, true)] bool useCompiledXaml)
{
var page = new Maui17597(useCompiledXaml);
Assert.That(page.Test_Entry.Text, Is.EqualTo("Remove Text To Disable Button"));
Assert.That(page.button.IsEnabled, Is.True);
page.Test_Entry.SetValueFromRenderer(Entry.TextProperty, "");
Assert.That(page.Test_Entry.Text, Is.Empty);
Assert.That(page.Test_Entry.Text.Length, Is.EqualTo(0));
Assert.That(page.button.IsEnabled, Is.False);
page.Test_Entry.SetValueFromRenderer(Entry.TextProperty, "foo");
Assert.That(page.Test_Entry.Text, Is.Not.Empty);
Assert.That(page.button.IsEnabled, Is.True);
}
}
}