Caliburn.Micro Screen and Conductor Lifecycle documentation - c#

Does anyone know a source for an overview of the Caliburn.Micro Screen/Conductor Lifecycle? For example a sequence diagram / flow chart that describes their call order/dependencies and the conditions when they get called or not?
Until now I have primarily uses OnViewLoaded but I want to know which are called a second time (when shown again) etc. I didn't found a good documentation about the Screen Lifecycle yet.
And yes, I know it is Open Source and I can read the source code or debug that (what I'm doing at the moment)... just thought that this requirement is somewhat basic to work with Caliburn.Micro and there must be something already done and I don't need to create that overview on my own. Maybe the answer might help someone else, too. ;-)
For example, when derriving from Conductor.Collection.OneActive
there are the following (and even more) methods that seem to play a role in the lifecycle and can be overloaded:
protected virtual void OnInitialize()
protected virtual void OnActivate()
protected virtual void OnActivationProcessed(IScreen item, bool success)
protected virtual void OnDeactivate(bool close)
protected virtual void OnViewAttached(object view, object context)
protected virtual void OnViewLoaded(object view)
protected virtual void OnViewReady(object view)
What I have seen so far this seems to be the order (app startup to exit):
OnViewAttached
OnInitialize
OnActivate
OnViewReady
OnViewLoaded
OnActivationProcessed
OnDeactivate
But what are the bullet points for each method? E.g. when is the datacontext set, the style template applied to the view and ready to be shown? When is the view shown? (difference between ViewReady and ViewLoaded?)

Not a full answer but it is a start, from the documentation of this project, which is worth reading, you can find bullet points for some of those events:
OnInitialize – Override this method to add logic which should execute only the first time that the screen is activated. After initialization is complete, IsInitialized will be true.
OnActivate – Override this method to add logic which should execute every time the screen is activated. After activation is complete, IsActive will be true.
OnDeactivate – Override this method to add custom logic which should be executed whenever the screen is deactivated or closed. The bool property will indicated if the deactivation is actually a close. After deactivation is complete, IsActive will be false.
OnViewLoaded – Since Screen implements IViewAware, it takes this as an opportunity to let you know when your view’s Loaded event is fired. Use this if you are following a SupervisingController or PassiveView style and you need to work with the view. This is also a place to put view model logic which may be dependent on the presence of a view even though you may not be working with the view directly.
There is also a good explanation on the parameters being sent to the methods, and many other topics for the layers of the screens and their life cycle.

Related

How can I know when a view is finished rendering?

