You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MAUI自定义Markup扩展首次调用在亚马逊Fire平板崩溃求助

MAUI自定义本地化标记扩展在亚马逊Fire平板崩溃问题

在MAUI项目中,我通过现有标记扩展LocalizationResourceManager管理文本本地化,为了根据应用特定数据动态切换本地化键,基于它实现了自定义扩展DynamicTranslateExtension,同时配合单例DynamicTranslator在语言变化时触发XAML更新。

实现代码

namespace MyApp.Extensions
{
    [ContentProperty(nameof(PrefixKey))]
    public class DynamicTranslateExtension : IMarkupExtension<BindingBase>
    {
        public string PrefixKey { get; set; }

        // 指定从AppData中获取后缀的属性类型
        public string SuffixType { get; set; }

        public BindingBase ProvideValue(IServiceProvider serviceProvider)
        {
            // 返回Binding而非字符串,以便语言变化时触发刷新
            return new Binding
            {
                Source = DynamicTranslator.Instance,
                Path = $"[{PrefixKey},{SuffixType}]",
                Mode = BindingMode.OneWay
            };
        }

        object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider) => ProvideValue(serviceProvider);
    }

    public class DynamicTranslator : INotifyPropertyChanged
    {
        #region 单例实现
        private static DynamicTranslator m_Singleton;
        public static DynamicTranslator Instance
        {
            get
            {
                m_Singleton ??= new DynamicTranslator();
                return m_Singleton;
            }
        }

        public DynamicTranslator()
        {
            WeakReferenceMessenger.Default.Register<LangChangedMessage>(this, (o, m) => Invalidate());
        }
        #endregion

        public string this[string compositeKey]
        {
            get
            {
                var parts = compositeKey.Split(',');
                var prefixKey = parts[0];
                var suffixType = parts.Length > 1 ? parts[1] : "";
                return GetDynamicTranslation(prefixKey, suffixType);
            }
        }

        public static string GetDynamicTranslation(string prefixKey, string suffixType)
        {
            string finalKey;

            if (string.IsNullOrEmpty(prefixKey))
            {
                finalKey = "!InvalidKey!";
            }
            else
            {
                var ruleset = AppData.CurrentCompetition.Ruleset;
                string suffix = "";

                if (suffixType == "Group")
                {
                    suffix = (ruleset.GroupDenomination);
                }

                finalKey = $"{prefixKey}_{suffix}";
            }

            try
            {
                var translation = App.LocalizationResourceManager[finalKey];
                return translation;
            }
            catch
            {
                return $"!{finalKey}!";
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void Invalidate()
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
        }
    }
}

XAML使用方式

LabelText="{UBext:DynamicTranslate GroupSelect,SuffixType='Group'}"

根据ruleset.GroupDenomination的值(如"Squad"或"Relay"),会在LocalizationResourceManager中查询GroupSelect_Squad或GroupSelect_Relay对应的本地化文本。

崩溃异常信息

[mono-rt] [ERROR] FATAL UNHANDLED EXCEPTION: System.NullReferenceException: Object reference not set to an instance of an object.

