TextBlock复杂的使用,请参考https://www.cnblogs.com/dalgleish/p/18995027

由于Avalonia没有内置Hyperlink,所以我们自己实现一个就行了。老规矩,留了一个坑,大家可以增加功能实现超链接。

Hyperlink类,移动端需要自己单独实现。(这个版本之后会被弃用)

 public class Hyperlink : InlineUIContainer
 {
     private readonly TextBlock textBlock;
     public event EventHandler<PointerPressedEventArgs>? Click;


     public static readonly StyledProperty<string> TextProperty =
         AvaloniaProperty.Register<Hyperlink, string>(
             nameof(Text), defaultBindingMode: BindingMode.TwoWay);

     public string Text
     {
         get => GetValue(TextProperty);
         set => SetValue(TextProperty, value);
     }

     public Hyperlink()
     {
         textBlock = new TextBlock
         {
             TextDecorations = new TextDecorationCollection
             {
                 new TextDecoration { Location = TextDecorationLocation.Underline }
             },
             Foreground = Brushes.Blue,
             Cursor = new Cursor(StandardCursorType.Hand)
         };

         // 绑定 Text 属性
         this.GetObservable(TextProperty).Subscribe(t => textBlock.Text = t ?? string.Empty);

         textBlock.PointerPressed += OnPointerPressed;

         this.Child = textBlock;
     }

     private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
     {
         Click?.Invoke(this, e);
     }
     private bool IsValidHttpUrl()
     {
         if (Uri.TryCreate(Text, UriKind.Absolute, out var uri))
         {
             return uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps;
         }
         return false;
     }
     public void Navigate()
     {
         if (!IsValidHttpUrl())
             return;

         try
         {
             // Windows / Linux / macOS 通用跳转方式
             ProcessStartInfo psi = new ProcessStartInfo
             {
                 FileName = Text,
                 UseShellExecute = true
             };
             Process.Start(psi);
         }
         catch (Exception ex)
         {
             Console.WriteLine($"无法打开链接{Text}: {ex.Message}");
         }
     }
 }

进阶版本(内嵌浏览器,浏览器WebBrowser类会在之后的章节里给出,项目也会改成用NavigationService进行导航,此时可以暂时不用学习这个进阶版本)

NavigationService类

using System;
using System.Collections.Concurrent;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Threading;

namespace Shares.Avalonia;

public static class NavigationService
{
    private sealed class Relation
    {
        public object? Host;
        public object? Parent;
    }

    private static readonly ConcurrentDictionary<(Assembly assembly, string pageName), Type?> cache = new();
    private static readonly ConditionalWeakTable<object, Relation> relationMap = new();

    public static void Navigate(object current, string pageAxaml, bool fNewWindow = true, params object?[] ctorArgs)
    {
        try
        {
            if (current is null)
                return;

            var hostWindow = ResolveHostWindow(current);
            if (hostWindow is null)
                return;

            var ownerWindow = ResolveOwnerWindowForModal(current) ?? hostWindow;
            if (ownerWindow is null)
                return;

            var pageName = NormalizePageName(pageAxaml);
            if (string.IsNullOrWhiteSpace(pageName))
                return;

            var targetType = ResolvePageType(hostWindow, pageName);
            if (targetType is null)
                return;

            var instance = CreateInstance(targetType, ctorArgs);
            if (instance is null)
                return;

            if (fNewWindow)
            {
                _ = ShowModalLikeMessageBoxAsync(ownerWindow, instance);
                return;
            }

            _ = Dispatcher.UIThread.InvokeAsync(() =>
                ReplaceFromCurrent(current, instance, hostWindow));
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[NavigationService.Navigate] 导航主流程异常: {ex}");
        }
    }