I've noticed that when OnElementPropertyChanged is fired on a VisualElement like a BoxView, the properties of the underlying platform view are not updated at that time.
I want to know when the VisualElement's corresponding platform view is finished rendering, something like:
this.someBoxView.ViewHasRendered += (sender, e) => {
// Here I would know underlying UIView (in iOS) has finished rendering
};
Looking through some code inside of Xamarin.Forms, namely VisualElementRenderer.cs, it would seem that I could raise an event after OnPropertyChanged has finished. Something like:
protected virtual void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
SetBackgroundColor(Element.BackgroundColor);
else if (e.PropertyName == Layout.IsClippedToBoundsProperty.PropertyName)
UpdateClipToBounds();
else if (e.PropertyName == PlatformConfiguration.iOSSpecific.VisualElement.BlurEffectProperty.PropertyName)
SetBlur((BlurEffectStyle)Element.GetValue(PlatformConfiguration.iOSSpecific.VisualElement.BlurEffectProperty));
// Raise event
VisualElement visualElement = sender as VisualElement;
visualElement.ViewHasRendered();
}
Naturally there's a few more complexities to adding an event to the VisualElement class, as it would need to be subclassed. But I think you can see what I'm after.
While poking around I've noticed properties on VisualElement like IsInNativeLayout. But that only seems to be implementing in Win/WP8. Also, UpdateNativeWidget on VisualElementRenderer as well, however I can't figure out the proper way to leverage them.
Any ideas?
Much appreciated.
TL;DR : Run away, do not go down this path...
On iOS everything that displays content to the screen happens within a UIView (or subclass) and drawRect: is the method that does the drawing. So when drawRect: is done, the UIView is drawing is done.
Note: Animations could be occurring and you might see hundreds of completed rendering cycles completed. You might need to hook into every animation's completion handler to determine when things really are done "rendering".
Note: The drawing is done off-screen and depending upon the iDevice, the screen refresh Hz could 30FPS, 60FPS or in the case of iPad Pro it is variable (30-60hz)...
Example:
public class CustomRenderer : ButtonRenderer
{
public override void Draw(CGRect rect)
{
base.Draw(rect);
Console.WriteLine("A UIView is finished w/ drawRect: via msg/selector)");
}
}
On Android the common way to draw content is via a View or subclass, you could obtain a surface, draw/bilt via OpenGL to a screen, etc... and that might not be within a View, but for your use-case, think Views..
Views have Draw methods you can override, and you can also hook into ViewTreeObserver and monitor OnPreDraw and OnDraw, etc, etc, etc... Sometimes you have to monitor the View's parent (ViewGroup) to determine when drawing is going to be done or when is completed.
Also all standard Widgets are completely inflated via xml resources and that is optimized so you will never see a Draw/OnDraw method call (Note: You should always(?) get a OnPreDraw listener call if you force it).
Different Views / Widgets behave differently and there no way to review all the challenges you will have determining when a View is really done "rendering"...
Example:
public class CustomButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer,
ViewTreeObserver.IOnDrawListener, ViewTreeObserver.IOnPreDrawListener
{
public bool OnPreDraw() // IOnPreDrawListener
{
System.Console.WriteLine("A View is *about* to be Drawn");
return true;
}
public void OnDraw() // IOnDrawListener
{
System.Console.WriteLine("A View is really *about* to be Drawn");
}
public override void Draw(Android.Graphics.Canvas canvas)
{
base.Draw(canvas);
System.Console.WriteLine("A View was Drawn");
}
protected override void Dispose(bool disposing)
{
Control?.ViewTreeObserver.RemoveOnDrawListener(this);
Control?.ViewTreeObserver.RemoveOnPreDrawListener(this);
base.Dispose(disposing);
}
protected override void OnElementChanged(ElementChangedEventArgs<Button> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
Control?.SetWillNotDraw(false); // force the OnPreDraw to be called :-(
Control?.ViewTreeObserver.AddOnDrawListener(this); // API16+
Control?.ViewTreeObserver.AddOnPreDrawListener(this); // API16+
System.Console.WriteLine($"{Control?.ViewTreeObserver.IsAlive}");
}
}
}
Misc:
Note: Layout Optimizations, Content caching, GPU caching, is hardware acceleration enabled in the Widget/View or not, etc... can prevent the Draw methods from being called...
Note: Animation, effects, etc... can cause these these methods to be call many, many, many times before an area of the screen is completely finished displaying and ready for user interaction.
Personal Note: I've gone down this path once due to a crazy client requirements, and after banging my head on the desk for some time, a review of the actual goal of that area of the UI was done and I re-wrote the requirement and never tried this again ;-)
I'm going to answer my own question it hopes that the solution will help someone who is struggling with this issue in the future.
Follow #SushiHangover's advice and RUN don't WALK away from doing something like this. (Although his recommendation will work and is sound). Attempting to listen/be notified when the platform has finished rendering a view, is a terrible idea. As #SushiHangover mentions there's simply too many things that can go wrong.
So what brought me down this path?
I have a requirement for a pin code UI similar to the one in iOS to unlock your device, and in many other apps. When a user presses a digit on the pad I want to update the corresponding display "box" (boxes above the pad). When the user inputs the last digit I want the last "box" to be filled in, in my case a Background color change, and then execution to continue in which the view would transition to the next screen in the workflow.
A problem arose as I tried to set the BackgroundColor property on the fourth box and then transition the screen. However, since execution doesn't wait for the property to change the screen transitions before the change is rendered. Naturally this makes for a bad user experience.
In an attempt to fix it, I thought "Oh! I simply need to be notified when the view has been rendered". Not smart, as I've mentioned.
After looking at some objective C implementations of similar UIs I realizes that the fix it quite simple. The UI should wait for a brief moment and allow the BackgroundColor property to render.
Solution
private async Task HandleButtonTouched(object parameter)
{
if (this.EnteredEmployeeCode.Count > 4)
return;
string digit = parameter as string;
this.EnteredEmployeeCode.Add(digit);
this.UpdateEmployeeCodeDigits();
if (this.EnteredEmployeeCode.Count == 4) {
// let the view render on the platform
await Task.Delay (1);
this.SignIn ();
}
}
A small millisecond delay is enough to let the view finished rendering without having to go down a giant rabbit hole and attempt to listen for it.
Thanks again to #SushiHangover for his detailed response. You are awesome my friend! :D