[mono-rt]    at Microsoft.Maui.Controls.BindingExpression.BindingExpressionPart.TryGetValue(Object source, Object& value) in /_/src/Controls/src/Core/BindingExpression.cs:line 737
[mono-rt]    at Microsoft.Maui.Controls.BindingExpression.ApplyCore(Object sourceObject, BindableObject target, BindableProperty property, Boolean fromTarget, SetterSpecificity specificity) in /_/src/Controls/src/Core/BindingExpression.cs:line 166
[mono-rt]    at Microsoft.Maui.Controls.BindingExpression.Apply(Object sourceObject, BindableObject target, BindableProperty property, SetterSpecificity specificity) in /_/src/Controls/src/Core/BindingExpression.cs:line 94
[mono-rt]    at Microsoft.Maui.Controls.Binding.Apply(Object context, BindableObject bindObj, BindableProperty targetProperty, Boolean fromBindingContextChanged, SetterSpecificity specificity) in /_/src/Controls/src/Core/Binding.cs:line 150
[mono-rt]    at Microsoft.Maui.Controls.BindableObject.SetBinding(BindableProperty targetProperty, BindingBase binding, SetterSpecificity specificity) in /_/src/Controls/src/Core/BindableObject.cs:line 335
[mono-rt]    at Microsoft.Maui.Controls.BindableObject.SetBinding(BindableProperty targetProperty, BindingBase binding) in /_/src/Controls/src/Core/BindableObject.cs:line 289
[mono-rt]    at Microsoft.Maui.Controls.Xaml.ApplyPropertiesVisitor.TrySetBinding(Object element, BindableProperty property, String localName, Object value, IXmlLineInfo lineInfo, BindingBase& binding, Exception& exception) in /_/src/Controls/src/Xaml/ApplyPropertiesVisitor.cs:line 558
[mono-rt]    at Microsoft.Maui.Controls.Xaml.ApplyPropertiesVisitor.TrySetPropertyValue(Object element, XmlName propertyName, String xKey, Object value, Object rootElement, IXmlLineInfo lineInfo, IServiceProvider serviceProvider, Exception& xpe) in /_/src/Controls/src/Xaml/ApplyPropertiesVisitor.cs:line 396
[mono-rt]    at Microsoft.Maui.Controls.Xaml.ApplyPropertiesVisitor.SetPropertyValue(Object xamlelement, XmlName propertyName, Object value, Object rootElement, INode node, HydrationContext context, IXmlLineInfo lineInfo) in /_/src/Controls/src/Xaml/ApplyPropertiesVisitor.cs:line 358
[mono-rt]    at Microsoft.Maui.Controls.Xaml.ApplyPropertiesVisitor.Visit(ElementNode node, INode parentNode) in /_/src/Controls/src/Xaml/ApplyPropertiesVisitor.cs:line 125
[mono-rt]    at Microsoft.Maui.Controls.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode) in /_/src/Controls/src/Xaml/XamlNode.cs:line 189
[mono-rt]    at Microsoft.Maui.Controls.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode) in /_/src/Controls/src/Xaml/XamlNode.cs:line 183
[mono-rt]    at Microsoft.Maui.Controls.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode) in /_/src/Controls/src/Xaml/XamlNode.cs:line 183
[mono-rt]    at Microsoft.Maui.Controls.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode) in /_/src/Controls/src/Xaml/XamlNode.cs:line 185
[mono-rt]    at Microsoft.Maui.Controls.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode) in /_/src/Controls/src/Xaml/XamlNode.cs:line 185
[mono-rt]    at Microsoft.Maui.Controls.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode) in /_/src/Controls/src/Xaml/XamlNode.cs:line 185
[mono-rt]    at Microsoft.Maui.Controls.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode) in /_/src/Controls/src/Xaml/XamlNode.cs:line 185
[mono-rt]    at Microsoft.Maui.Controls.Xaml.ElementNode.Accept(IXamlNodeVisitor visitor, INode parentNode) in /_/src/Controls/src/Xaml/XamlNode.cs:line 185
[mono-rt]    at Microsoft.Maui.Controls.Xaml.RootNode.Accept(IXamlNodeVisitor visitor, INode parentNode) in /_/src/Controls/src/Xaml/XamlNode.cs:line 242
[mono-rt]    at Microsoft.Maui.Controls.Xaml.XamlLoader.Visit(RootNode rootnode, HydrationContext visitorContext, Boolean useDesignProperties) in /_/src/Controls/src/Xaml/XamlLoader.cs:line 216
[mono-rt]    at Microsoft.Maui.Controls.Xaml.XamlLoader.Load(Object view, String xaml, Assembly rootAssembly, Boolean useDesignProperties) in /_/src/Controls/src/Xaml/XamlLoader.cs:line 82
[mono-rt]    at Microsoft.Maui.Controls.Xaml.XamlLoader.Load(Object view, String xaml, Boolean useDesignProperties) in /_/src/Controls/src/Xaml/XamlLoader.cs:line 57
[mono-rt]    at Microsoft.Maui.Controls.Xaml.XamlLoader.Load(Object view, Type callingType) in /_/src/Controls/src/Xaml/XamlLoader.cs:line 53
[mono-rt]    at Microsoft.Maui.Controls.Xaml.Extensions.LoadFromXaml[CompetitionPage](CompetitionPage view, Type callingType) in /_/src/Controls/src/Xaml/ViewExtensions.cs:line 42
[mono-rt]    at UBScoring.CompetitionPage.InitializeComponent() in C:\Users\gregoire.chapeaux_ap\Local\ApexO Projects\MAUI Projects\UBApps\UBScoring\obj\Debug\net9.0-android\Microsoft.Maui.Controls.SourceGen\Microsoft.Maui.Controls.SourceGen.CodeBehindGenerator\Views_CompetitionPage.xaml.sg.cs:line 55
[mono-rt]    at UBScoring.CompetitionPage..ctor(CompetitionViewModel vm, ILocalizationResourceManager _localizationResourceManager) in C:\Users\gregoire.chapeaux_ap\Local\ApexO Projects\MAUI Projects\UBApps\UBScoring\Views\CompetitionPage.xaml.cs:line 23
[mono-rt]    at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Constructor(Object obj, IntPtr* args)
[mono-rt]    at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
[mono-rt]    at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
[mono-rt]    at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=9.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSiteMain(ServiceCallSite callSite, RuntimeResolverContext argument)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite callSite, RuntimeResolverContext context)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.RuntimeResolverContext, Microsoft.Extensions.DependencyInjection, Version=9.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[System.Object, System.Private.CoreLib, Version=9.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].VisitCallSite(ServiceCallSite callSite, RuntimeResolverContext argument)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceProvider.CreateServiceAccessor(ServiceIdentifier serviceIdentifier)
[mono-rt]    at System.Collections.Concurrent.ConcurrentDictionary`2[[Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceIdentifier, Microsoft.Extensions.DependencyInjection, Version=9.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.Extensions.DependencyInjection.ServiceProvider.ServiceAccessor, Microsoft.Extensions.DependencyInjection, Version=9.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].GetOrAdd(ServiceIdentifier key, Func`2 valueFactory)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
[mono-rt]    at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
[mono-rt]    at Microsoft.Maui.MauiContext.WrappedServiceProvider.GetService(Type serviceType) in /_/src/Core/src/MauiContext.cs:line 72
[mono-rt]    at Microsoft.Maui.Controls.ShellContent.<>c__DisplayClass20_0.<Microsoft.Maui.Controls.IShellContentController.GetOrCreateContent>b__0() in /_/src/Controls/src/Core/Shell/ShellContent.cs:line 78
[mono-rt]    at Microsoft.Maui.Controls.ElementTemplate.CreateContent() in /_/src/Controls/src/Core/ElementTemplate.cs:line 87
[mono-rt]    at Microsoft.Maui.Controls.Internals.DataTemplateExtensions.CreateContent(DataTemplate self, Object item, BindableObject container) in /_/src/Controls/src/Core/DataTemplateExtensions.cs:line 23
[mono-rt]    at Microsoft.Maui.Controls.ShellContent.Microsoft.Maui.Controls.IShellContentController.GetOrCreateContent() in /_/src/Controls/src/Core/Shell/ShellContent.cs:line 91
[mono-rt]    at Microsoft.Maui.Controls.Platform.Compatibility.ShellSectionRenderer.OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) in /_/src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellSectionRenderer.cs:line 124
[mono-rt]    at AndroidX.Fragment.App.Fragment.n_OnCreateView_Landroid_view_LayoutInflater_Landroid_view_ViewGroup_Landroid_os_Bundle_(IntPtr jnienv, IntPtr native__this, IntPtr native_inflater, IntPtr native_container, IntPtr native_savedInstanceState) in D:\a\_work\1\s\generated\androidx.fragment.fragment\obj\Release\net8.0-android\generated\src\AndroidX.Fragment.App.Fragment.cs:line 2045
[mono-rt]    at Android.Runtime.JNINativeWrapper.Wrap_JniMarshal_PPLLL_L(_JniMarshal_PPLLL_L callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, IntPtr p1, IntPtr p2) in /Users/runner/work/1/s/xamarin-android/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs:line 363
[BpBinder] onLastStrongRef automatically unlinking death recipients: <uncached descriptor>