    private static object? CreateInstance(Type targetType, object?[] ctorArgs)
    {
        try
        {
            if (ctorArgs is { Length: > 0 })
                return Activator.CreateInstance(targetType, ctorArgs);

            return Activator.CreateInstance(targetType);
        }
        catch (Exception ex)
        {
            Console.WriteLine(
                $"[NavigationService.CreateInstance] 创建实例失败: {targetType.FullName}, args={ctorArgs?.Length ?? 0}, ex={ex}");
            return null;
        }
    }

    private static Window? ResolveHostWindow(object current)
    {
        try
        {
            if (current is Window w)
                return w;

            if (current is Control c)
                return TopLevel.GetTopLevel(c) as Window;

            if (TryGetHost(current, out var host) && host is Window hw)
                return hw;

            if (TryGetParent(current, out var parent))
                return ResolveHostWindow(parent);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ResolveHostWindow] 推导 HostWindow 失败: {ex}");
        }

        return null;
    }

    private static Window? ResolveOwnerWindowForModal(object current)
    {
        try
        {
            var main = GetMainWindow();

            if (current is Window w)
            {
                if (w.IsVisible)
                    return w;

                if (main is not null && main.IsVisible)
                    return main;

                return w;
            }

            if (current is Control c)
            {
                var top = TopLevel.GetTopLevel(c) as Window;
                if (top is not null && top.IsVisible)
                    return top;

                if (main is not null && main.IsVisible)
                    return main;

                return top ?? main;
            }

            if (TryGetHost(current, out var host) && host is Window hw)
                return hw.IsVisible ? hw : (main?.IsVisible == true ? main : hw);

            if (TryGetParent(current, out var parent))
                return ResolveOwnerWindowForModal(parent);

            return main;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ResolveOwnerWindowForModal] 推导 OwnerWindow 失败: {ex}");
            return null;
        }
    }

    private static Window? GetMainWindow()
    {
        try
        {
            if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
                return desktop.MainWindow;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[GetMainWindow] 获取 MainWindow 失败: {ex}");
        }

        return null;
    }

    private static string NormalizePageName(string pageAxaml)
    {
        try
        {
            var name = pageAxaml?.Trim() ?? string.Empty;
            if (name.Length == 0)
                return string.Empty;

            name = Path.GetFileName(name);

            if (name.EndsWith(".axaml", true, CultureInfo.InvariantCulture))
                name = Path.GetFileNameWithoutExtension(name);

            return name;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[NormalizePageName] 解析页面名失败: {pageAxaml}, ex={ex}");
            return string.Empty;
        }
    }

    private static Type? ResolvePageType(Window hostWindow, string pageName)
    {
        try
        {
            var t = ResolveType(hostWindow.GetType().Assembly, pageName);
            if (t is not null)
                return t;

            t = ResolveType(Assembly.GetEntryAssembly(), pageName);
            if (t is not null)
                return t;

            t = ResolveType(typeof(NavigationService).Assembly, pageName);
            if (t is not null)
                return t;

            return ResolvePageTypeAllAssemblies(pageName);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ResolvePageType] 查找页面类型失败: {pageName}, ex={ex}");
            return null;
        }
    }

    private static Type? ResolvePageTypeAllAssemblies(string pageName)
    {
        try
        {
            foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
            {
                // 跳过动态程序集/反射可能抛异常的情况由 ResolveType 内部处理
                var t = ResolveType(asm, pageName);
                if (t is not null)
                    return t;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ResolvePageTypeAllAssemblies] 扫描所有程序集失败: page={pageName}, ex={ex}");
        }

        return null;
    }


    private static Type? ResolveType(Assembly? assembly, string pageName)
    {
        if (assembly is null)
            return null;

        return cache.GetOrAdd((assembly, pageName), key =>
        {
            try
            {
                return key.assembly
                    .GetTypes()
                    .FirstOrDefault(t =>
                        string.Equals(t.Name, key.pageName, StringComparison.OrdinalIgnoreCase));
            }
            catch (Exception ex)
            {
                Console.WriteLine(
                    $"[ResolveType] 扫描程序集失败: {assembly.FullName}, page={pageName}, ex={ex}");
                return null;
            }
        });
    }

    private static async Task ShowModalLikeMessageBoxAsync(Window owner, object instance)
    {
        try
        {
            var realOwner = ResolveBestOwnerForModal(owner);
            if (realOwner is null)
                return;

            if (!realOwner.IsVisible)
            {
                var ok = await WaitForOwnerVisibleWithTimeout(realOwner, 2000);
                if (!ok || !realOwner.IsVisible)
                {
                    var main = GetMainWindow();
                    if (main is not null && main.IsVisible)
                        realOwner = main;
                    else
                    {
                        await Dispatcher.UIThread.InvokeAsync(
                            () => ShowNonModalFallback(instance));
                        return;
                    }
                }
            }

            await Dispatcher.UIThread.InvokeAsync(
                () => ShowDialogCore(realOwner, instance));
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ShowModalLikeMessageBoxAsync] 显示模态窗口失败: {ex}");
        }
    }

    private static Window? ResolveBestOwnerForModal(Window owner)
    {
        try
        {
            if (owner.IsVisible)
                return owner;

            var main = GetMainWindow();
            if (main is not null && main.IsVisible)
                return main;

            return owner;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ResolveBestOwnerForModal] 选择 Owner 失败: {ex}");
            return null;
        }
    }

    private static void ShowDialogCore(Window owner, object instance)
    {
        try
        {
            if (!owner.IsVisible)
                return;

            if (instance is Window window)
            {
                window.Icon = owner.Icon;
                window.WindowStartupLocation = WindowStartupLocation.CenterOwner;
                _ = window.ShowDialog(owner);
                return;
            }

            var content = EnsureControl(instance);
            if (content is null)
                return;

            var hostWindow = new Window
            {
                Icon = owner.Icon,
                WindowStartupLocation = WindowStartupLocation.CenterOwner,
                Content = content
            };

            _ = hostWindow.ShowDialog(owner);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ShowDialogCore] ShowDialog 执行失败: {ex}");
        }
    }

    private static void ShowNonModalFallback(object instance)
    {
        try
        {
            if (instance is Window w)
            {
                w.Show();
                return;
            }

            var content = EnsureControl(instance);
            if (content is null)
                return;

            new Window { Content = content }.Show();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ShowNonModalFallback] 非模态显示失败: {ex}");
        }
    }

    private static async Task<bool> WaitForOwnerVisibleWithTimeout(Window owner, int timeoutMs)
    {
        try
        {
            if (owner.IsVisible)
                return true;

            var tcs = new TaskCompletionSource<bool>();

            void OnOpened(object? s, EventArgs e)
            {
                owner.Opened -= OnOpened;
                tcs.TrySetResult(true);
            }

            owner.Opened += OnOpened;

            var delay = Task.Delay(timeoutMs);
            var done = await Task.WhenAny(tcs.Task, delay);
            return done == tcs.Task && owner.IsVisible;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[WaitForOwnerVisibleWithTimeout] 等待 Owner 可见失败: {ex}");
            return false;
        }
    }

    private static Control? EnsureControl(object instance)
    {
        try
        {
            if (instance is Window w)
                return w.Content as Control;

            if (instance is Control c)
                return c;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[EnsureControl] 提取 Control 失败: {ex}");
        }

        return null;
    }

    private static void ReplaceFromCurrent(object current, object instance, Window fallbackHostWindow)
    {
        try
        {
            var content = EnsureControl(instance);
            if (content is null)
                return;

            if (TryGetHost(current, out var cachedHost) &&
                TryReplaceOnHost(cachedHost, content))
            {
                CacheRelations(current, instance, content, cachedHost);
                return;
            }

            object? host = current switch
            {
                Window w => w,
                ContentControl cc => cc,
                Control c => FindNearestContentHost(c),
                _ => null
            };

            if (host is null && TryGetParent(current, out var parent))
            {
                if (TryGetHost(parent, out var parentHost))
                    host = parentHost;
                else if (parent is Control pc)
                    host = FindNearestContentHost(pc);
                else if (parent is Window pw)
                    host = pw;
            }

            host ??= fallbackHostWindow;

            if (TryReplaceOnHost(host, content))
                CacheRelations(current, instance, content, host);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[ReplaceFromCurrent] 替换 Content 失败: {ex}");
        }
    }

    private static void CacheRelations(object current, object instance, Control content, object host)
    {
        CacheHost(current, host);
        CacheHost(instance, host);
        CacheHost(content, host);

        CacheParent(instance, current);
        CacheParent(content, current);
    }

    private static bool TryReplaceOnHost(object host, Control content)
    {
        try
        {
            if (host is Window w)
            {
                w.Content = content;
                return true;
            }

            if (host is ContentControl cc)
            {
                cc.Content = content;
                return true;
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[TryReplaceOnHost] 写入 Content 失败: {ex}");
        }

        return false;
    }

    private static object? FindNearestContentHost(Control start)
    {
        try
        {
            Control? cur = start;

            while (cur != null)
            {
                if (cur is ContentControl or Window)
                    return cur;

                cur = cur.Parent as Control;
            }

            if (TryGetParent(start, out var parent))
            {
                if (parent is Control pc)
                    return FindNearestContentHost(pc);

                if (parent is Window pw)
                    return pw;
            }

            return TopLevel.GetTopLevel(start) as Window;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[FindNearestContentHost] 查找 Host 失败: {ex}");
            return null;
        }
    }

    private static Relation GetOrCreateRelation(object key)
    {
        return relationMap.GetValue(key, _ => new Relation());
    }

    private static bool TryGetHost(object key, out object host)
    {
        if (relationMap.TryGetValue(key, out var relation) &&
            relation.Host is not null)
        {
            host = relation.Host;
            return true;
        }

        host = null!;
        return false;
    }

    private static void CacheHost(object key, object host)
    {
        GetOrCreateRelation(key).Host = host;
    }

    private static void CacheParent(object child, object parent)
    {
        GetOrCreateRelation(child).Parent = parent;
    }

    private static bool TryGetParent(object child, out object parent)
    {
        if (relationMap.TryGetValue(child, out var relation) &&
            relation.Parent is not null)
        {
            parent = relation.Parent;
            return true;
        }

        parent = null!;
        return false;
    }
}
    public class Hyperlink : InlineUIContainer
    {
        public sealed class WebBrowserPage : UserControl
        {
            public WebBrowserPage(string url)
            {
                Content = new CustomControls.WebBrowser
                {
                    Address = url,
                    HomeUrl = url
                };
            }
        }

        private readonly TextBlock textBlock;
        private ContextMenu? contextMenu;

        public event EventHandler<PointerPressedEventArgs>? Click;

        public static readonly StyledProperty<string?> TextProperty =
            AvaloniaProperty.Register<Hyperlink, string?>(nameof(Text));

        public string? Text
        {
            get => GetValue(TextProperty);
            set => SetValue(TextProperty, value);
        }

        public static readonly StyledProperty<string?> ContentTextProperty =
            AvaloniaProperty.Register<Hyperlink, string?>(nameof(ContentText));

        [Content]
        public string? ContentText
        {
            get => GetValue(ContentTextProperty);
            set => SetValue(ContentTextProperty, value);
        }

        public static readonly StyledProperty<string?> NavigateUriProperty =
            AvaloniaProperty.Register<Hyperlink, string?>(nameof(NavigateUri));

        public string? NavigateUri
        {
            get => GetValue(NavigateUriProperty);
            set => SetValue(NavigateUriProperty, value);
        }

        // 语义统一:
        // http(s):true=外部浏览器,false=内置 WebBrowser
        // 非 http(s):true/false 直接传给 NavigationService
        public static readonly StyledProperty<bool> OpenInNewWindowProperty =
            AvaloniaProperty.Register<Hyperlink, bool>(nameof(OpenInNewWindow), true);

        public bool OpenInNewWindow
        {
            get => GetValue(OpenInNewWindowProperty);
            set => SetValue(OpenInNewWindowProperty, value);
        }

        // 内置 WebBrowser 承载页名(Type.Name)
        public static readonly StyledProperty<string> WebBrowserPageNameProperty =
            AvaloniaProperty.Register<Hyperlink, string>(
                nameof(WebBrowserPageName),
                nameof(WebBrowserPage));

        public string WebBrowserPageName
        {
            get => GetValue(WebBrowserPageNameProperty);
            set => SetValue(WebBrowserPageNameProperty, value);
        }

        public static readonly StyledProperty<double> OpacityProperty =
            AvaloniaProperty.Register<Hyperlink, double>(nameof(Opacity), 1.0);

        public double Opacity
        {
            get => GetValue(OpacityProperty);
            set => SetValue(OpacityProperty, value);
        }

        public Hyperlink()
        {
            textBlock = new TextBlock
            {
                TextDecorations = new TextDecorationCollection
            {
                new TextDecoration { Location = TextDecorationLocation.Underline }
            },
                Foreground = Brushes.Blue,
                Cursor = new Cursor(StandardCursorType.Hand),
                Margin = new Thickness(0, 0, 0, -2),
                Transitions = new Transitions
            {
                new DoubleTransition
                {
                    Property = OpacityProperty,
                    Duration = TimeSpan.FromSeconds(0.5),
                    Easing = new SineEaseInOut()
                }
            }
            };

            this.GetObservable(TextProperty).Subscribe(_ => UpdateText());
            this.GetObservable(ContentTextProperty).Subscribe(_ => UpdateText());

            textBlock[!!TextBlock.OpacityProperty] = this[!!OpacityProperty];

            textBlock.PointerPressed += OnPointerPressed;
            textBlock.PointerReleased += OnPointerReleased;

            Child = textBlock;

            UpdateText();
        }

        private void UpdateText()
        {
            if (!string.IsNullOrWhiteSpace(Text))
            {
                textBlock.Text = Text;
                return;
            }

            if (!string.IsNullOrWhiteSpace(ContentText))
            {
                textBlock.Text = ContentText;
                return;
            }

            textBlock.Text = string.Empty;
        }

        private void OnPointerPressed(object? sender, PointerPressedEventArgs e)
        {
            Click?.Invoke(this, e);

            if (!e.GetCurrentPoint(textBlock).Properties.IsRightButtonPressed)
                return;

            var target = NavigateUri;
            if (string.IsNullOrWhiteSpace(target))
                return;

            if (!TryNormalizeHttpUrl(target, out _))
                return;

            ShowContextMenu();
            e.Handled = true;
        }

        private void OnPointerReleased(object? sender, PointerReleasedEventArgs e)
        {
            if (e.InitialPressMouseButton != MouseButton.Left)
                return;

            HandleNavigateFromClick(e.KeyModifiers);
            e.Handled = true;
        }

        private void HandleNavigateFromClick(KeyModifiers modifiers)
        {
            var target = NavigateUri;
            if (string.IsNullOrWhiteSpace(target))
                return;

            var ctrl = (modifiers & KeyModifiers.Control) != 0;

            if (TryNormalizeHttpUrl(target, out var httpUrl))
            {
                if (ctrl)
                {
                    TryOpenExternalUrl(httpUrl);
                    return;
                }

                if (OpenInNewWindow)
                {
                    TryOpenExternalUrl(httpUrl);
                    return;
                }

                NavigationService.Navigate(
                    textBlock,
                    WebBrowserPageName,
                    fNewWindow: false,
                    ctorArgs: new object?[] { httpUrl });

                return;
            }

            var openNew = ctrl || OpenInNewWindow;
            NavigationService.Navigate(textBlock, target, openNew);
        }

        private void ShowContextMenu()
        {
            var target = NavigateUri;
            if (string.IsNullOrWhiteSpace(target))
                return;

            if (!TryNormalizeHttpUrl(target, out _))
                return;

            if (contextMenu == null)
                contextMenu = BuildContextMenu();

            contextMenu.PlacementTarget = textBlock;
            contextMenu.Open(textBlock);
        }

        private ContextMenu BuildContextMenu()
        {
            var copyItem = new MenuItem
            {
                Header = MenuText("Copy link")
            };
            copyItem.Click += async (_, __) =>
            {
                var target = NavigateUri;
                if (string.IsNullOrWhiteSpace(target))
                    return;

                var top = TopLevel.GetTopLevel(textBlock);
                var cb = top?.Clipboard;
                if (cb == null)
                    return;

                await cb.SetTextAsync(target);
            };

            var openExternalItem = new MenuItem
            {
                Header = MenuText("Open in new window")
            };
            openExternalItem.Click += (_, __) =>
            {
                var target = NavigateUri;
                if (TryNormalizeHttpUrl(target ?? "", out var httpUrl))
                    TryOpenExternalUrl(httpUrl);
            };

            var openEmbeddedItem = new MenuItem
            {
                Header = MenuText("Open embedded")
            };
            openEmbeddedItem.Click += (_, __) =>
            {
                var target = NavigateUri;
                if (TryNormalizeHttpUrl(target ?? "", out var httpUrl))
                {
                    NavigationService.Navigate(
                        textBlock,
                        WebBrowserPageName,
                        fNewWindow: false,
                        ctorArgs: new object?[] { httpUrl });
                }
            };

            return new ContextMenu
            {
                Items =
            {
                copyItem,
                new Separator(),
                openExternalItem,
                openEmbeddedItem
            }
            };
        }

        private static TextBlock MenuText(string text)
        {
            return new TextBlock
            {
                Text = text,
                TextDecorations = null
            };
        }

        private static bool TryNormalizeHttpUrl(string target, out string normalized)
        {
            normalized = "";

            if (!Uri.TryCreate(target, UriKind.Absolute, out var uri))
                return false;

            if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps)
                return false;

            normalized = uri.ToString();
            return true;
        }

        private static void TryOpenExternalUrl(string target)
        {
            try
            {
                Process.Start(new ProcessStartInfo
                {
                    FileName = target,
                    UseShellExecute = true
                });
            }
            catch
            {
            }
        }
    }