what the equivalent to OnResume on Xamarin.forms

Right now, I am using Xamarin.Forms PLC project.
I have a label[x] on Page[x], then I will press button and go to Page[xx] ,then I will go back to Page[x] but I need to update Label[x] Text upon some Choices selected on Page[xx].
Which Event should i use to update Label.Text?
I was overriding OnResuem()Function on Xamarin.android, but it is not working on Xamarin.forms, I have no idea which is the best solution.
Some quick solution for this is:
-overriding the OnAppearing() method of the page and change the label.Text property once you change it on the other page
-Change the property to be public and change it on the other page
-Send the property to the next page as a parameter
but what you should do! is bind your property to a ViewModel and use OnPropertyChange() (Xamarin.Forms way and MVVM architecture) Events: a couple of tutorials how to understand this better:
https://blog.xamarin.com/advanced-data-binding-for-ios-android-and-windows/
https://developer.xamarin.com/guides/xamarin-forms/user-interface/xaml-basics/data_bindings_to_mvvm/
https://developer.xamarin.com/guides/xamarin-forms/user-interface/xaml-basics/data_binding_basics/
I'm not sure how youre code works because you havent said. So I'm not sure how Page[x] knows about Page[xx] but it sounds to me like you want to use the OnAppearing() override.
Which from the Xamarin.Forms Page API documentation states:
When overridden, allows application developers to
customize behavior immediately prior to the Page becoming visible.
You could do this by adding the following to your Page[x].xaml.cs file
protected override void OnAppearing()
{
//Your code here
}

Xamarin Forms: ContentPage OnCreate event?

I'm creating a messaging app, and I need that when I enter the messaging thread with someone, message bubbles show inside the layout. I'm doing this easily using:
MyMessageThreadStackLayout.Children.Add(
// Message bubble building logic goes here
);
But currently, I'm putting this inside the:
protected override void OnAppearing ()
But this makes my elements appear after the content page animation is shown making it look really weird. Is there a OnCreate event that can let me load the messages into the view before showing them with the animation? Thank you
You could use the protected virtual void LayoutChildren method that's exposed on all Pages. Keep in mind it may be called many times, so you'll need to account for that possibility.
Another option would be to simply add your child Views in the Page's constructor. This option is not ideal, but it's guaranteed to only be called once, and always before your Page is presented.

Using EventHandler with NavigationService

