I'm trying to figure out how can I use DispatcherHelperftom MVVM light toolkit in SL, but I can't find any example.
From home page of this framework I know that
DispatcherHelper class, a lightweight class helping you to create
multithreaded applications.
But I don't know how to use it.
How and for what I can use it?
You only need the DispatcherHelper when yo want to make changes to components on your UI thread, from code that runs on a different thread. E.g. in an Silverlight application you call a web service to retrieve some data asynchroneously, and now want to inform the Ui that the data is present via a OnNotifyPropertyChanged event.
First you have to initialize the DispatcherHelper. In Silverlight you do this in Application_Startup:
//initialize Dispatch helper
private void Application_Startup( object sender, StartupEventArgs e) {
RootVisual = new MainPage();
DispatcherHelper.Initialize();
}
In WPF the initialization is done in the static constructor of you App class:
static App() {
DispatcherHelper.Initialize();
}
Then in your event, handling the completion of your asnc call, use the following code to call RaisePropertyChanged on the UI thread:
DispatcherHelper.CheckBeginInvokeOnUI(
() => RaisePropertyChanged(PowerStatePropertyName)
);
DispatcherHelper.BeginInvokeOnUl expects an Action so you can use any code in here just use
DispatcherHelper.CheckBeginInvokeOnUI(
() => { /* complex code goes in here */ }
);
to do more complex tasks.
Related
I'm using nServiceBus 5 and have created a number of host endpoints, two of which listen for database changes. (The specifics of how to do this can be found here). The intention is to have a service running in the background which publishes an event message using the Bus when notified to do so by the database listener.
The code which creates the database listener object and handles events is in the Start method, implemented as part of IWantToRunWhenBusStartsAndStops.
So - Is putting the code here likely to cause problems later on, for example if an exception is thrown (yes, I do have try/catch blocks, but I removed them from the sample code for clarity)? What happens when the Start method finishes executing?
Would I be better off with a constructor on my RequestNewQuoteSender class to instantiate the database listener as a class property and not use the Start method at all?
namespace MySample.QuoteRequest
{
public partial class RequestNewQuoteSender : IWantToRunWhenBusStartsAndStops
{
public void Start()
{
var changeListener = new DatabaseChangeListener(_ConnectionString);
// Assign the code within the braces to the DBListener's onChange event
changeListener.OnChange += () =>
{
// code to handle database change event
changeListener.Start(_SQLStatement);
};
// Now everything has been set up.... start it running.
changeListener.Start(_SQLStatement);
}
public void Stop() { LogInfo("Service Bus has stopped"); }
}
}
Your code seems fine to me.
Just a few small things:
Make changeListener a class field, so that it won't be GC (not 100% sure if it would be but just to make sure);
Unsubscribe from OnChange on the Stop() method;
You may also want to have a "lock" around changeListener.Start(_SQLStatement); and the Stop so that there are no racing conditions (I leave that one up to you to figure out if you need it or not);
Does this make sense ?
What I'm trying to achieve is creating an application that is highly modular. I'm trying to create a cron script which addresses all the cron scripts that need to be fired in all the sub-modules. What I would actually would like to do, is create an event, say runCron that gets fired from a CController and then hook into that when it get's raised from within the sub modules with onRunCron methods. Outlining what I'm trying to do:
Application
|- CronController.php - Raise the event 'RunCron', not knowing which modules will fire after this.
|- Modules
|- Nodes
|- Observe the event 'onRunCron' and run own cron script
|- Users
|- Observe the event 'onRunCron' and run own cron script
What I think I will need to do according to Yii's event system, is create the event and raise it at an application-level (that is still what I'm trying to do) but then; I also need to assign the callbacks from the submodules on an application-level in the controller. Which I do not want, since when a submodule gets added / deleted, the application needs to be adjusted, which does not sound modular at all.
Could someone line out the basics of setting up a event like this and making it as modular as possible? Since I think I'm looking at this completely the wrong way.
Edit (solution):
Thanks to acorncom's answer I've managed to work out the following system.
application.components.CronSingleton
<?php
class CronSingleton extends CApplicationComponent {
/**
* Make sure we "touch" the modules, so they are initialised and are able to attach their listeners.
*/
public function touchModules () {
$modules = Yii::app()->getModules();
if (!empty($modules)) {
foreach ($modules as $name => $module) {
Yii::app()->getModule($name);
}
}
}
/**
* This method should be run to run the cron. It will commense all the procedures in cronjobs
*/
public function execCron($caller) {
$this->touchModules();
$this->onStartCron(new CEvent($caller));
$this->onRunCron(new CEvent($caller));
$this->onExitCron(new CEvent($caller));
}
/**
* Raise an event when starting cron, all modules should add their listeners to this event if they would like to fire something before running the cron.
*/
public function onStartCron ($event) {
$this->raiseEvent('onStartCron', $event);
}
/**
* Raise an event when running cron, all modules should add their listeners to this event to execute during cron run. Mosty this event should be used.
*/
public function onRunCron ($event) {
$this->raiseEvent('onRunCron', $event);
}
/**
* Raise an event when cron exits, all modules should add their listeners to this event when cron exits.
*/
public function onExitCron ($event) {
$this->raiseEvent('onExitCron', $event);
}
}
?>
application.controllers.CronController
<?php
class CronController extends Controller
{
public $layout='//layouts/bare';
public function actionIndex($k) {
Yii::app()->cron->onStartCron = array($this, 'startcron');
Yii::app()->cron->onRunCron = array($this, 'runcron');
Yii::app()->cron->onExitCron = array($this, 'exitcron');
Yii::app()->cron->execCron($this);
}
public function startcron(){
var_dump('CronController - Starting cron');
}
public function runcron(){
var_dump('CronController - Run cron');
}
public function exitcron(){
var_dump('CronController - Ending cron');
}
}
?>
application.modules.admin.AdminModule
<?php
class AdminModule extends CWebModule
{
public function init()
{
// this method is called when the module is being created
// you may place code here to customize the module or the application
// import the module-level models and components
$this->setImport(array(
'admin.models.*',
'admin.components.*',
));
Yii::app()->cron->onRunCron = array($this, 'runCron');
}
public function runCron($event) {
var_dump('AdminModule - Run cron');
}
public function beforeControllerAction($controller, $action)
{
if(parent::beforeControllerAction($controller, $action))
{
// this method is called before any module controller action is performed
// you may place customized code here
return true;
}
else
return false;
}
}
?>
This "proof of concept" setup manages to print out the following result, exactly what I wanted it to do:
string(30) "CronController - Starting cron"
string(25) "CronController - Run cron"
string(22) "AdminModule - Run cron"
string(28) "CronController - Ending cron"
I think you'll want to do something like the following (note: this hasn't been tested, but it should work conceptually).
Create a CApplicationComponent for your cron system. Having it be an application component (registered in your config/main.php file) makes it accessible from anywhere in your app / modules / sub modules / controllers, etc
Have your cron component handle the registration of events / firing of events. Refer to the Yii events page for more info on the details of how this works. Note: you can create your own custom events that subclass the main CEvent class if you need to pass in additional parameters about your events.
Have your modules register as event handlers with your cron component as they initialize.
Have your controller fire off an event to your cron component.
One potential gotcha. I'm not sure whether modules or components are registered first (I believe components should be, but it's worth testing). If your cron component is loading after the modules that are trying to register events with your cron component, then you may want to preload your cron component. There are a few other hacks you can try if that doesn't work (come back and ask for more details).
Oh, and let us know how it goes!
Have you checked the wikis on yii's website about events? I think it's a good place to start, then if you still have some questions we could help you!
Events explained
Behaviors & events
The problem: I'm crashing when I want to render my incoming data which was retrieved asynchronously.
The app starts and displays some dialog boxes using XAML. Once the user fills in their data and clicks the login button, the XAML class has in instance of a worker class that does the HTTP stuff for me (asynchronously using IXMLHTTPRequest2). When the app has successfully logged in to the web server, my .then() block fires and I make a callback to my main xaml class to do some rendering of the assets.
I am always getting crashes in the delegate though (the main XAML class), which leads me to believe that I cannot use this approach (pure virtual class and callbacks) to update my UI. I think I am inadvertently trying to do something illegal from an incorrect thread which is a byproduct of the async calls.
Is there a better or different way that I should be notifying the main XAML class that it is time for it to update it's UI? I am coming from an iOS world where I could use NotificationCenter.
Now, I saw that Microsoft has it's own Delegate type of thing here: http://msdn.microsoft.com/en-us/library/windows/apps/hh755798.aspx
Do you think that if I used this approach instead of my own callbacks that it would no longer crash?
Let me know if you need more clarification or what not.
Here is the jist of the code:
public interface class ISmileServiceEvents
{
public: // required methods
virtual void UpdateUI(bool isValid) abstract;
};
// In main XAML.cpp which inherits from an ISmileServiceEvents
void buttonClick(...){
_myUser->LoginAndGetAssets(txtEmail->Text, txtPass->Password);
}
void UpdateUI(String^ data) // implements ISmileServiceEvents
{
// This is where I would render my assets if I could.
// Cannot legally do much here. Always crashes.
// Follow the rest of the code to get here.
}
// In MyUser.cpp
void LoginAndGetAssets(String^ email, String^ password){
Uri^ uri = ref new URI(MY_SERVER + "login.json");
String^ inJSON = "some json input data here"; // serialized email and password with other data
// make the HTTP request to login, then notify XAML that it has data to render.
_myService->HTTPPostAsync(uri, json).then([](String^ outputJson){
String^ assets = MyParser::Parse(outputJSON);
// The Login has returned and we have our json output data
if(_delegate)
{
_delegate->UpdateUI(assets);
}
});
}
// In MyService.cpp
task<String^> MyService::HTTPPostAsync(Uri^ uri, String^ json)
{
return _httpRequest.PostAsync(uri,
json->Data(),
_cancellationTokenSource.get_token()).then([this](task<std::wstring> response)
{
try
{
if(_httpRequest.GetStatusCode() != 200) SM_LOG_WARNING("Status code=", _httpRequest.GetStatusCode());
String^ j = ref new String(response.get().c_str());
return j;
}
catch (Exception^ ex) .......;
return ref new String(L"");
}, task_continuation_context::use_current());
}
Edit: BTW, the error I get when I go to update the UI is:
"An invalid parameter was passed to a function that considers invalid parameters fatal."
In this case I am just trying to execute in my callback is
txtBox->Text = data;
It appears you are updating the UI thread from the wrong context. You can use task_continuation_context::use_arbitrary() to allow you to update the UI. See the "Controlling the Execution Thread" example in this document (the discussion of marshaling is at the bottom).
So, it turns out that when you have a continuation, if you don't specify a context after the lambda function, that it defaults to use_arbitrary(). This is in contradiction to what I learned in an MS video.
However by adding use_currrent() to all of the .then blocks that have anything to do with the GUI, my error goes away and everything is able to render properly.
My GUI calls a service which generates some tasks and then calls to an HTTP class that does asynchronous stuff too. Way back in the HTTP classes I use use_arbitrary() so that it can run on secondary threads. This works fine. Just be sure to use use_current() on anything that has to do with the GUI.
Now that you have my answer, if you look at the original code you will see that it already contains use_current(). This is true, but I left out a wrapping function for simplicity of the example. That is where I needed to add use_current().
One of the many benefits of implementing any pattern is to have a separation of concerns between the different layers in an application. In the case of Silverlight and MVVM it is my opinion that the NavigationService belongs to the UI.
If the NavigationService belongs to the UI then it should be used in the XAML code behind, but the commands happens on the ViewModel. Should I raise an event on the Command in the ViewModel and let the View handle the event and call the Navigation? That sounds a little absurd if all I'm doing is simply navigating to another page. Shouldn't I just handle the UI event directly and navigate from there?
View Control Event -> ViewModel Command -> Raise Event -> View
Handled Event -> Navigation
or
View Control Event -> View Handled Event -> Navigation
There are two documented approaches to this problem
Implementing the navigation using MVVM Light's messaging functionality This approach was put forward by Jesse Liberty in Part 3 his MVVM Ligtht soup to nuts series. His approach is to send a message from the command to the view indicating that a navigation operation should take place.
Implementing a ViewService that handles the navigationThis approach was Laurent Bugnion's response to Jesse's post. This implements a service that handles all navigation operations triggered by the view models.
Both approaches deal only with navigation in WP7 applications. However, they can be adapted to Silverligt applications too.
Jesse's approach is easier to use in SL as it does not require access to the root visual. However, the navigation code gets distributed in several places and requires code behind to do the actual navigation.
Laurent's approach requires access to the root visual - which is used for accessing the built-in navigation functionality. Getting access to this, as shown in Laurent's code, is no big deal in WP7 applications. In SL applications, however, it is slightly more complicated as there is no sourrounding frame. However, I alreay implented the pattern for SL in one of my projects using an attached property do do the required wiring - so although requires more work, it is usable for SL as well.
So concluding - although, Jesse's approach is easier to implement, personally I prefer Laurent's approach for it is cleaner architecture - there is no code behind required, and the functioality is encapsulated into a separate component and thus located at a single point.
A bit late to this question, but it is relevant and will hopefully be of benefit to someone. I had to create a SL4 application with MvvmLight and wanted to use a navigation service wrapper that was mock-able and could be injected into the ViewModel. I found a good starting point here: Laurent Bugnion's SL4 sample code samples from Mix11 which includes a navigation service demo: Deep Dive MVVM Mix11
Here are the essential parts for implementing a mock-able navigation service that can be used with Silverlight 4. The key issue is getting a reference to the main navigation frame to be used in the custom NavigationService class.
1) In MainPage.xaml, the navigation frame is given a unique name, for this example it will be ContentFrame:
<navigation:Frame x:Name="ContentFrame" Style="{StaticResource ContentFrameStyle}"
Source="/Home" Navigated="ContentFrame_Navigated"
NavigationFailed="ContentFrame_NavigationFailed">
<!-- UriMappers here -->
</navigation:Frame>
2) In MainPage.xaml.cs, the navigation frame is exposed as a property:
public Frame NavigationFrame
{
get { return ContentFrame; }
}
3) The navigation service class implements the INavigationService interface and relies on the NavigationFrame property of MainPage.xaml.cs to get a reference to the navigation frame:
public interface INavigationService
{
event NavigatingCancelEventHandler Navigating;
void NavigateTo(Uri uri);
void GoBack();
}
public class NavigationService : INavigationService
{
private Frame _mainFrame;
public event NavigatingCancelEventHandler Navigating;
public void NavigateTo(Uri pageUri)
{
if (EnsureMainFrame())
_mainFrame.Navigate(pageUri);
}
public void GoBack()
{
if (EnsureMainFrame() && _mainFrame.CanGoBack)
_mainFrame.GoBack();
}
private bool EnsureMainFrame()
{
if (_mainFrame != null)
return true;
var mainPage = (Application.Current.RootVisual as MainPage);
if (mainPage != null)
{
// **** Here is the reference to the navigation frame exposed earlier in steps 1,2
_mainFrame = mainPage.NavigationFrame;
if (_mainFrame != null)
{
// Could be null if the app runs inside a design tool
_mainFrame.Navigating += (s, e) =>
{
if (Navigating != null)
{
Navigating(s, e);
}
};
return true;
}
}
return false;
}
}
I am new to creating WCF projects as well as windows phone 7.
I created a simple method in WCF which just returns a list of an object.
public List<Sticky> GetSticky()
{
return stickys;
}
I then used it very simply
Sticky[] test = client.GetSticky();
When I import the WCF dll via a service reference into a console app the method acts how it should. When I import the method into a Windows Phone 7 application it become an async method (not sure what this means)and doesnt return a list, it comes up void.
client.GetStickyAsync();
If anyone can help explain what is going on and help me to be a little less confused.
Silverlight wants you to avoid making blocking service calls on the UI thread, so it forces you to use the non-blocking, async version of WCF method calls. This means that the call returns immediately and you must get the result of the call with the related event. What you need to do is register an event handler before you make the call.
client.GetStickyCompleted
+= new EventHandler<ServiceClient.GetStickyCompletedEventArgs>(client_GetStickyCompleted);
client.GetStickyAsync();
The result of your method call is one of the parameters passed into the event handler, like such
void client_GetStickyCompleted(object sender, ServiceClient.GetStickyCompletedEventArgs e)
{
List<Sticky> retList = e.Result;
}