Reputation: 168
I have an interface called “IAppConfig”, that has an EventHandler PolicyDefaultChanged with custom EventAgrs. The interface is inherited in Platform specific code(iOS in this case) and I raise the event. The "PolicyDefaultChanged" Event is null and hence the .Invoke() is not being called. That is done because of the null check using "PolicyDefaultChanged?". I have handled the event in main code as shown below.
// in Common Code / Controls /PolicyDefaultChangedEventArgs
public class PolicyDefaultChangedEventArgs
: EventArgs {
public Dictionary<string, object> DefaultPolicy { get; private set; }
public PolicyDefaultChangedEventArgs(
Dictionary<string, object> defaultPolicy) {
Console.WriteLine("Policy Changed");
DefaultPolicy = defaultPolicy;
}
}
// Event Handler in Interface
// in Common Code / IAppConfig.cs
public interface IAppConfig {
public event EventHandler<Controls.PolicyDefaultChangedEventArgs>
PolicyDefaultChanged;
}
// iOS specific code
// in Platforms / iOS /AppConfigService.cs
public class AppConfigService : IAppConfig {
private Dictionary<string, object> m_dict =
new Dictionary<string, object>();
public event EventHandler<Controls.PolicyDefaultChangedEventArgs>
PolicyDefaultChanged;
public AppConfigService() {
Dictionary<string, object> dict = new Dictionary<string, object>();
dict["keyString"] = "test";
setAppConfigurationSettings(dict);
}
public void setAppConfigurationSettings(Dictionary<string, object> dict) {
m_dict = dict;
Controls.PolicyDefaultChangedEventArgs ars =
new Controls.PolicyDefaultChangedEventArgs(dict);
OnPolicyDefaultChanged(ars);
}
protected virtual void OnPolicyDefaultChanged(
Controls.PolicyDefaultChangedEventArgs e) {
PolicyDefaultChanged?.Invoke(
this, e); // line which doesn't get raise event due to "?" check
// which shows that PolicyDefaultChanged is null
}
}
// Main Code
// in MainPage.xaml.cs
public partial class MainPage : ContentPage {
public MainPage(IAppConfig appConfig) {
InitializeComponent();
appConfig.PolicyDefaultChanged += OnPolicyDefaultsChanged;
}
private void OnPolicyDefaultsChanged(
object sender, Controls.PolicyDefaultChangedEventArgs e) {
Debug.WriteLine("Policy changed");
}
}
// MAUIProgram.cs
public static class MauiProgram {
public static MauiApp CreateMauiApp() {
var builder = MauiApp.CreateBuilder();
builder.UseMauiApp<App>()
.ConfigureFonts(fonts => {
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
})
#if DEBUG
builder.Logging.AddDebug();
#endif
builder.Services.AddTransient<IAppConfig, AppConfigService>();
builder.Services.AddTransient<MainPage>();
return builder.Build();
}
}
What am I doing wrong. Added code sample below The function in MainCode is not being called/ event not being raised
EDIT 1:
Okay I got it to work using PropertyMapper but I don’t think it is the right way because I am using this IView class which I want to avoid. Please find the sample code below (which works). I would want to use it without PropertyMapper or without calling this custom “View” in my main class. The current code mean that I have to declare this view in my MainPage.xaml and then use that to connect to the handler instead of the event handler declared in the IAppConfig/the interface
// Common Code Controls/PolicyDefault.cs
public interface IHybridPolicyDefault : IView {
event EventHandler<Controls.PolicyDefaultChangedEventArgs>
PolicyDefaultChanged;
void Refresh();
Dictionary<string, object> DefaultPolicy { get; set; }
void InvokePolicyDict(Dictionary<string, object> data);
}
// Common Code Controls/PolicyDefault.cs
public class MyClass : View, IHybridPolicyDefault {
public string Error {
get => throw new NotImplementedException();
set => throw new NotImplementedException();
}
public Dictionary<string, object> DefaultPolicy {
get => (Dictionary<string, object>)GetValue(SourceProperty);
set => SetValue(SourceProperty, value);
}
public event EventHandler<PolicyDefaultChangedEventArgs>
PolicyDefaultChanged;
public static BindableProperty SourceProperty =
BindableProperty.Create(propertyName: "DefaultPolicy",
returnType: typeof(Dictionary<string, object>),
declaringType: typeof(MyClass),
defaultValue: new Dictionary<string, object>(),
propertyChanged: OnPolicyDefaultChanged);
protected static void OnPolicyDefaultChanged(BindableObject bindable,
object oldValue,
object newValue) {
// PolicyDefaultChanged.Invoke(this, e);
Console.WriteLine("Source invoke");
var view = bindable as MyClass;
Console.WriteLine("Test" + oldValue + " " + newValue);
bindable.Dispatcher.Dispatch(() => {
view.PolicyDefaultChanged.Invoke(
view, new Controls.PolicyDefaultChangedEventArgs(
newValue as Dictionary<string, object>));
});
}
public void InvokePolicyDict(Dictionary<string, object> data) {
PolicyDefaultChanged?.Invoke(this,
new PolicyDefaultChangedEventArgs(data));
}
public void Refresh() {
throw new NotImplementedException();
}
}
// iOS Specific Code: Platforms/iOS/AppConfigService.cs
public class AppConfigService
: ViewHandler<Controls.IHybridPolicyDefault, UIKit.UIView>,
IAppConfig {
public static PropertyMapper<Controls.IHybridPolicyDefault,
AppConfigService> HybridWebViewMapper =
new PropertyMapper<Controls.IHybridPolicyDefault, AppConfigService>(
ViewHandler.ViewMapper);
public event EventHandler<Controls.PolicyDefaultChangedEventArgs>
PolicyDefaultChanged;
public AppConfigService() : base(HybridWebViewMapper) {}
private void VirtualView_SourceChanged(
object sender, Controls.PolicyDefaultChangedEventArgs e) {
Console.WriteLine("Policy Changed handler");
}
protected override void ConnectHandler(UIView platformView) {
base.ConnectHandler(platformView);
if (VirtualView.DefaultPolicy != null) {
m_instance = this;
Dictionary<string, object> dict = new Dictionary<string, object>();
dict["keyString"] = "test";
setAppConfigurationSettings(dict);
}
VirtualView.PolicyDefaultChanged += VirtualView_SourceChanged;
}
public void setAppConfigurationSettings(Dictionary<string, object> dict) {
m_dict = dict;
VirtualView?.InvokePolicyDict(dict);
}
protected override void DisconnectHandler(UIView platformView) {
platformView.Dispose();
base.DisconnectHandler(platformView);
}
}
// MainPage.xaml
<ContentPage xmlns = "http://schemas.microsoft.com/dotnet/2021/maui" xmlns : x =
"http://schemas.microsoft.com/winfx/2009/xaml" x : Class =
"ManagedAppConfig.MainPage" xmlns : control =
"clr-namespace:ManagedAppConfig.Controls">
<ScrollView><VerticalStackLayout Spacing = "25" Padding =
"30,0" VerticalOptions = "Center">
<control : MyClass x : Name = "test" />
<Image Source = "dotnet_bot.png" SemanticProperties.Description =
"Cute dot net bot waving hi to you!" HeightRequest =
"200" HorizontalOptions = "Center" />
<Label Text = "Hello, World!" SemanticProperties.HeadingLevel =
"Level1" FontSize = "32" HorizontalOptions = "Center" />
<Label Text = "Welcome to .NET Multi-platform App UI" SemanticProperties
.HeadingLevel = "Level2" SemanticProperties.Description =
"Welcome to dot net Multi platform App U I" FontSize =
"18" HorizontalOptions = "Center" />
<Button x : Name = "CounterBtn" Text = "Click me" SemanticProperties.Hint =
"Counts the number of times you click" Clicked =
"OnCounterClicked" HorizontalOptions = "Center" />
</VerticalStackLayout></ScrollView>
</ContentPage>
// MainPage.xaml.cs
public partial class MainPage : ContentPage {
public MainPage(IAppConfig appConfig) {
InitializeComponent();
test.PolicyDefaultChanged += OnPolicyDefaultsChanged;
}
private void OnPolicyDefaultsChanged(
object sender, Controls.PolicyDefaultChangedEventArgs e) {
// This works
Console.WriteLine(e.DefaultPolicy.Count);
}
}
Upvotes: 0
Views: 941
Reputation: 2146
In your first code sample, your event is not raised because it's invoked from the constructor of AppConfigService
, which is before the code of MainPage
can even subscribe to that event.
The sequence goes like this:
MainPage()
AppConfigService()
PolicyDefaultChanged
has no subscribers.
setAppConfigurationSettings()
PolicyDefaultChanged
is not raised because it still has no subscribers.
appConfig.PolicyDefaultChanged += OnPolicyDefaultsChanged;
PolicyDefaultChanged
is now subscribed to, but it's too late.
Upvotes: 1