I programming a WPF GUI that uses multiple Views. I am using the MVVM Light Toolkit to implement the MVVM pattern.
For Navigating i use this mechanism by changing my main frame to a NavigationWindow and all my views to Pages.
I injected the navigation service in the ViewModel constructor and now I can navigate between the views.
However, i would like to transmit data between the views while navigating. There is a method from Navigation Window that makes this possible through event handlers. I already implemented a method into my Interface but I'm having problems calling the event handler on the navigated View Model.
Can anyone tell me how to call the event handler inside my ViewModel?
Thanks!!
Edit: I tried calling:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
}
But i get an error saying:
OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs)': no
suitable method found to override
I already cleaned my solution and tried a rebuild...
Any ideas?
Edit2:
So i found out that in WPF .Net 4.5 the OnNavigatedTo event is gone. What i couldnt find out why and how i could call something similiar.
I haven't gotten an answer to my question so I will attempt an answer...
Don't over think this...if one needs to transfer information either create a static link to the VM(s) in question, or set aside a static drop on the app class. Either way when a view is shown, subscribe to one of the initialization/load events and pick up the information at the predetermined location.

When to Call Method after Property Change

I have a User Control that contains a list of items and I raise an event when the currentIndex changes, also, when it changes, I must call two other methods two verify and change the appearance of the Control (change an Image and block/unblock some buttons).
What I want to know, mostly out of curiosity because it is already working, is when is it more appropriate to call these two methods?
Should I call them within the CurrentIndex property per se? Should I call them within the OnCurrentIndexChanged(...)? Should I handle the event within the class and do it there?
I'll assume you've implemented the standard event generating pattern and made OnCurrentIndexChanged protected virtual so that a derived class can override the method and alter the event generation and/or handling.
Unfortunately that requires reading tea leaves, why would anybody want to override the method? And more seriously, how could overriding the method break your control when they do? That's awfully hard to guess at for anybody that doesn't know the code well, not exactly easy for you either. The principle to apply here, used in the .NET framework code as well, is to do as little as possible. Just raise the event, nothing else. Which minimizes the odds of breakage when the derived class does something silly, but entirely common, like not calling base.OnCurrentIndexChanged.
The behavior of your controls is an implementation detail of your UserControl. So change their properties in your CurrentIndex property setter, then call OnCurrentIndexChanged(). Anybody that derives from your class can override that behavior, if necessary. And nothing goes wrong when they forget to call your OnCurrentIndexChanged() method. But do note that you need to make the control variables protected instead of private. So they can override the behavior, if they need to.
And don't hesitate to just not use a virtual method at all if this is too spooky for you. It's not common to have to accommodate hundreds of thousands of programmers with your controls :)
In the user control, I would have a property that represents the selected item. Then, during the setter of the object, raise the event method to change your user control. That way, in the future, if you need to add more listeners, you just need to add another handler in the setter method. This is pretty common in MVVM applications and is pretty maintainable.
Because your UserControl acts as a ListControl, you need to implement two events and two properties.
public event System.EventHandler SelectedIndexChanged;
public event System.EventHandler SelectionChangeCommitted;
public int SelectedIndex {
get;
set;
}
public T SelectedItem { // Where T is whatever your type is
get;
set;
}
SelectedIndexChanged should always be used for actions that always need to be triggered when your selected index is changed. SelectionChangeCommitted should only be triggered when the user physically changes the selection. The separation between the two is an important distinction, and most controls in .NET follow this pattern (eg. ComboBox), but may not use the same name for the events.
Now, with that said, if the controls you need to change properties for are also within the same user control, then you should of course handle that within the user control code in the appropriate event. Otherwise, the code should be orphaned to whoever implements the user control (eg. a form or another user control) by subscribing to the event and doing the work there.
The order really depends on your requirements, but SelectedIndexChanged should always be raised (but not more than once per change as that would introduce strange behavior), and again SelectionChangeCommitted should only be raised by the user (eg. setting SelectedIndex or SelectedItem).
A good rule of thumb is if your internal stuff MUST happen before the user knows about it, call SelectedIndexChanged first, then SelectionChangeCommitted. If it doesn't matter, either or. Changing the order later on could result in breaking changes in whoever implements the control, so make sure your decision is solid.
The difference between the two is SelectedIndex and SelectedItem would be updated by things like clearing your list internally, adding new items, et cetera, but does not necessarily mean it was a physical user action that should result in both your events firing.
Hope this helps.

Categories

Resources