PopupTest.axaml代码

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Height="300" Width="300"
        x:Class="AvaloniaUI.PopupTest"
        Title="PopupTest">
    <Grid Margin="10">
        <TextBlock TextWrapping="Wrap">
            You can use a Popup to provide a link for
            a specific  
        <Hyperlink Text="term" Click="run_MouseClicked"/> 
            of interest.
        </TextBlock>
        <Popup Name="popLink" IsOpen="False" Placement="Pointer" IsLightDismissEnabled="True">
            <Border Background="White" CornerRadius="5" Padding="10">
                <Border.Transitions>
                    <Transitions>
                        <DoubleTransition Property="Opacity" Duration="0:0:0.5" />
                    </Transitions>
                </Border.Transitions>
                <TextBlock TextWrapping="Wrap">
                    Welcome to C#  Avalonia: 
                    <Hyperlink Text="https://www.cnblogs.com/dalgleish" Click="Hyperlink_Click"></Hyperlink>
                </TextBlock>
            </Border>
        </Popup>
    </Grid>
</Window>

PopupTest.axaml.cs代码

using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Documents;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Shares.Avalonia;

namespace AvaloniaUI;

public partial class PopupTest : Window
{
    public PopupTest()
    {
        InitializeComponent();
    }

    private void run_MouseClicked(object? sender, PointerPressedEventArgs e)
    {
        popLink.IsOpen = true;
    }

    private void Hyperlink_Click(object? sender, PointerPressedEventArgs e)
    {
        ((Hyperlink?)sender)?.Navigate();
    }
}

运行效果

 

posted on 2025-07-21 12:36  dalgleish  阅读(118)  评论(0)    收藏  举报