### Description of Change

We all know the drill!
This commit is contained in:
Rui Marinho 2024-08-23 21:42:28 +01:00 коммит произвёл GitHub
Родитель a15cf9b55a eeafab4a0b
Коммит ad8275e2bb
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
24 изменённых файлов: 267 добавлений и 237 удалений

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

@ -45,124 +45,6 @@
</Attributes>
<Docs>
<summary>An <see cref="T:Microsoft.Maui.Controls.ItemsView`1" /> that displays a collection of data as a vertical list.</summary>
<remarks>
<para>
<img href="~/xml/Microsoft.Maui.Controls/_images/ListView.TripleScreenShot.png" />
</para>
<para>The following example shows a basic use:</para>
<example>
<code lang="csharp lang-csharp"><![CDATA[
using System;
using System.Collections.Generic;
using Microsoft.Maui.Controls;
namespace FormsGallery
{
class ListViewDemoPage : ContentPage
{
class Person
{
public Person(string name, DateTime birthday, Color favoriteColor)
{
this.Name = name;
this.Birthday = birthday;
this.FavoriteColor = favoriteColor;
}
public string Name { private set; get; }
public DateTime Birthday { private set; get; }
public Color FavoriteColor { private set; get; }
};
public ListViewDemoPage()
{
Label header = new Label
{
Text = "ListView",
FontSize = Device.GetNamedSize (NamedSize.Large, typeof(Label)),
HorizontalOptions = LayoutOptions.Center
};
// Define some data.
List<Person> people = new List<Person>
{
new Person("Abigail", new DateTime(1975, 1, 15), Color.Aqua),
new Person("Bob", new DateTime(1976, 2, 20), Color.Black),
// ...etc.,...
new Person("Yvonne", new DateTime(1987, 1, 10), Color.Purple),
new Person("Zachary", new DateTime(1988, 2, 5), Color.Red)
};
// Create the ListView.
ListView listView = new ListView
{
// Source of data items.
ItemsSource = people,
// Define template for displaying each item.
// (Argument of DataTemplate constructor is called for
// each item; it must return a Cell derivative.)
ItemTemplate = new DataTemplate(() =>
{
// Create views with bindings for displaying each property.
Label nameLabel = new Label();
nameLabel.SetBinding(Label.TextProperty, "Name");
Label birthdayLabel = new Label();
birthdayLabel.SetBinding(Label.TextProperty,
new Binding("Birthday", BindingMode.OneWay,
null, null, "Born {0:d}"));
BoxView boxView = new BoxView();
boxView.SetBinding(BoxView.ColorProperty, "FavoriteColor");
// Return an assembled ViewCell.
return new ViewCell
{
View = new StackLayout
{
Padding = new Thickness(0, 5),
Orientation = StackOrientation.Horizontal,
Children =
{
boxView,
new StackLayout
{
VerticalOptions = LayoutOptions.Center,
Spacing = 0,
Children =
{
nameLabel,
birthdayLabel
}
}
}
}
};
})
};
// Accomodate iPhone status bar.
this.Padding = new Thickness(10, Device.OnPlatform(20, 0, 0), 10, 5);
// Build the page.
this.Content = new StackLayout
{
Children =
{
header,
listView
}
};
}
}
}
]]></code>
</example>
</remarks>
</Docs>
<Members>
<Member MemberName=".ctor">

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

