caliburn micro - run code when view is visible? - wpf

I need to run some code (let's say to show message box) right after the view is displayed. I tried to override OnInitialize, OnViewLoaded or OnViewAttached but it's always the same. The View is not fully displayed yet.
I use some animation when displaying view but at the same time need to load some data into grid. If I put data load into OnInitialize, OnViewLoaded or OnViewAttached the animation is not smooth as it's kind of happening the same time when loading data.
Any thoughts?

Give something like this a try - use a couroutine to wait for the animation to complete before binding the grid:
private IEnumerator<IResult> ViewModelStartup()
{
yield return new WaitForAnimation("AnimationName");
BindData();
}
(note - you can load the data async, but just don't assign it)
Then when your form loads:
private void OnViewAttached()
{
Coroutine.BeginExecute(ViewModelStartup(), new ActionExecutionContext() { View = this.GetView() });
}
(the code above might not be 100%... I think View must be FrameworkElement in ActionExecutionContext so cast as needed or create a wrapper class)
The implementation of the WaitForAnimation coroutine would search the view for a named animation and wait for it to complete before firing the callback. You should probably just fire the callback if the animation could not be found. The couroutine could be used on multiple views.
(Coroutines must implement IResult have a look at the docs on the CM codeplex site for info)
http://caliburnmicro.codeplex.com/wikipage?title=IResult%20and%20Coroutines&referringTitle=Documentation

Related

Prism/MVVM running code after switching a view

I started to use Prism (5.0) some days ago and now I run into a problem I cant solve myself.
I used the quickstart "viewswitchingnavigation".
So I have a shell with a navigation and a main panel/region.
Navigation region holds some radiobuttons to switch the views that are split into view and viewmodel with a service based model.
View A shows a collection of documents with a button each to open a new view B for this document's details.
A button in this detailview should start a part of code. This code moves some data what takes quite a while since the data need to be parsed. Since the user shouldnt wait for a nonreactiong window I want to show some information about the status of the running code. (Example: Getting data 1/3012, which is updated with each new got data piece). This code gets one piece of data at a time so I could send(?) some information to the view to update the status there (if I knew how)
So.
How to implement the button that starts the "external" code?
How to access the current view (e.g. to change the status shown in a loader or in a textbox like destribed above.
You could use a BackgroundWorker class which has built-in progress reporting functionality. Alternatively, you could create your own asynchronous data retreiving mechanism by using async / await and Tasks.
For your progress displaying view you could use the State-Based Navigation provided by Prism. You could display an on-top view with a progress bar or text box showing the progress. As usual, the UI elements of the view should be bound to the view model's properties. To update these UI properties you should use Dispatcher.Invoke(), or SynchronizationContext, or similar sync mechanism, because your progress reporting method (or event) will be called by a background thread.
If you could post your code, then my answer could be more specific.
Not exactly sure what you aim for, though here are two options:
Shall the data begin to be fetched when the View/Windows gets loaded? Then your ViewModels have to implement INavigationAware interface and INavigationAware.OnNavigatedTo (and From) Method). You can mark that method as async to be able to use await within it.
public async void INavigationAware.OnNavigatedTo(NavigationContext navigationContext)
{
await LongRunningTask();
}
public async Task LongRunningTask()
{
foreach(var dataChunk in allData)
{
// Status Text is a property which raises OnPropertyChanged when changed and which you databind to a textbox
StatusText = string.Format("Getting Data {0}", dataChunk.Description);
await ProcessDataChunk(dataChunk);
}
}
Or if you want it happen on an user action, you do it via an async ICommand.
private ICommand processCommand;
public ICommand ProcessCommand)
{
return processCommand??(processCommand = DelegateCommand.FromAsyncHandler(this.LongRunningTask));
}

Why should you put custom event listeners under intitalize in backbone?

I'm wondering why you need to put a object.on(event, callback, [context]) specifically within an initialize method in backbone, and not somewhere else?
Is it because the initialize method runs automatically -> and the listener starts to listen automatically for that reason?
A better way to design a backbone application is to separate data loading code and views construction.
This can be done by using a separate controller(A custom javascript object) that does the data load part i.e, initializing your models or collections. The controller loads the data (models/collections) necessary for a particular view.
The view instance is then created. Data required for the view can be passed as parameters to the view. One of the better ways is to pass it to use the initialize method which gets invoked automatically during the instantiation of the view.
Also we will expect the view to reflect the changes that model/collection undergoes. Its good to define what events of the model/collection that must be listened to in the intialize method since:
You know that it will be called for sure.
You know for sure that the event binding will be set, since you can reach a view from more than one way like thru a route or a click event on tabs for instance (though better designing is that you have a center piece of code which handles all the ways you reach a view).
Also set the rules for event bindings during the initialization itself.
You can also refer to the below link that talks about decoupling view from data loading.
Should the backbone router or view handle fetching data and displaying loading status?

Pass parameter out from Ext.window.Window to an Ext.panel.Panel on close

I have a main window with a custom Ext.panel.Panel using ExtJS 5.0.1.
Clicking an item shown on the custom panel creates and shows a custom Ext.window.Window for editing said item.
For the purpose of getting some kind of answer, or a starting point for my own situation, I'll ask for standard Ext components.
On closing the custom window, a variable in the custom panel needs to be updated to show that changes have been made.
This is a different requirement to a confirmation message before closing the window; the window does not control saving of information to a DB, but needs to let another component know that changes have been made.
This should be as simple as setting a boolean unsavedChanges to true on the panel if the window's information has been altered; I've performed this on a simpler page, which did not have the additional window as an obstacle.
Due to the use of the window, my typical go-to methods of calculating a variable as use of this.up or this.lookupReference returns null, or throw an error.
I'm aware that I can consider going to the parent of the page, then try and work down, but I'm not experienced enough to consolidate standard javaScript with the up and down capabilities of ExtJS, or at least understand the intricacies of ExtJS to navigate past the "item window" back to the panel.
How would I be able to refer to the panel while closing the window in order to update the panel's data?
Well, there's a couple of ways.
First, you can pass in your panel, or a callback function, during the construction of the window. e.g.:
// in panel
var window = Ext.create('MyWindow', { callingPanel: this })
...
// in window
onClose: function() { this.callingPanel.doStuff(); }
The other way to be to register a listener to the 'close' event of the window.
// in panel
var window = Ext.create('MyWindow', {
listeners: {
scope: this,
close: this.doStuff
}
})
The listener approach tends to work best when you don't want to tightly couple the window to the calling panel.

Lightswitch modal window

I've got standard CreateNewEntity screen. Entity can contain list of entities of some other type. By default there is an add button that opens modal window when user wants to add another entity into collection. However, default modal window was lacking some of the needed functionality so I've done a bit of research. Turns out that default modal screens cannot be modified. So, I found a nice custom modal window helper class. The problem is that I can't seem to be able to access modal window fields in order to enforce needed logic. There are two dropdown lists that are associated. Change in one will result in limiting the other dropdown list options. I'm stuck at this particular part:
var proxy = this.FindControl("DodavanjeParcele");
proxy.ControlAvailable += (s, e) =>
{
var ctrl = e.Control as System.Windows.Controls.Control;
//how to obtain access to ctrl fields?
};
"DodavanjeParcele" is custom modal window. Before this, modal window is instantiated and initialized. It pops up after button click and functions as expected. The only thing missing are above-mentioned rules. I need to set change event handlers for modal window fields in order to define rules. As seen above I tried to cast IProxy as a standard Windows control. This is where I got stuck. I can't seem to find a way to access control fields and set event handlers. Any thoughts?
If I understand you correctly, I'm not sure why you need to search through controls or cast anything.
Control1 is an entity which creates an AutoComplete Box (dropdown list). That selection is copied into a local property in the Control1_Changed method. That property is used as a parameter in a filter query to create Control2.
C#:
private void Control1_Changed()
{
this.MyLocalProperty = this.Control1.SelectedItem;
}
VB.NET:
Private Sub Control1_Changed()
Me.MyLocalProperty = Me.Control1.SelectedItem
End Sub
Just make sure you have Auto Execute Query checked in Control2's Properties and the second control should update and filter when Control1 changes the query parameter.
The code in my screen shots all takes place inside of Yann's Modal Helper so there is nothing special you need to do.

Backbone.Marionette onRender callback fires before view is rendered in browser?

The setting
I have Backbone.Marionette.ItemView which renders some content. When the content is rendered I'd like to apply a jQuery plugin which turns part of the view into a container with a scrollbar.
The scrollbar is implemented completely in javascript and upon initialization it has to check the height of the scroll-container as well as that of the content inside the container.
If the content is higher then the container, a scrollbar should be enabled.
The problem
While this all sounds simple, I've ran into a strange problem:
If I initialize my scrollbar plugin directly in the onRender callback it seems to think the .scroll-container element has a height 0 and maxHeight of 0.
If I wrap the initialization code inside a 0ms timeout though, everything works as it should, the .scroll-container element's height property is returned by jQuery correctly and the scrollbar plugin works great.
The code
onRender: function() {
var that = this;
setTimeout(function() {
that.onLayout();
var $scrollContainer = that.$el.find('.scroll-container'),
scrollPane = new ScrollPane($scrollContainer, {
maxHeightProperty: 'maxHeight',
scrollUpButton: false,
scrollDownButton: false
});
}, 0);
},
The question
I'm assuming the problem occurs because the browser didn't finish actually rendering the newly inserted html when the onRender callback is executed.
Is this assumption correct? And if so, is my solution of using a 0ms timeout reliable under normal circumstances?
Since the plugin in question depends on the DOM, onRender won't do what you need. This callback kicks off after the view has been rendered, but there is no guarantee that the view's el has been added to the DOM yet. In fact, you are safe assuming the opposite - that it has not been added yet.
If you're using a Marionette Region to show the view, you can implement an onShow method in your view. This method gets called by the region, after the region has added the view to the DOM. It was implemented specifically to handle this situation.
For a little more info on this, and on working with jQuery plugins in general, see this blog post: http://lostechies.com/derickbailey/2012/02/20/using-jquery-plugins-and-ui-controls-with-backbone/

Resources