I have registered a handler with the HardwareButtons.BackPressed event, performed some logic and then set the handled property in the args to true if it applies. The handler runs through without any issue, and the Handled property gets set. The phone still navigates back outside of the app. Am I misunderstanding how to use the event?
Page
public sealed partial class FirstRunPage : VisualStateAwarePage
{
public FirstRunPage()
{
this.InitializeComponent();
#if WINDOWS_PHONE_APP
HardwareButtons.BackPressed += (sender, args) =>
{
bool isHandled = false;
Action handledCallback = () => isHandled = true;
var state = new Dictionary<string, object> { { "Callback", handledCallback } };
((INavigationAware)this.DataContext).OnNavigatedTo("Back", NavigationMode.Back, state);
args.Handled = isHandled;
};
#endif
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
}
}
View model.
public override void OnNavigatedTo(object navigationParameter, NavigationMode navigationMode, Dictionary<string, object> viewModelState)
{
if (navigationParameter == null || !navigationParameter.ToString().Equals("Back"))
{
return;
}
if (!viewModelState.ContainsKey("Callback"))
{
return;
}
var callback = (Action)viewModelState["Callback"];
// If the user is new, then we set it to false and invoke our callback.
if (this.IsNewUser)
{
this.IsNewUser = false;
callback();
}
else
{
return;
}
}
Update
I have modified my FirstRunPage to subscribe and unsubscribe as recommended by #Martin but it still closes the app.
public sealed partial class FirstRunPage : VisualStateAwarePage
{
public FirstRunPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
#if WINDOWS_PHONE_APP
HardwareButtons.BackPressed += HardwareBack_OnPressed;
#endif
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
#if WINDOWS_PHONE_APP
HardwareButtons.BackPressed -= HardwareBack_OnPressed;
#endif
}
private void HardwareBack_OnPressed(object sender, BackPressedEventArgs e)
{
Action handledCallback = () => e.Handled = true;
var state = new Dictionary<string, object> { { "Callback", handledCallback } };
((INavigationAware)this.DataContext).OnNavigatedTo("Back", NavigationMode.Back, state);
}
}
With the help of #yasen I was able to get this resolved. The issue stems from the fact that the Prism library has your App.xaml.cs inherit from MvvmAppbase, which intercepts the BackPressed event.
To resolve this, I overrode the MvvmAppBase OnHardwareButtonsBackPressed and added a bit of logic to handle it.
My view model and view both implement a new interface called INavigateBackwards and they're used like this:
View Model
public bool CanNavigateBack()
{
// If the new user is true, then we can't navigate backwards.
// There isn't any navigation stack, so the app will die.
bool canNavigate = !this.IsNewUser;
// Disable the new user mode.
this.IsNewUser = false;
// Return so that the view can return to it's sign-in state.
return canNavigate;
}
View
public sealed partial class FirstRunPage : VisualStateAwarePage, INavigateBackwards
{
private INavigateBackwards ViewModel
{
get
{
return (INavigateBackwards)this.DataContext;
}
}
public FirstRunPage()
{
this.InitializeComponent();
}
public bool CanNavigateBack()
{
return ViewModel.CanNavigateBack();
}
}
Then in the MvvmAppBase subclass, I determine if I need to handle the navigation or not.
MvvmAppBase child
protected override void OnHardwareButtonsBackPressed(object sender, BackPressedEventArgs e)
{
var page = (Page)((Frame)Window.Current.Content).Content;
if (page is INavigateBackwards)
{
var navigatingPage = (INavigateBackwards)page;
if (!navigatingPage.CanNavigateBack())
{
e.Handled = true;
return;
}
}
base.OnHardwareButtonsBackPressed(sender, e);
}
This allows my single view to have multiple states and the user to navigate back from one state to the previous without navigating to an entirely new view.
The reason why your application closes is that the same handler is called more than just once. First handler sets the Handled property to true, but any other subsequent call for the same event fire sets it back to false.
To illustrate it, try this:
public sealed partial class FirstRunPage : VisualStateAwarePage
{
public FirstRunPage()
{
// ...
InitializeComponent();
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
}
void HardwareButtons_BackPressed(object sender, BackPressedEventArgs e)
{
bool isHandled = false;
Action handledCallback = () => isHandled = true;
var state = new Dictionary<string, object> { { "Callback", handledCallback } };
((INavigationAware)this.DataContext).OnNavigatedTo("Back", NavigationMode.Back, state);
args.Handled = isHandled;
};
}
And set breakpoint to last line of the handler code.
To avoid it, assign your handler in the OnNavigatedTo method of your FirstRunPage, and unregister the handler in OnNavigatedFrom.
protected override void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
HardwareButtons.BackPressed += HardwareButtons_BackPressed;
}
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
HardwareButtons.BackPressed -= HardwareButtons_BackPressed;
}
Related
We have setup some Xamarin behavior for not null entry fields etc, this fires when the user makes a change to a field and we then changed the entry border color, red for invalid.
However, we'd also like to reuse this behaviors when a submit button is tapped.
So I need to fire the TextChanged event manually, any ideas how I can do this, now sure if it's possible ?
public class NotEmptyEntryBehaviour : Behavior<Entry>
{
protected override void OnAttachedTo(Entry bindable)
{
bindable.TextChanged += OnEntryTextChanged;
base.OnAttachedTo(bindable);
}
protected override void OnDetachingFrom(Entry bindable)
{
bindable.TextChanged -= OnEntryTextChanged;
base.OnDetachingFrom(bindable);
}
void OnEntryTextChanged(object sender, TextChangedEventArgs args)
{
if (args == null)
return;
var oldString = args.OldTextValue;
var newString = args.NewTextValue;
}
}
If you want an alternative you can use one of the pre-built validation behaviors that comes with Xamarin.CommunityToolkit package, like TextValidationBehavior (by specifying a Regexp) or any more specific derived ones (example NumericValidationBehavior) that may fit your needs or even create a custom one by sub-classing ValidationBehavior.
It let you define custom styles for Valid and InValid states, but more important for the question has an async method called ForceValidate().
Also the Flags property could be interesting.
NotEmptyEntryBehaviour seems closer to TextValidationBehavior with MinimumLenght=1
xaml
<Entry Placeholder="Type something..." x:Name="entry">
<Entry.Behaviors>
<xct:TextValidationBehavior Flags="ValidateOnValueChanging"
InvalidStyle="{StaticResource InvalidEntryStyle}"
ValidStyle="{StaticResource ValidEntryStyle}"/>
</Entry.Behaviors>
</Entry>
Code
await (entry.Behaviors[0] as TextValidationBehavior)?.ForceValidate();
Docs
https://learn.microsoft.com/en-us/xamarin/community-toolkit/behaviors/charactersvalidationbehavior
Repo Samples
https://github.com/xamarin/XamarinCommunityToolkit/tree/main/samples/XCT.Sample/Pages/Behaviors
EDIT
If you want to run the validation from the ViewModel you need to bind ForceValidateCommand as explained in this GitHub discussion/question.
We have setup some Xamarin behavior for not null entry fields etc, this fires when the user makes a change to a field and we then changed the entry border color, red for invalid.
You can create custom Entry with behavior to get.
The first I’m going to do is to create a new control that inherits from Entry and will add three properties: IsBorderErrorVisible, BorderErrorColor, ErrorText.
public class ExtendedEntry : Entry
{
public static readonly BindableProperty IsBorderErrorVisibleProperty =
BindableProperty.Create(nameof(IsBorderErrorVisible), typeof(bool), typeof(ExtendedEntry), false, BindingMode.TwoWay);
public bool IsBorderErrorVisible
{
get { return (bool)GetValue(IsBorderErrorVisibleProperty); }
set
{
SetValue(IsBorderErrorVisibleProperty, value);
}
}
public static readonly BindableProperty BorderErrorColorProperty =
BindableProperty.Create(nameof(BorderErrorColor), typeof(Xamarin.Forms.Color), typeof(ExtendedEntry), Xamarin.Forms.Color.Transparent, BindingMode.TwoWay);
public Xamarin.Forms.Color BorderErrorColor
{
get { return (Xamarin.Forms.Color)GetValue(BorderErrorColorProperty); }
set
{
SetValue(BorderErrorColorProperty, value);
}
}
public static readonly BindableProperty ErrorTextProperty =
BindableProperty.Create(nameof(ErrorText), typeof(string), typeof(ExtendedEntry), string.Empty);
public string ErrorText
{
get { return (string)GetValue(ErrorTextProperty); }
set
{
SetValue(ErrorTextProperty, value);
}
}
}
Then creating custom render to android platform.
[assembly: ExportRenderer(typeof(ExtendedEntry), typeof(ExtendedEntryRenderer))]
namespace FormsSample.Droid
{
public class ExtendedEntryRenderer : EntryRenderer
{
public ExtendedEntryRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control == null || e.NewElement == null) return;
UpdateBorders();
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Control == null) return;
if (e.PropertyName == ExtendedEntry.IsBorderErrorVisibleProperty.PropertyName)
UpdateBorders();
}
void UpdateBorders()
{
GradientDrawable shape = new GradientDrawable();
shape.SetShape(ShapeType.Rectangle);
shape.SetCornerRadius(0);
if (((ExtendedEntry)this.Element).IsBorderErrorVisible)
{
shape.SetStroke(3, ((ExtendedEntry)this.Element).BorderErrorColor.ToAndroid());
}
else
{
shape.SetStroke(3, Android.Graphics.Color.LightGray);
this.Control.SetBackground(shape);
}
this.Control.SetBackground(shape);
}
}
}
Finally, Creating an Entry Behavior, handle the error to provide ui feedback to the user when validation occurs
public class EmptyEntryValidatorBehavior : Behavior<ExtendedEntry>
{
ExtendedEntry control;
string _placeHolder;
Xamarin.Forms.Color _placeHolderColor;
protected override void OnAttachedTo(ExtendedEntry bindable)
{
bindable.TextChanged += HandleTextChanged;
bindable.PropertyChanged += OnPropertyChanged;
control = bindable;
_placeHolder = bindable.Placeholder;
_placeHolderColor = bindable.PlaceholderColor;
}
void HandleTextChanged(object sender, TextChangedEventArgs e)
{
ExtendedEntry customentry = (ExtendedEntry)sender;
if (!string.IsNullOrEmpty(customentry.Text))
{
((ExtendedEntry)sender).IsBorderErrorVisible = false;
}
else
{
((ExtendedEntry)sender).IsBorderErrorVisible = true;
}
}
protected override void OnDetachingFrom(ExtendedEntry bindable)
{
bindable.TextChanged -= HandleTextChanged;
bindable.PropertyChanged -= OnPropertyChanged;
}
void OnPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName == ExtendedEntry.IsBorderErrorVisibleProperty.PropertyName && control != null)
{
if (control.IsBorderErrorVisible)
{
control.Placeholder = control.ErrorText;
control.PlaceholderColor = control.BorderErrorColor;
control.Text = string.Empty;
}
else
{
control.Placeholder = _placeHolder;
control.PlaceholderColor = _placeHolderColor;
}
}
}
}
Update:
You can change custom entry's IsBorderErrorVisible in button.click, to call this from submit button.
private void btn1_Clicked(object sender, EventArgs e)
{
if(string.IsNullOrEmpty(entry1.Text))
{
entry1.IsBorderErrorVisible = true;
}
}
<customentry:ExtendedEntry
x:Name="entry1"
BorderErrorColor="Red"
ErrorText="please enter name!">
<customentry:ExtendedEntry.Behaviors>
<behaviors:EmptyEntryValidatorBehavior />
</customentry:ExtendedEntry.Behaviors>
</customentry:ExtendedEntry>
How to put an ActivityIndicator on Xamarin Forms OnStart() function.
I am check Network access on OnStart() function.
Bind the ActivityIndicator to a property in your BaseViewModel (IsBusy).
View
<ActivityIndicator Color="Accent" IsVisible="{Binding IsBusy}" IsRunning="{Binding IsBusy}" />
BaseViewModel (Inherited by all ViewModels)
private bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
_isBusy = value;
OnPropertyChanged("IsBusy");
}
}
Get yourself a good MVVM framework (Prism) and put the network check in the OnNavigatedTo method for your start page.
public override void OnNavigatedTo(INavigationParameters parameters)
{
IsBusy = true;
await CheckNetwork();
IsBusy = false;
}
Now you can paste that same ActivityIndicator snippet into any page (XAML) that is bound to a ViewModel inheriting BaseViewModel and it will just work when you set IsBusy.
Haven't used ActivityIndicator, but this nuget works great: Acr.UserDialogs.
After installing and adding the initialization part in the MainActivity or ios equivalent, just add the following code between resource intensive threads in either your code-behind file or viewmodel (mvvm):
This works for code-behind file:
protected override async void OnAppearing(object sender, EventArgs e)
{
base.ViewIsAppearing(sender, e);
UserDialogs.Instance.ShowLoading();
//do stuff here
UserDialogs.Instance.HideLoading();
}
This works for FreshMVVM framework:
protected override async void ViewIsAppearing(object sender, EventArgs e)
{
base.ViewIsAppearing(sender, e);
UserDialogs.Instance.ShowLoading();
//do stuff here
UserDialogs.Instance.HideLoading();
}
I'm using network checking in my projects too, please check this:
using Plugin.Connectivity;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace PetBellies.View
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class NoConnection : ContentPage
{
private bool wasNotConn = false;
public NoConnection()
{
InitializeComponent();
CrossConnectivity.Current.ConnectivityChanged += async (sender, args) =>
{
if (CrossConnectivity.Current.IsConnected && !wasNotConn)
{
wasNotConn = true;
await Navigation.PushModalAsync(new NavigationPage(new MainPage()));
}
else
{
wasNotConn = false;
}
};
}
public NoConnection(bool isFromLogin)
{
CrossConnectivity.Current.ConnectivityChanged += async (sender, args) =>
{
if (CrossConnectivity.Current.IsConnected && !wasNotConn)
{
wasNotConn = true;
var page = new LoginPage();
var navPage = new NavigationPage(page);
NavigationPage.SetHasNavigationBar(navPage, false);
await Navigation.PushModalAsync(navPage);
}
else
{
wasNotConn = false;
}
};
}
}
}
https://github.com/officialdoniald/PetBellies/blob/master/PetBellies/PetBellies/View/NoConnection.xaml.cs
If the connection lost, the application navigate to this page and stay on this page while the connection is unavailable.
I have 'BasePage' class which is base class for all other pages in my project.
In initialization, I'm adding EventHandler for 'SystemNavigationManager' for event 'BackRequest'. For some reason that line id causing 'AccessViolationException' when XAML designer is trying to render XAML of class that extends 'BasePage'
I'm not familiar with UWP, so I'll be very grateful for tips.
BasePage
public class BasePage: Page {
internal string title = "";
internal HeaderView headerView;
public BasePage() {
this.Loaded += BasePage_Loaded;
// FIXME: For some reason if this line is uncommented then xaml designer fails.
SystemNavigationManager.GetForCurrentView().BackRequested += BasePage_BackRequested;
}
private void BasePage_BackRequested(object sender, BackRequestedEventArgs e) {
bool handled = e.Handled;
this.BackRequested(ref handled);
e.Handled = handled;
}
private void BackRequested(ref bool handled) {
//Get a hold of the current frame so that we can inspect the app back stack.
if (this.Frame == null)
return;
// Check to see if this is the top-most page on the app back stack.
if (this.Frame.CanGoBack && !handled) {
// If not, set the event to handled and go back to the previous page in the app.
handled = true;
this.Frame.GoBack();
}
}
private void setupPageAnimation() {
TransitionCollection collection = new TransitionCollection();
NavigationThemeTransition theme = new NavigationThemeTransition();
var info = new ContinuumNavigationTransitionInfo();
theme.DefaultNavigationTransitionInfo = info;
collection.Add(theme);
this.Transitions = collection;
}
private void BasePage_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e) {
setupPageAnimation();
}
}
SOLUTION
Just like Ivan said, final code looks like this. Without of a trace of bug.
BasePage
public BasePage() {
this.Loaded += BasePage_Loaded;
}
protected override void OnNavigatedTo(NavigationEventArgs e) {
base.OnNavigatedTo(e);
SystemNavigationManager.GetForCurrentView().BackRequested += BasePage_BackRequested;
}
protected override void OnNavigatedFrom(NavigationEventArgs e) {
base.OnNavigatedFrom(e);
SystemNavigationManager.GetForCurrentView().BackRequested -= BasePage_BackRequested;
}
You shouldn't subscribe to back events on constructor but OnNavigatedTo and unsubscribe in OnNavigatedFrom. Even if it didn't crash it would cause a lot of problems because your back logic would be activated on all previous pages when you press the back button.
I have mainviewmodel where it makes a collection of itemviewmodel and binds that data to a longlist in Mainpage.xaml. Now in the process of making a collection of ItemViewModel I am making a web request and when that downloads I would make a list.
I wanted to know in MainPage as when this download finishes.
MainViewModel
public void LoadData()
{
if (this.CanLoad)
{
WebClient dealsOfDay = new WebClient();
dealsOfDay.DownloadStringCompleted += dealsOfDay_DownloadStringCompleted;
dealsOfDay.DownloadStringAsync(new Uri("http://loadsomedata.php"));
}
else
{
this.IsDataLoaded = false;
}
}
void dealsOfDay_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null && e.Result != null)
{
var deals=//something making a collection.
Items = new ObservableCollection<ItemViewModel>(deals);
NotifyPropertyChanged("Items");
this.IsDataLoaded = true;
}
else
{
MessageBox.Show("Error");
}
}
App.xaml.cs
private static MainViewModel viewModel = null;
/// <summary>
/// A static ViewModel used by the views to bind against.
/// </summary>
/// <returns>The MainViewModel object.</returns>
public static MainViewModel ViewModel
{
get
{
// Delay creation of the view model until necessary
if (viewModel == null)
viewModel = new MainViewModel();
return viewModel;
}
}
MainPage.xaml.cs
Inside the constructor I would set this.
DataContext = App.ViewModel;
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
I think you have no need for bool IsDataLoaded. Instead that create event in MainViewModel and register in Main page.
public event EventHandler DataLoadedEvent;
void dealsOfDay_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
if (e.Error == null && e.Result != null)
{
var deals=//something making a collection.
Items = new ObservableCollection<ItemViewModel>(deals);
NotifyPropertyChanged("Items");
if ( DataLoadedEvent != null)
{
DataLoadedEvent(this, new EventHandler());
}
}
else
{
MessageBox.Show("Error");
}
}
Now in MainPage constructor register this event.
App.ViewModel.DataLoadedEvent += new EventHandler(data_loadedEvent);
void data_loadedEvent(object sender, EventArgs e)
{
App.ViewModel.LoadData();
}
Remember also that you can subscribe more methods to DownloadStringCompleted - and they will be fired, so maybe there is no need to create new event. Also in many cases you can just perform actions in dealsOfDay_DownloadStringCompleted.
But if you want to make an Event which will be fired when DownloadCompletes it can look like this:
Create a delegate:
public delegate void StatusUpdateHandler(object sender, StatusEventArgs e);
public event StatusUpdateHandler OnUpdateStatus;
For this purpose you need somewhere to define StatusEventArgs Class:
public class StatusEventArgs : EventArgs
{
public string Status { get; private set; }
public StatusEventArgs(string status)
{
Status = status;
}
}
Then your method can look like this:
private void UpdateStatus(string status)
{
if (OnUpdateStatus == null) return;
StatusEventArgs args = new StatusEventArgs(status);
OnUpdateStatus(this, args);
}
Then you can freely subscribe to that event and put in your dealsOfDay_DownloadStringCompleted:
UpdateStatus("Downloaded");
Well, i'm working in a asp.net 3.5 site.
I have set an Observer like this:
public delegate void ActionNotification();
protected Dictionary<string, List<ActionNotification>> Observers
{
get
{
Dictionary<string, List<ActionNotification>> _observers = Session["Observers"] as Dictionary<string, List<ActionNotification>>;
if (_observers == null)
{
_observers = new Dictionary<string, List<ActionNotification>>();
Observers = _observers;
}
return _observers;
}
set
{
Session["Observers"] = value;
}
}
public void Attach(string actionName, ActionNotification observer)
{
if (!Observers.ContainsKey(actionName))
{
Observers.Add(actionName, new List<ActionNotification>());
}
Observers[actionName].Add(observer);
}
public void Detach(string actionName, ActionNotification observer)
{
if (Observers.ContainsKey(actionName))
{
Observers[actionName].Remove(observer);
}
}
public void DetachAll(string actionName)
{
if (Observers.ContainsKey(actionName))
{
Observers.Remove(actionName);
}
}
public void Notify(string action)
{
if (Observers.ContainsKey(action))
{
foreach (ActionNotification o in Observers[action])
{
o.Invoke();
}
}
}
I use the observer like this:
//Esta es llamada al notify con cierto action
protected void btnNext_Click(object sender, ImageClickEventArgs e)
{
Notify("Next");
}
//Y este es el register del Listener
Attach("Next", new ActionNotification(NextButton_Click));
If before the o.Invoke(); for example i change the page title to "Hello".
And inside the "NextButton_Click" I set it to "Goodbye", after the NextButton_Click finish, the Title goes back to "Hello"...
Any idea why?
I think problem is that the "Page" in your NextButton_Click event is not the same page as the page you set the title to "Hello" on. Because you are passing around events in the session when the event is raised the object is acts on is no longer in scope. You can recreate it with the following code (which is using EventHandlers, but they are basically the same as what you have outlined in your code)
protected void Page_Load(object sender, EventArgs e)
{
this.Page.Title = "test";
//Store it in your session...seems like a weird thing to do given how your page should be stateless, so I would think about what you are
//trying to do a bit more carefully. You don't want to call an event handler such as test below from another page in your asp.net app.
Dictionary<string, EventHandler> myEvents = null;
if (Session["Invokers"] == null)
{
myEvents = new Dictionary<string, EventHandler>();
Session["Invokers"] = myEvents;
}
else
{
myEvents = Session["Invokers"] as Dictionary<string, EventHandler>;
}
//If the event handler key is not in there then add it
if (myEvents.ContainsKey("buttonClickOnPageDefault") == false)
{
//Subscribe to event (i.e. add your method to the invokation list
this.TestEvent += new EventHandler(test);
myEvents.Add("buttonClickOnPageDefault", this.TestEvent);
}
else
{
//if it does contain this key then you may already be subscribed to event, so unsubscribe in case and then resubscribe...you could
//probably do this more elegantly by looking at the vales in the GetInvokationList method on the eventHandler
//Wire up the event
this.TestEvent -= new EventHandler(test);
this.TestEvent += new EventHandler(test);
}
//Resave the dictionary.
Session["Invokers"] = myEvents;
}
void test(object o, EventArgs e)
{
this.Page.Title = "testEvent";
}
public event EventHandler TestEvent;
protected void Button1_Click(object sender, EventArgs e)
{
if (Session["Invokers"] != null)
{
Dictionary<string, EventHandler> myEvents = (Dictionary<string, EventHandler>)Session["Invokers"];
if (myEvents.ContainsKey("buttonClickOnPageDefault"))
{
EventHandler ev = myEvents["buttonClickOnPageDefault"];
ev(null, EventArgs.Empty);
}
}
}
If you put the above code in an asp.net page it will never change page title, but if you put a breakpoint in the Test method you will see it being hit. The reason is that its being hit in a different page (and that page is out of scope and may not be garbage collected as your event still has a reference to it, so this could cause a memory leak...be careful with it!). Really you probably shouldn't be using your events this way (at least not to act on a page...maybe it has some utility for domain objects). Note that the following will work (as its acting on the same page)
protected void Page_Load(object sender, EventArgs e)
{
this.Page.Title = "test";
//Store it in your session...seems like a weird thing to do given how your page should be stateless, so I would think about what you are
//trying to do a bit more carefully. You don't want to call an event handler such as test below from another page in your asp.net app.
this.TestEvent += new EventHandler(test);
Session["Invoker"] = this.TestEvent;
}
void test(object o, EventArgs e)
{
this.Page.Title = "testEvent";
}
public event EventHandler TestEvent;
protected void Button1_Click(object sender, EventArgs e)
{
if (Session["Invoker"] != null)
{
EventHandler ev = (EventHandler)Session["Invoker"];
ev(null, EventArgs.Empty);
}
}
Hope that gives you some pointers to where your problem might be.