@ -330,7 +330,10 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
var actuallyRemoved = poppedViewController == null ? true : !await task;
_ignorePopCall = false;
poppedViewController?.Dispose();
if (poppedViewController is ParentingViewController pvc)
pvc.Disconnect(false);
else
poppedViewController?.Dispose();
UpdateToolBarVisible();
return actuallyRemoved;
@ -1144,7 +1147,9 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
return;
if (child is not null)
{
child.PropertyChanged -= HandleChildPropertyChanged;
}
if (value is not null)
{
@ -1274,6 +1279,57 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
}
}
internal void Disconnect(bool dispose)
{
if (Child is Page child)
{
child.SendDisappearing();
child.PropertyChanged -= HandleChildPropertyChanged;
Child = null;
}
if (_tracker is not null)
{
_tracker.Target = null;
_tracker.CollectionChanged -= TrackerOnCollectionChanged;
_tracker = null;
}
if (NavigationItem.TitleView is not null)
{
if (dispose)
NavigationItem.TitleView.Dispose();
NavigationItem.TitleView = null;
}
if (NavigationItem.RightBarButtonItems is not null && dispose)
{
for (var i = 0; i < NavigationItem.RightBarButtonItems.Length; i++)
NavigationItem.RightBarButtonItems[i].Dispose();
}
if (ToolbarItems is not null && dispose)
{
for (var i = 0; i < ToolbarItems.Length; i++)
ToolbarItems[i].Dispose();
}
for (int i = View.Subviews.Length - 1; i >= 0; i--)
{
View.Subviews[i].RemoveFromSuperview();
}
for (int i = ChildViewControllers.Length - 1; i >= 0; i--)
{
var childViewController = ChildViewControllers[i];
childViewController.View.RemoveFromSuperview();
childViewController.RemoveFromParentViewController();
}
}
protected override void Dispose(bool disposing)
{
if (_disposed)
@ -1285,34 +1341,7 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
if (disposing)
{
if (Child is Page child)
{
child.SendDisappearing();
child.PropertyChanged -= HandleChildPropertyChanged;
Child = null;
}
_tracker.Target = null;
_tracker.CollectionChanged -= TrackerOnCollectionChanged;
_tracker = null;
if (NavigationItem.TitleView != null)
{
NavigationItem.TitleView.Dispose();
NavigationItem.TitleView = null;
}
if (NavigationItem.RightBarButtonItems != null)
{
for (var i = 0; i < NavigationItem.RightBarButtonItems.Length; i++)
NavigationItem.RightBarButtonItems[i].Dispose();
}
if (ToolbarItems != null)
{
for (var i = 0; i < ToolbarItems.Length; i++)
ToolbarItems[i].Dispose();
}
Disconnect(true);
}
base.Dispose(disposing);
@ -1973,7 +2002,7 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
if (_child?.IsConnected() == true)
{
_child.PlatformView.RemoveFromSuperview();
(_child.ContainerView ?? _child.PlatformView).RemoveFromSuperview();
_child.DisconnectHandler();
_child = null;
}

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

@ -208,7 +208,7 @@ namespace Microsoft.Maui.Controls.Handlers.Compatibility
public override void SetNeedsLayout()
{
base.SetNeedsLayout();
Superview?.SetNeedsLayout();
this.GetSuperViewIfWindowSet()?.SetNeedsLayout();
}
[Microsoft.Maui.Controls.Internals.Preserve(Conditional = true)]

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

@ -402,6 +402,8 @@ namespace Microsoft.Maui.Controls
void RemoveFromInnerChildren(Element page)
{
InternalChildren.Remove(page);
// TODO For NET9 we should remove this because the DisconnectHandlers will take care of it
page.Handler = null;
}

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

@ -188,7 +188,7 @@ namespace Microsoft.Maui.Controls.Platform
{
platformButton.ImageEdgeInsets = imageInsets;
platformButton.TitleEdgeInsets = titleInsets;
platformButton.Superview?.SetNeedsLayout();
platformButton.GetSuperViewIfWindowSet()?.SetNeedsLayout();
}
#pragma warning restore CA1416, CA1422
}

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

@ -51,7 +51,7 @@ namespace Microsoft.Maui.Controls
newToolBarItems.AddRange(toolbarItems);
if (sender is ToolbarItem ti)
PlatformView.OnToolbarItemPropertyChanged(e, ti, newToolBarItems, MauiContext!, null, OnToolbarItemPropertyChanged, _currentMenuItems, _currentToolbarItems, UpdateMenuItemIcon);
PlatformView.OnToolbarItemPropertyChanged(e, ti, newToolBarItems, MauiContext!, BarTextColor, OnToolbarItemPropertyChanged, _currentMenuItems, _currentToolbarItems, UpdateMenuItemIcon);
}
void UpdateMenuItemIcon(Context context, IMenuItem menuItem, ToolbarItem toolBarItem)

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 27 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 25 KiB

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

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ns="clr-namespace:Maui.Controls.Sample.Issues"
Shell.TitleColor="Red"
x:Class="Maui.Controls.Sample.Issues.Issue10660">
<Shell.ToolbarItems>
<ToolbarItem x:Name="StateButton" Text="Close"/>
</Shell.ToolbarItems>
<ShellContent Title="Home page"
Route="MainPage">
<ShellContent.ContentTemplate>
<DataTemplate>
<ContentPage>
<StackLayout>
<Button Text="Change Text"
AutomationId="ChangeText"
Clicked="ChangeTextClicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
</DataTemplate>
</ShellContent.ContentTemplate>
</ShellContent>
</Shell>

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

