Xamarin 表单 PropertyChanged object 为空
Xamarin forms PropertyChanged object is null
所以我已经查看了很多已经在 Whosebug 中 post 的答案,但我似乎找不到一个适用的答案。
首先,让我解释一下我的 Xamarin Forms 应用程序。我有传统的解决方案结构(.Net Standard 2.0 class 库与 Andoid 和 iOS 应用程序项目)。我正在处理的应用程序是一套应用程序的一部分,该应用程序通过附加的 .Net 2.0 标准 class 库共享大量代码(header、页脚、错误处理等)。在这个 class 中,我有一个用于异常处理的视图模型以及一个标准的 header 内容视图,用于作为套件一部分的所有应用程序。
在 header 中,我在 header 区域显示了一个错误图标,当异常处理器添加异常时,用户可以单击该图标来报告错误等。我将错误图标的图像控件的 IsVisible 属性 绑定到异常处理视图模型中的 属性。每次我的页面显示时(所有页面都使用共享 header),将调用视图模型中绑定到 IsVisible 属性 的 属性 的获取。所以看起来绑定配置正确。
问题是当我通过我的异常处理视图模型添加错误然后尝试通知 UI 绑定 属性 已更改时,PropertyChange 事件对象 - 定义如下:
public event PropertyChangedEventHandler PropertyChanged;
始终为空。因此,绑定 属性 的 'get' 永远不会被调用,我的 UI 也不会更新。我希望这是简单的事情,但我一直无法弄清楚。
请注意!!套件中的另一个解决方案(仅使用 UWP 应用程序存根)使用完全相同的内容视图和代码,完全按预期工作。换句话说,当异常处理器添加错误时,绑定 属性 会被询问,错误图标会按预期显示。
下面是我的代码。如有任何帮助,我们将不胜感激。
XAML APPHEADER 内容视图代码
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:sfGrad ="clr-namespace:Syncfusion.XForms.Graphics;assembly=Syncfusion.Core.XForms"
BackgroundColor="#4D648D"
x:Class="FCISharedAll.AppHeader">
<ContentView.Content>
<StackLayout x:Name="slMaster" HorizontalOptions="Fill" VerticalOptions="Start" Orientation="Vertical" Margin="0,0,0,-6"
Spacing="0">
<Grid x:Name="grdGradient" HorizontalOptions="Fill" VerticalOptions="Start" HeightRequest="110" MinimumHeightRequest="110">
<sfGrad:SfGradientView>
<sfGrad:SfGradientView.BackgroundBrush>
<sfGrad:SfLinearGradientBrush StartPoint="0.5, 0" EndPoint="0.5, 1">
<sfGrad:SfLinearGradientBrush.GradientStops>
<sfGrad:SfGradientStop Color="#4D648D" Offset="0.0" />
<sfGrad:SfGradientStop Color="#283655" Offset="0.5" />
<sfGrad:SfGradientStop Color="#4D648D" Offset="1.0" />
</sfGrad:SfLinearGradientBrush.GradientStops>
</sfGrad:SfLinearGradientBrush>
</sfGrad:SfGradientView.BackgroundBrush>
</sfGrad:SfGradientView>
<Grid x:Name="grdHeader" HorizontalOptions="Fill" VerticalOptions="Start">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image x:Name="imgHeader" Source="fciapp.png" HeightRequest="110" MinimumHeightRequest="110" HorizontalOptions="Fill"
VerticalOptions="Center" Aspect="AspectFit" Grid.Column="0" />
<Image x:Name="imgError" Source="error.png" HorizontalOptions="End" VerticalOptions="Center" Margin="8,8,8,8"
IsVisible="{Binding ShowErrorIcon}" Grid.Column="0">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="imgError_Tapped" />
</Image.GestureRecognizers>
</Image>
</Grid>
</Grid>
</StackLayout>
</ContentView.Content>
</ContentView>
APPHEADER 内容视图的 C# 代码
using Rg.Plugins.Popup.Services;
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace FCISharedAll
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AppHeader : ContentView
{
public AppHeader()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
imgError.BindingContext = FCISharedAllHelperClass.gvm_GlobalExceptionClass;
}
private async void imgError_Tapped(object sender, EventArgs e)
{
try
{
await PopupNavigation.Instance.PushAsync(new FCISharedAll.ExceptionDisplay(FCISharedAllHelperClass.gvm_GlobalExceptionClass));
}
catch (Exception ex)
{
SharedErrorHandler.ProcessException(ex);
}
}
}
}
异常处理程序视图模型的 C# 代码
namespace FCISharedAll
{
public class CompleteException
{
public DateTime Error_DateTime { get; set; }
public Exception Error { get; set; }
}
public class ExceptionProcessor : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private List<CompleteException> iobj_ApplicationExceptions { get; set; }
public ExceptionProcessor()
{
try
{
if (iobj_ApplicationExceptions == null)
{
iobj_ApplicationExceptions = new List<CompleteException>();
}
}
catch (Exception ex)
{
iobj_ApplicationExceptions.Add(new CompleteException() { Error_DateTime = DateTime.Now, Error = ex });
}
}
public bool ShowErrorIcon
{
get
{
return (iobj_ApplicationExceptions == null || iobj_ApplicationExceptions.Count > 0);
}
}
public void AddExceptionToList(Exception pobj_Exception)
{
try
{
iobj_ApplicationExceptions.Add(new CompleteException() { Error_DateTime = DateTime.Now, Error = pobj_Exception });
//NotifyPropertyChanged(ge_PreferenceKey.ShowErrorIcon.ToString());
NotifyPropertyChanged("ShowErrorIcon");
}
catch (Exception ex)
{
iobj_ApplicationExceptions.Add(new CompleteException() { Error_DateTime = DateTime.Now, Error = ex });
}
}
public int ErrorCount
{
get
{
int li_ReturnValue = 0;
if (iobj_ApplicationExceptions != null)
{
li_ReturnValue = iobj_ApplicationExceptions.Count;
}
return li_ReturnValue;
}
}
public void RemoveProcessedExceptions(int pi_NumberOfExceptionsToInclude)
{
int li_ErrorsToProcess = 0;
try
{
li_ErrorsToProcess = (pi_NumberOfExceptionsToInclude <= iobj_ApplicationExceptions.Count ? pi_NumberOfExceptionsToInclude : iobj_ApplicationExceptions.Count);
for (int li_IX = 0; li_IX <= li_ErrorsToProcess - 1; li_IX++)
{
iobj_ApplicationExceptions.RemoveAt(0);
}
NotifyPropertyChanged(ge_PreferenceKey.ErrorCount.ToString());
NotifyPropertyChanged(ge_PreferenceKey.ShowErrorIcon.ToString());
}
catch (Exception ex)
{
iobj_ApplicationExceptions.Add(new CompleteException() { Error_DateTime = DateTime.Now, Error = ex });
}
}
public string PrepareErrorText(int pi_NumberOfExceptionsToInclude = 5)
{
string ls_EmailText = "";
int li_ErrorsToProcess = 0;
try
{
li_ErrorsToProcess = (pi_NumberOfExceptionsToInclude <= iobj_ApplicationExceptions.Count ? pi_NumberOfExceptionsToInclude : iobj_ApplicationExceptions.Count);
for (int li_IX = 0; li_IX <= li_ErrorsToProcess - 1; li_IX++)
{
ls_EmailText += FormatException(iobj_ApplicationExceptions[li_IX]) + System.Environment.NewLine;
}
}
catch (Exception ex)
{
iobj_ApplicationExceptions.Add(new CompleteException() { Error_DateTime = DateTime.Now, Error = ex });
}
return ls_EmailText;
}
private string FormatException(CompleteException pobj_CompleteException)
{
string ls_ReturnText = "";
ls_ReturnText = "Date: " + pobj_CompleteException.Error_DateTime + System.Environment.NewLine +
"Message: " + (pobj_CompleteException.Error.Message == null ? "" : pobj_CompleteException.Error.Message + System.Environment.NewLine) +
"Source: " + (pobj_CompleteException.Error.Source == null ? "" : pobj_CompleteException.Error.Source + System.Environment.NewLine) +
"Stack Trace: " + (pobj_CompleteException.Error.StackTrace == null ? "" : pobj_CompleteException.Error.StackTrace + System.Environment.NewLine);
if (pobj_CompleteException.Error.InnerException != null)
{
ls_ReturnText += "Inner Exception Message: " + (pobj_CompleteException.Error.InnerException.Message == null ? "" : pobj_CompleteException.Error.InnerException.Message + System.Environment.NewLine) +
"Inner Exception Source: " + (pobj_CompleteException.Error.InnerException.Source == null ? "" : pobj_CompleteException.Error.InnerException.Source + System.Environment.NewLine) +
"Inner Exception Stack Trace: " + (pobj_CompleteException.Error.InnerException.StackTrace == null ? "" : pobj_CompleteException.Error.InnerException.StackTrace);
}
if (ls_ReturnText.LastIndexOf(System.Environment.NewLine) > ls_ReturnText.Length - 5)
{
//Remove the last line linefeed
ls_ReturnText = ls_ReturnText.Substring(0, ls_ReturnText.LastIndexOf(System.Environment.NewLine));
}
return ls_ReturnText;
}
/// <summary>
/// Manual Notification to subscribers that a property has changed.
/// </summary>
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
这是我的共享代码项目中助手 class 的代码,它将异常处理视图模型实例化为静态 object
using FCISharedAll.FCICommObjects;
using System;
namespace FCISharedAll
{
public static class FCISharedAllHelperClass
{
public static ExceptionProcessor gvm_GlobalExceptionClass = new ExceptionProcessor();
public static ThemeConfig gobj_ThemeConfig { get; set; }
public static bool IsCheckInPage { get; set; } = false;
//internal
private static KioskInfo iobj_KioskInfo = null;
public static KioskInfo HostKioskInfo
{
get
{
if (iobj_KioskInfo == null)
{
iobj_KioskInfo = new KioskInfo();
}
return iobj_KioskInfo;
}
set
{
iobj_KioskInfo = value;
}
}
public static void ConfigureTheme(bool pb_UseKioskTheme)
{
try
{
gobj_ThemeConfig = new ThemeConfig(pb_UseKioskTheme);
}
catch (Exception ex)
{
SharedErrorHandler.ProcessException(ex);
}
}
}
}
我知道很多东西需要仔细阅读,但如果能提供任何帮助,我将不胜感激。
更新!!! ARG - 你知道 post 你认为你已经解决的问题。我相信我实际上得到了我的异常处理程序 class 的两个不同实例,一个来自 Xamarin forms .net class 库,一个在我的共享代码中。我正在从我的 xamarin forms .net class 库中删除视图模型,看看是否能解决问题,我会更新。
SO - 在我的例子中,发生的事情是我有一个在我的 Xamarin Forms 项目中定义的视图模型实例和一个在我的共享代码中定义的对象的静态版本。我忘记从 Xamarin Forms 应用程序中删除视图模型的实例,因此该项目中引用视图模型的所有内容都在访问与我共享代码 class 中所有代码不同的实例。一旦我删除了我的 Xamarin Forms 应用程序中的实例并将所有内容指向我单独的共享代码项目中的静态实例,一切都运行良好。也许这会对其他人有所帮助。
所以我已经查看了很多已经在 Whosebug 中 post 的答案,但我似乎找不到一个适用的答案。
首先,让我解释一下我的 Xamarin Forms 应用程序。我有传统的解决方案结构(.Net Standard 2.0 class 库与 Andoid 和 iOS 应用程序项目)。我正在处理的应用程序是一套应用程序的一部分,该应用程序通过附加的 .Net 2.0 标准 class 库共享大量代码(header、页脚、错误处理等)。在这个 class 中,我有一个用于异常处理的视图模型以及一个标准的 header 内容视图,用于作为套件一部分的所有应用程序。
在 header 中,我在 header 区域显示了一个错误图标,当异常处理器添加异常时,用户可以单击该图标来报告错误等。我将错误图标的图像控件的 IsVisible 属性 绑定到异常处理视图模型中的 属性。每次我的页面显示时(所有页面都使用共享 header),将调用视图模型中绑定到 IsVisible 属性 的 属性 的获取。所以看起来绑定配置正确。
问题是当我通过我的异常处理视图模型添加错误然后尝试通知 UI 绑定 属性 已更改时,PropertyChange 事件对象 - 定义如下:
public event PropertyChangedEventHandler PropertyChanged;
始终为空。因此,绑定 属性 的 'get' 永远不会被调用,我的 UI 也不会更新。我希望这是简单的事情,但我一直无法弄清楚。
请注意!!套件中的另一个解决方案(仅使用 UWP 应用程序存根)使用完全相同的内容视图和代码,完全按预期工作。换句话说,当异常处理器添加错误时,绑定 属性 会被询问,错误图标会按预期显示。
下面是我的代码。如有任何帮助,我们将不胜感激。
XAML APPHEADER 内容视图代码
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:sfGrad ="clr-namespace:Syncfusion.XForms.Graphics;assembly=Syncfusion.Core.XForms"
BackgroundColor="#4D648D"
x:Class="FCISharedAll.AppHeader">
<ContentView.Content>
<StackLayout x:Name="slMaster" HorizontalOptions="Fill" VerticalOptions="Start" Orientation="Vertical" Margin="0,0,0,-6"
Spacing="0">
<Grid x:Name="grdGradient" HorizontalOptions="Fill" VerticalOptions="Start" HeightRequest="110" MinimumHeightRequest="110">
<sfGrad:SfGradientView>
<sfGrad:SfGradientView.BackgroundBrush>
<sfGrad:SfLinearGradientBrush StartPoint="0.5, 0" EndPoint="0.5, 1">
<sfGrad:SfLinearGradientBrush.GradientStops>
<sfGrad:SfGradientStop Color="#4D648D" Offset="0.0" />
<sfGrad:SfGradientStop Color="#283655" Offset="0.5" />
<sfGrad:SfGradientStop Color="#4D648D" Offset="1.0" />
</sfGrad:SfLinearGradientBrush.GradientStops>
</sfGrad:SfLinearGradientBrush>
</sfGrad:SfGradientView.BackgroundBrush>
</sfGrad:SfGradientView>
<Grid x:Name="grdHeader" HorizontalOptions="Fill" VerticalOptions="Start">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image x:Name="imgHeader" Source="fciapp.png" HeightRequest="110" MinimumHeightRequest="110" HorizontalOptions="Fill"
VerticalOptions="Center" Aspect="AspectFit" Grid.Column="0" />
<Image x:Name="imgError" Source="error.png" HorizontalOptions="End" VerticalOptions="Center" Margin="8,8,8,8"
IsVisible="{Binding ShowErrorIcon}" Grid.Column="0">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="imgError_Tapped" />
</Image.GestureRecognizers>
</Image>
</Grid>
</Grid>
</StackLayout>
</ContentView.Content>
</ContentView>
APPHEADER 内容视图的 C# 代码
using Rg.Plugins.Popup.Services;
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace FCISharedAll
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class AppHeader : ContentView
{
public AppHeader()
{
InitializeComponent();
NavigationPage.SetHasNavigationBar(this, false);
imgError.BindingContext = FCISharedAllHelperClass.gvm_GlobalExceptionClass;
}
private async void imgError_Tapped(object sender, EventArgs e)
{
try
{
await PopupNavigation.Instance.PushAsync(new FCISharedAll.ExceptionDisplay(FCISharedAllHelperClass.gvm_GlobalExceptionClass));
}
catch (Exception ex)
{
SharedErrorHandler.ProcessException(ex);
}
}
}
}
异常处理程序视图模型的 C# 代码
namespace FCISharedAll
{
public class CompleteException
{
public DateTime Error_DateTime { get; set; }
public Exception Error { get; set; }
}
public class ExceptionProcessor : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private List<CompleteException> iobj_ApplicationExceptions { get; set; }
public ExceptionProcessor()
{
try
{
if (iobj_ApplicationExceptions == null)
{
iobj_ApplicationExceptions = new List<CompleteException>();
}
}
catch (Exception ex)
{
iobj_ApplicationExceptions.Add(new CompleteException() { Error_DateTime = DateTime.Now, Error = ex });
}
}
public bool ShowErrorIcon
{
get
{
return (iobj_ApplicationExceptions == null || iobj_ApplicationExceptions.Count > 0);
}
}
public void AddExceptionToList(Exception pobj_Exception)
{
try
{
iobj_ApplicationExceptions.Add(new CompleteException() { Error_DateTime = DateTime.Now, Error = pobj_Exception });
//NotifyPropertyChanged(ge_PreferenceKey.ShowErrorIcon.ToString());
NotifyPropertyChanged("ShowErrorIcon");
}
catch (Exception ex)
{
iobj_ApplicationExceptions.Add(new CompleteException() { Error_DateTime = DateTime.Now, Error = ex });
}
}
public int ErrorCount
{
get
{
int li_ReturnValue = 0;
if (iobj_ApplicationExceptions != null)
{
li_ReturnValue = iobj_ApplicationExceptions.Count;
}
return li_ReturnValue;
}
}
public void RemoveProcessedExceptions(int pi_NumberOfExceptionsToInclude)
{
int li_ErrorsToProcess = 0;
try
{
li_ErrorsToProcess = (pi_NumberOfExceptionsToInclude <= iobj_ApplicationExceptions.Count ? pi_NumberOfExceptionsToInclude : iobj_ApplicationExceptions.Count);
for (int li_IX = 0; li_IX <= li_ErrorsToProcess - 1; li_IX++)
{
iobj_ApplicationExceptions.RemoveAt(0);
}
NotifyPropertyChanged(ge_PreferenceKey.ErrorCount.ToString());
NotifyPropertyChanged(ge_PreferenceKey.ShowErrorIcon.ToString());
}
catch (Exception ex)
{
iobj_ApplicationExceptions.Add(new CompleteException() { Error_DateTime = DateTime.Now, Error = ex });
}
}
public string PrepareErrorText(int pi_NumberOfExceptionsToInclude = 5)
{
string ls_EmailText = "";
int li_ErrorsToProcess = 0;
try
{
li_ErrorsToProcess = (pi_NumberOfExceptionsToInclude <= iobj_ApplicationExceptions.Count ? pi_NumberOfExceptionsToInclude : iobj_ApplicationExceptions.Count);
for (int li_IX = 0; li_IX <= li_ErrorsToProcess - 1; li_IX++)
{
ls_EmailText += FormatException(iobj_ApplicationExceptions[li_IX]) + System.Environment.NewLine;
}
}
catch (Exception ex)
{
iobj_ApplicationExceptions.Add(new CompleteException() { Error_DateTime = DateTime.Now, Error = ex });
}
return ls_EmailText;
}
private string FormatException(CompleteException pobj_CompleteException)
{
string ls_ReturnText = "";
ls_ReturnText = "Date: " + pobj_CompleteException.Error_DateTime + System.Environment.NewLine +
"Message: " + (pobj_CompleteException.Error.Message == null ? "" : pobj_CompleteException.Error.Message + System.Environment.NewLine) +
"Source: " + (pobj_CompleteException.Error.Source == null ? "" : pobj_CompleteException.Error.Source + System.Environment.NewLine) +
"Stack Trace: " + (pobj_CompleteException.Error.StackTrace == null ? "" : pobj_CompleteException.Error.StackTrace + System.Environment.NewLine);
if (pobj_CompleteException.Error.InnerException != null)
{
ls_ReturnText += "Inner Exception Message: " + (pobj_CompleteException.Error.InnerException.Message == null ? "" : pobj_CompleteException.Error.InnerException.Message + System.Environment.NewLine) +
"Inner Exception Source: " + (pobj_CompleteException.Error.InnerException.Source == null ? "" : pobj_CompleteException.Error.InnerException.Source + System.Environment.NewLine) +
"Inner Exception Stack Trace: " + (pobj_CompleteException.Error.InnerException.StackTrace == null ? "" : pobj_CompleteException.Error.InnerException.StackTrace);
}
if (ls_ReturnText.LastIndexOf(System.Environment.NewLine) > ls_ReturnText.Length - 5)
{
//Remove the last line linefeed
ls_ReturnText = ls_ReturnText.Substring(0, ls_ReturnText.LastIndexOf(System.Environment.NewLine));
}
return ls_ReturnText;
}
/// <summary>
/// Manual Notification to subscribers that a property has changed.
/// </summary>
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
这是我的共享代码项目中助手 class 的代码,它将异常处理视图模型实例化为静态 object
using FCISharedAll.FCICommObjects;
using System;
namespace FCISharedAll
{
public static class FCISharedAllHelperClass
{
public static ExceptionProcessor gvm_GlobalExceptionClass = new ExceptionProcessor();
public static ThemeConfig gobj_ThemeConfig { get; set; }
public static bool IsCheckInPage { get; set; } = false;
//internal
private static KioskInfo iobj_KioskInfo = null;
public static KioskInfo HostKioskInfo
{
get
{
if (iobj_KioskInfo == null)
{
iobj_KioskInfo = new KioskInfo();
}
return iobj_KioskInfo;
}
set
{
iobj_KioskInfo = value;
}
}
public static void ConfigureTheme(bool pb_UseKioskTheme)
{
try
{
gobj_ThemeConfig = new ThemeConfig(pb_UseKioskTheme);
}
catch (Exception ex)
{
SharedErrorHandler.ProcessException(ex);
}
}
}
}
我知道很多东西需要仔细阅读,但如果能提供任何帮助,我将不胜感激。
更新!!! ARG - 你知道 post 你认为你已经解决的问题。我相信我实际上得到了我的异常处理程序 class 的两个不同实例,一个来自 Xamarin forms .net class 库,一个在我的共享代码中。我正在从我的 xamarin forms .net class 库中删除视图模型,看看是否能解决问题,我会更新。
SO - 在我的例子中,发生的事情是我有一个在我的 Xamarin Forms 项目中定义的视图模型实例和一个在我的共享代码中定义的对象的静态版本。我忘记从 Xamarin Forms 应用程序中删除视图模型的实例,因此该项目中引用视图模型的所有内容都在访问与我共享代码 class 中所有代码不同的实例。一旦我删除了我的 Xamarin Forms 应用程序中的实例并将所有内容指向我单独的共享代码项目中的静态实例,一切都运行良好。也许这会对其他人有所帮助。