问题原因与修复方案

原因分析

从异常栈和代码来看,崩溃的核心原因是页面初始化时AppData.CurrentCompetition或其Ruleset属性为null,导致GetDynamicTranslation方法中访问ruleset.GroupDenomination时触发空引用异常。亚马逊Fire平板的页面初始化时机可能比其他设备/模拟器更早,此时应用数据还未完成加载,而其他设备的初始化顺序刚好避开了这个问题。

修复步骤

  1. 给数据访问添加空值防护
    修改GetDynamicTranslation方法,增加对AppData.CurrentCompetition和Ruleset的空值检查,避免直接访问null对象的属性:

    public static string GetDynamicTranslation(string prefixKey, string suffixType)
    {
        string finalKey;
    
        if (string.IsNullOrEmpty(prefixKey))
        {
            finalKey = "!InvalidKey!";
        }
        else
        {
            // 检查当前竞赛和规则集是否存在
            var currentCompetition = AppData.CurrentCompetition;
            if (currentCompetition == null || currentCompetition.Ruleset == null)
            {
                // 返回默认占位符或默认键的翻译
                return $"!{prefixKey}_Default!";
            }
            
            var ruleset = currentCompetition.Ruleset;
            string suffix = "";
    
            if (suffixType == "Group")
            {
                // 额外防护GroupDenomination为null的情况
                suffix = ruleset.GroupDenomination ?? "";
            }
    
            finalKey = $"{prefixKey}_{suffix}";
        }
    
        try
        {
            // 防护LocalizationResourceManager为null的情况
            var translation = App.LocalizationResourceManager?[finalKey] ?? $"!{finalKey}!";
            return translation;
        }
        catch
        {
            return $"!{finalKey}!";
        }
    }
    
  2. **给绑定

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 00:25:16