@ -0,0 +1,20 @@
using System;
using System.Collections.ObjectModel;
using Microsoft.Maui.Controls;
namespace Maui.Controls.Sample.Issues;
[Issue(IssueTracker.Github, 10660, "Inconsistent toolbar text color on interaction", PlatformAffected.Android)]
public partial class Issue10660 : Shell
{
public Issue10660()
{
InitializeComponent();
}
private void ChangeTextClicked(object sender, EventArgs e)
{
StateButton.Text = StateButton.Text == "Close" ? "Open" : "Close";
}
}

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

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8" ?>
<Shell xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ns="clr-namespace:Maui.Controls.Sample.Issues"
Shell.TitleColor="Red"
x:Class="Maui.Controls.Sample.Issues.Issue22937">
<Shell.ToolbarItems>
<ToolbarItem x:Name="EditButton" Text="Edit"/>
<ToolbarItem x:Name="SaveButton" Text="Save"/>
</Shell.ToolbarItems>
<ShellContent Title="Home page"
Route="MainPage">
<ShellContent.ContentTemplate>
<DataTemplate>
<ContentPage>
<StackLayout>
<Button Text="Change State"
AutomationId="ChangeState"
Clicked="ChangeStateClicked"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage>
</DataTemplate>
</ShellContent.ContentTemplate>
</ShellContent>
</Shell>

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

@ -0,0 +1,20 @@
using System;
using System.Collections.ObjectModel;
using Microsoft.Maui.Controls;
namespace Maui.Controls.Sample.Issues;
[Issue(IssueTracker.Github, 22937, "ToolbarItem font color not updating properly after changing the available state at runtime", PlatformAffected.Android)]
public partial class Issue22937 : Shell
{
public Issue22937()
{
InitializeComponent();
}
private void ChangeStateClicked(object sender, EventArgs e)
{
EditButton.IsEnabled = !EditButton.IsEnabled;
}
}

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

@ -0,0 +1,27 @@
#if ANDROID || WINDOWS
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue10660 : _IssuesUITest
{
public override string Issue => "Inconsistent toolbar text color on interaction";
public Issue10660(TestDevice device)
: base(device)
{ }
[Test]
[Category(UITestCategories.ToolbarItem)]
public void ToolbarTextColorOnInteraction()
{
App.WaitForElement("ChangeText");
App.Tap("ChangeText");
VerifyScreenshot();
}
}
}
#endif

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

@ -0,0 +1,27 @@
#if ANDROID || WINDOWS
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
namespace Microsoft.Maui.TestCases.Tests.Issues
{
public class Issue22937 : _IssuesUITest
{
public override string Issue => "ToolbarItem font color not updating properly after changing the available state at runtime";
public Issue22937(TestDevice device)
: base(device)
{ }
[Test]
[Category(UITestCategories.ToolbarItem)]
public void ToolbarItemFontColorDynamicUpdate()
{
App.WaitForElement("ChangeState");
App.Tap("ChangeState");
VerifyScreenshot();
}
}
}
#endif

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 8.3 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 8.0 KiB

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

@ -63,7 +63,7 @@ namespace Microsoft.Maui.Handlers
if (containerView is WrapperView wrapperView)
{
wrapperView.RemoveFromSuperview();
wrapperView.Dispose();
wrapperView.Disconnect();
}
}
}

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

@ -11,14 +11,14 @@ namespace Microsoft.Maui.Platform
{
InvalidateConstraintsCache();
base.SubviewAdded(uiview);
Superview?.SetNeedsLayout();
this.GetSuperViewIfWindowSet()?.SetNeedsLayout();
}
public override void WillRemoveSubview(UIView uiview)
{
InvalidateConstraintsCache();
base.WillRemoveSubview(uiview);
Superview?.SetNeedsLayout();
this.GetSuperViewIfWindowSet()?.SetNeedsLayout();
}
public override UIView? HitTest(CGPoint point, UIEvent? uievent)

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

@ -140,7 +140,7 @@ namespace Microsoft.Maui.Platform
{
InvalidateConstraintsCache();
base.SetNeedsLayout();
Superview?.SetNeedsLayout();
this.GetSuperViewIfWindowSet()?.SetNeedsLayout();
}
IVisualTreeElement? IVisualTreeElementProvidable.GetElement()

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

@ -266,10 +266,18 @@ namespace Microsoft.Maui.Platform
}
}
internal static UIView? GetSuperViewIfWindowSet(this UIView? view)
{
if (view?.Window is null)
return null;
return view.Superview;
}
public static void InvalidateMeasure(this UIView platformView, IView view)
{
platformView.SetNeedsLayout();
platformView.Superview?.SetNeedsLayout();
platformView.GetSuperViewIfWindowSet()?.SetNeedsLayout();
}
public static void UpdateWidth(this UIView platformView, IView view)

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

@ -105,12 +105,19 @@ namespace Microsoft.Maui.Platform
SetBorder();
}
internal void Disconnect()
{
MaskLayer = null;
BackgroundMaskLayer = null;
ShadowLayer = null;
_borderView?.RemoveFromSuperview();
}
// TODO obsolete or delete this for NET9
public new void Dispose()
{
DisposeClip();
DisposeShadow();
DisposeBorder();
Disconnect();
base.Dispose();
}
@ -158,7 +165,7 @@ namespace Microsoft.Maui.Platform
{
base.SetNeedsLayout();
Superview?.SetNeedsLayout();
this.GetSuperViewIfWindowSet()?.SetNeedsLayout();
}
partial void ClipChanged()
@ -200,12 +207,6 @@ namespace Microsoft.Maui.Platform
backgroundMask.Path = nativePath;
}
void DisposeClip()
{
MaskLayer = null;
BackgroundMaskLayer = null;
}
void SetShadow()
{
var shadowLayer = ShadowLayer;
@ -230,11 +231,6 @@ namespace Microsoft.Maui.Platform
shadowLayer.SetShadow(Shadow);
}
void DisposeShadow()
{
ShadowLayer = null;
}
void SetBorder()
{
if (Border == null)
@ -251,11 +247,6 @@ namespace Microsoft.Maui.Platform
_borderView.UpdateMauiCALayer(Border);
}
void DisposeBorder()
{
_borderView?.RemoveFromSuperview();
}
CALayer? GetLayer()
{
var sublayers = Layer?.Sublayers;

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

@ -19,11 +19,9 @@ namespace Microsoft.Maui.Authentication
/// <param name="webAuthenticatorOptions">A <see cref="WebAuthenticatorOptions"/> instance containing additional configuration for this authentication call.</param>
/// <returns>A <see cref="WebAuthenticatorResult"/> object with the results of this operation.</returns>
/// <exception cref="TaskCanceledException">Thrown when the user canceled the authentication flow.</exception>
/// <exception cref="HttpRequestException">Windows: Thrown when a HTTP Request error occured.</exception>
/// <exception cref="Exception">Windows: Thrown when a unexpected HTTP response was received.</exception>
/// <exception cref="PlatformNotSupportedException">Windows: Thrown when called on Windows.</exception>
/// <exception cref="FeatureNotSupportedException">iOS/macOS: Thrown when iOS version is less than 13 is used or macOS less than 13.1 is used.</exception>
/// <exception cref="InvalidOperationException">
/// <para>Windows: Thrown when the callback custom URL scheme is not registered in the AppxManifest.xml file.</para>
/// <para>Android: Thrown when the no IntentFilter has been created for the callback URL.</para>
/// </exception>
Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions);
@ -76,13 +74,19 @@ namespace Microsoft.Maui.Authentication
/// <summary>Begin an authentication flow by navigating to the specified url and waiting for a callback/redirect to the callbackUrl scheme.</summary>
/// <param name="url"> Url to navigate to, beginning the authentication flow.</param>
/// <param name="callbackUrl"> Expected callback url that the navigation flow will eventually redirect to.</param>
/// <returns>Returns a result parsed out from the callback url.</returns>
/// <returns>Returns a result parsed out from the callback url.</returns>
#if !NETSTANDARD
[System.Runtime.Versioning.UnsupportedOSPlatform("windows")]
#endif
public static Task<WebAuthenticatorResult> AuthenticateAsync(Uri url, Uri callbackUrl)
=> Current.AuthenticateAsync(url, callbackUrl);
/// <summary>Begin an authentication flow by navigating to the specified url and waiting for a callback/redirect to the callbackUrl scheme.The start url and callbackUrl are specified in the webAuthenticatorOptions.</summary>
/// <param name="webAuthenticatorOptions">Options to configure the authentication request.</param>
/// <returns>Returns a result parsed out from the callback url.</returns>
#if !NETSTANDARD
[System.Runtime.Versioning.UnsupportedOSPlatform("windows")]
#endif
public static Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions)
=> Current.AuthenticateAsync(webAuthenticatorOptions);

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

@ -1,64 +1,13 @@
using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using Microsoft.Maui.ApplicationModel;
using Microsoft.Maui.Storage;
using Windows.Security.Authentication.Web;
namespace Microsoft.Maui.Authentication
{
partial class WebAuthenticatorImplementation : IWebAuthenticator
{
public async Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions)
public Task<WebAuthenticatorResult> AuthenticateAsync(WebAuthenticatorOptions webAuthenticatorOptions)
{
var url = webAuthenticatorOptions?.Url;
var callbackUrl = webAuthenticatorOptions?.CallbackUrl;
if (!IsUriProtocolDeclared(callbackUrl.Scheme))
throw new InvalidOperationException($"You need to declare the windows.protocol usage of the protocol/scheme `{callbackUrl.Scheme}` in your AppxManifest.xml file");
try
{
var r = await WebAuthenticationBroker.AuthenticateAsync(WebAuthenticationOptions.None, url, callbackUrl);
switch (r.ResponseStatus)
{
case WebAuthenticationStatus.Success:
// For GET requests this is a URI:
var resultUri = new Uri(r.ResponseData.ToString());
return new WebAuthenticatorResult(resultUri, webAuthenticatorOptions?.ResponseDecoder);
case WebAuthenticationStatus.UserCancel:
throw new TaskCanceledException();
case WebAuthenticationStatus.ErrorHttp:
throw new HttpRequestException("Error: " + r.ResponseErrorDetail);
default:
throw new Exception("Response: " + r.ResponseData.ToString() + "\nStatus: " + r.ResponseStatus);
}
}
catch (FileNotFoundException)
{
throw new TaskCanceledException();
}
}
static bool IsUriProtocolDeclared(string scheme)
{
var docPath = FileSystemUtils.PlatformGetFullAppPackageFilePath(PlatformUtils.AppManifestFilename);
var doc = XDocument.Load(docPath, LoadOptions.None);
var reader = doc.CreateReader();
var namespaceManager = new XmlNamespaceManager(reader.NameTable);
namespaceManager.AddNamespace("x", PlatformUtils.AppManifestXmlns);
namespaceManager.AddNamespace("uap", "http://schemas.microsoft.com/appx/manifest/uap/windows10");
// Check if the protocol was declared
var decl = doc.Root.XPathSelectElements($"//uap:Extension[@Category='windows.protocol']/uap:Protocol[@Name='{scheme}']", namespaceManager);
return decl != null && decl.Any();
throw new PlatformNotSupportedException("This implementation of WebAuthenticator does not support Windows. See https://github.com/microsoft/WindowsAppSDK/issues/441 for more details.");
}
}
}

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

@ -21,14 +21,21 @@ namespace Microsoft.Maui.Essentials.DeviceTests
[Trait(Traits.InteractionType, Traits.InteractionTypes.Human)]
public async Task Redirect(string urlBase, string callbackScheme, string accessToken, string refreshToken, int expires)
{
var r = await WebAuthenticator.AuthenticateAsync(
#pragma warning disable CA1416 // Validate platform compatibility: Not supported on Windows
var authenticationTask = WebAuthenticator.AuthenticateAsync(
new Uri($"{urlBase}?access_token={accessToken}&refresh_token={refreshToken}&expires={expires}"),
new Uri($"{callbackScheme}://"));
#pragma warning restore CA1416 // Validate platform compatibility
#if WINDOWS
var exception = await Assert.ThrowsAsync<PlatformNotSupportedException>(async () => await authenticationTask);
#else
var r = await authenticationTask;
Assert.Equal(accessToken, r?.AccessToken);
Assert.Equal(refreshToken, r?.RefreshToken);
Assert.NotNull(r?.ExpiresIn);
Assert.True(r?.ExpiresIn > DateTime.UtcNow);
#endif
}
[Theory]
@ -42,18 +49,24 @@ namespace Microsoft.Maui.Essentials.DeviceTests
public async Task RedirectWithResponseDecoder(string urlBase, string callbackScheme, string accessToken, string refreshToken, int expires)
{
var responseDecoder = new TestResponseDecoder();
var r = await WebAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions
#pragma warning disable CA1416 // Validate platform compatibility: Not supported on Windows
var authenticationTask = WebAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions
{
Url = new Uri($"{urlBase}?access_token={accessToken}&refresh_token={refreshToken}&expires={expires}"),
CallbackUrl = new Uri($"{callbackScheme}://"),
ResponseDecoder = responseDecoder
});
#pragma warning restore CA1416 // Validate platform compatibility
#if WINDOWS
var exception = await Assert.ThrowsAsync<PlatformNotSupportedException>(async () => await authenticationTask);
#else
var r = await authenticationTask;
Assert.Equal(accessToken, r?.AccessToken);
Assert.Equal(refreshToken, r?.RefreshToken);
Assert.NotNull(r?.ExpiresIn);
Assert.True(r?.ExpiresIn > DateTime.UtcNow);
Assert.Equal(1, responseDecoder.CallCount);
#endif
}
[Theory]