I have my application designed using Xamarin Prism .
I have a command that should be executed when the user navigate to this given page .
it runs in a different project , but when I rewrote the same code , it will not run .
I compared the codes and almost identical . however , it still will not run in this code .
first I defined the command :
public IAsyncCommand InitCommand { get; }
in my view model constructor I have this code :
InitCommand = new AsyncCommand(InitAsync);
and this is my InitAsync method :
private async Task InitAsync()
{
await GetTransactionsByWallet(WalletId).ConfigureAwait(false); ;
}
and in my OnNavigatedTo part of prism , i have this code :
public void OnNavigatedTo(INavigationParameters parameters)
{
WalletId = parameters.GetValue<IWallets>("wallet").Id;
InitCommand.ExecuteAsync();
}
note :
InitCommand.ExecuteAsync(); will not execute even if i put a stop at it to debug it , the debugger also wont stop to it .
im using AsyncAwaitBestPractices.MVVM for the creation of the command .
Any reason you have decided to run the command rather than the method directly? i.e. instead of InitCommand.ExecuteAsync(); why not just await InitAsync().
Note: you can make OnNavigatedTo an async method. And since it is an eventhandler (the events being fired during navigation), async void is allowed - https://learn.microsoft.com/en-us/archive/msdn-magazine/2013/march/async-await-best-practices-in-asynchronous-programming
Here - https://forums.xamarin.com/discussion/91897/prism-calling-async-method-in-onnavigatedto-or-any-of-the-methods-from-inavigationaware
As mentioned by Brian Lagunas (manager of the Prism Library)
But if that option doesnt sound good - another option is to run the method during the "Loaded" event of the view. These are not workarounds, these are the proper mvvm ways to do this. For running the method on loaded, you have two ways of doing it -
(1) Use Xaml Behavior Library's CallMethodAction or
(2) Use Prism's inbuilt InvokeCommandAction with the XAML Interactivity.
Related
I am looking at the EchoBot sample and trying to understand it. I see that BotController is mapped to api/messages and HttpPost which in turn invokes Adapter.ProcessAsync. But how does this translate into EchoBot.OnMessageActivityAsync call? I tried to set up a breakpoint and see the call stack but that doesn't help ( see attached screenshot).
I understand BotFrameworkHttpAdapter is invoked via dependency injection. But I don't know how we end up in EchoBot eventually.
To find the answer to this you really have to dive into the source code, so I hope you've got your scuba diving gear because we're going deep.
Step 1
Inside the BotController.cs file the following piece of code is called:
await Adapter.ProcessAsync(Request, Response, Bot);
which calls the ProcessAsync method on the IBotFrameworkHttpAdapter interface.
Step 2
Inside the Startup.cs file we have the following line:
services.AddSingleton<IBotFrameworkHttpAdapter, BotFrameworkHttpAdapter>();
which says that every time we ask for an IBotFrameworkHttpAdapter, provide the same instance of BotFrameworkHttpAdapter - essentially a static variable, you can read more about dependency injection service lifetimes here.
Step 3
Inside the Microsoft.Bot.Builder package we have the implementation for the ProcessAsync method, that can for our purposes be reduced to the following line:
var invokeResponse = await ProcessActivityAsync(authHeader, activity, bot.OnTurnAsync, cancellationToken).ConfigureAwait(false);
which calls ProcessActivityAsync which is another function that lives in this library - the important part here is the bot.OnTurnAsync parameter being passed in.
Step 5
Also inside the Microsoft.Bot.Builder package is the implementation for ProcessActivityAsync:
return await ProcessActivityAsync(claimsIdentity, activity, callback, cancellationToken).ConfigureAwait(false);
which calls an overload of the same method, but before we move on from here, the callback parameter is the bot.OnTurnAsync parameter that was passed through before.
Step 6
The overload of ProcessActivityAsync is also implemented inside the Microsoft.Bot.Builder package, and can be simplified to this line:
await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);
where callback is bot.OnTurnAsync.
Step 7
Digging deeper still we find the implementation of the RunPipelineAsync method inside of the Microsoft.Bot.Builder package which is were things start to get a bit fuzzy... Theoretically we want to fall through to the else block where the callback (i.e. bot.OnTurnAsync) is called:
// Call any registered Middleware Components looking for ReceiveActivityAsync()
if (turnContext.Activity != null)
{
// Other code
}
else
{
// call back to caller on proactive case
if (callback != null)
{
await callback(turnContext, cancellationToken).ConfigureAwait(false);
}
}
However, back in Step 6 we also had this line:
using (var context = new TurnContext(this, activity))
where the context is created, and the activity property is initialised. This same context is pass through to the RunPipelineAsync call, which means that we will not fall through to the else block...
But there are the following comment on the RunPipelineAsync method:
/// <param name="callback">A callback method to run at the end of the pipeline.</param>
and inside the remarks section:
...Once control reaches the end of the pipeline, the adapter calls
the <paramref name="callback"/> method...
So I believe is it safe to say that our callback method is being executed which means that we continue by bubbling back up the chain to resolve the function that callback maps to (bot.OnTurnAsync).
Step 8
In BotController we pass in an instance of IBot to the ProcessAsync method, and in Startup we wire up all requests for an IBot to return an instance of EchoBot like so:
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, EchoBot>();
The EchoBot implementation inherits from the ActivityHandler class:
public class EchoBot : ActivityHandler
Step 9
The ActivityHandler class provides a default implementation for the OnTurnAsync method which I will simplify to:
switch (turnContext.Activity.Type)
{
case ActivityTypes.Message:
return OnMessageActivityAsync(new DelegatingTurnContext<IMessageActivity>(turnContext), cancellationToken);
// Other cases
}
which the OnMessageActivityAsync method on the same class that has an implementation that returns a completed task, i.e. is a no-op, however it is a virtual method - classes which inherit ActivityHandler can provide their own implementation.
Step 10
A custom implementation for OnMessageActivityAsync is provided inside the EchoBot class:
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
await turnContext.SendActivityAsync(MessageFactory.Text($"Echo: {turnContext.Activity.Text}"), cancellationToken);
}
where the user's input is echoed back to them, and thus our journey ends.
Regarding Step 7, the Microsoft team are pretty active on SO for things tagged with botframework so it might be best to get #mdrichardson or #tdurnford to clarify what happens here.
As an aside in Visual Studio you can debug some library code by enabling the following option:
Tools --> Options --> Debugger
Uncheck "Enable Just my Code"
Also if you enable navigation to decompiled sources (you will have to accept a legal notification popup) by doing this:
Tools --> Options --> Text Editor --> C# --> Advanced
Check "Enable navigation to decompiled sources"
You will be able to inspect the source code of external packages within Visual Studio itself.
I made a custom report in AX2012, to replace the WHS Shipping pick list. The custom report is RDP based. I have no trouble running it directly (with the parameters dialog), but when I try to use the controller (WHSPickListShippingController), I get an error saying "Pre-Processed RecId not found. Cannot process report. Indicates a development error."
The error is because in the class SrsReportProviderQueryBuilder (setArgs method), the map variable reportProviderParameters is empty. I have no idea why that is. The code in my Data provider runs okay. Here is my code for running the report :
WHSWorkId id = 'LAM-000052';
WHSPickListShippingController controller;
Args args;
WHSShipmentTable whsShipmentTable;
WHSWorkTable whsWorkTable;
clWHSPickListShippingContract contract; //My custom RDP Contract
whsShipmentTable = WHSShipmentTable::find(whsWorkTable.ShipmentId);
args = new Args(ssrsReportStr(WHSPickListShipping, Report));
args.record(whsShipmentTable);
args.parm(whsShipmentTable.LoadId);
contract = new clWHSPickListShippingContract();
controller = new WHSPickListShippingController();
controller.parmReportName(ssrsReportStr(WHSPickListShipping, Report));
controller.parmShowDialog(false);
controller.parmLoadFromSysLastValue(false);
controller.parmReportContract().parmRdpContract(contract);
controller.parmReportContract().parmRdpName(classStr(clWHSPickListShippingDP));
controller.parmReportContract().parmRdlContract().parmLanguageId(CompanyInfo::languageId());
controller.parmArgs(args);
controller.startOperation();
I don't know if I'm clear enough... But I've been looking for a fix for hours without success, so I thought I'd ask here. Is there a reason why this variable (which comes from the method parameter AifQueryBuilderArgs) would be empty?
I'm thinking your issue is with these lines (try removing):
controller.parmReportContract().parmRdpContract(contract);
controller.parmReportContract().parmRdpName(classStr(clWHSPickListShippingDP));
controller.parmReportContract().parmRdlContract().parmLanguageId(CompanyInfo::languageId());
The style I'd expect to see with your contract would be like this:
controller = new WHSPickListShippingController();
contract = controller.getDataContractObject();
contract.parmWhatever('ParametersHere');
controller.parmArgs(args);
And for the DataProvider clWHSPickListShippingDP, usually if a report is using a DataProvider, you don't manually set it, but the DP extends SRSReportDataProviderBase and has an attribute SRSReportParameterAttribute(...) decorating the class declaration in this style:
[SRSReportParameterAttribute(classstr(MyCustomContract))]
class MyCustomDP extends SRSReportDataProviderBase
{
// Vars
}
You are using controller.parmReportContract().parmRdpContract(contract); wrong, as this is more for run-time modifications. It's typically used for accessing the contract for preRunModifyContract overloads.
Build your CrossReference in a development environment then right click on \Classes\SrsReportDataContract\parmRdpContract and click Add-Ins>Cross-reference>Used By to see how that is generally used.
Ok, so now I feel very stupid for spending so much time on that error, when it's such a tiny thing...
The erronous line is that one :
controller.parmReportName(ssrsReportStr(WHSPickListShipping, Report));
Because WHSPickListShipping is the name of the AX report, but I renamed my custom report clWHSPickListShipping. What confused me was that my DataProvider class was executing as wanted.
I'm trying to use a task launcher in WP7.1 Mango with the latest version of Caliburn Micro, but my code is not getting called back once the task has completed. Probably I'm doing something stupid somewhere, but I cannot see where. Here is what I did for a sample repro application you can download from:
http://www.filesonic.com/file/2750397005/PhoneTaskTest.zip
1) create a new WP7.1 application;
2) add a Lib folder in the solution, add there CM dll's, and add a reference to them;
3) change the generated files as specified by CM documentation and add a bootstrapper.
Now up to this point everything is OK and the application starts with no issues. I then do the following for taking a photo:
4) add a button in the main page to the view and a corresponding method in its VM, named TakePhoto.
5) change the VM as follows:
a) add a readonly IEventAggregator member injected in the constructor;
b) add OnActivate/OnDeactivate overrides to let the aggregator subscribe and unsubscribe this VM;
c) add the TakePhoto method which is just:
_aggregator.RequestTask<CameraCaptureTask>();
d) derive the VM from interface IHandle<TaskCompleted<CameraCaptureTask>> and implement it:
public void Handle(TaskCompleted<CameraCaptureTask> message)
{
if (message.Result.TaskEventArgs.TaskResult != TaskResult.OK) return;
SetPhoto(message.Result.TaskEventArgs.ChosenPhoto);
}
Now, when I click the button the camera task starts in the emulator and I can take a photo; then I'm taken back to my application, but nothing happens and my Handle method is NEVER called. You can just place a breakpoint there to confirm this.
So, what I'm doing wrong here?
You need to handle TaskCompleted<PhotoResult> instead of TaskCompleted<CameraCaptureTask>. Because Caliburn.Micro creates theTaskCompleted<T> message with the event args of the Task's Completed event which is in the case of CameraCaptureTask is PhotoResult. So you should implement IHandle<TaskCompleted<PhotoResult>> and your Handle method should look like this
public void Handle(TaskCompleted<PhotoResult> message)
{
if (message.Result.TaskResult != TaskResult.OK) return;
SetPhoto(message.Result.ChosenPhoto);
}
I have code such that:
[CodedUITest]
public class CodedUITest1
{
[TestMethod]
public void CodedUITestMethod1( )
{
using(var dlg = new MyWinForm( ))
{
dlg.Show();
System.Threading.Thread.Sleep(2000);
this.UIMap.AssertMethod1( );
this.UIMap.RecordedMethod1( );
this.UIMap.AssertMethod2( );
}
}
}
The code was running fine when I manually launched the app(before invoking the test) without the using clause to directly create the control.
I'd like to just use a reference to create an instance of the control and go from there instead of relying on trying to determine a path to the executable and opening it. The app just gets stuck with a ContextSwitchDeadlock.
Is there a way to do coded-Ui tests without doing a process launch? (using the reference and creating the control in the test code) or is there something wrong with the way I'm trying to do it?
It might be possible if you invoke the coded ui test portions (this.UIMap...) on a separate thread. But the way you have it now, they are both on the same thread, so you are going to get deadlocked.
What I would like to do is have VS2008, when I open a code file, collapse all members of the classes/interfaces in the file by default (including, crucially, any XML documentation and comments).
I do not want to use regions, at all.
I would also like to be able to use the ctrl+m, ctrl+l chord to toggle all member outlining (for example, if everything is collapsed, I would like it to expand all of the members, but not the comments or XML documentation).
Possible? How?
Yes to part 1.
Unsure about part 2.
To have VS2008 automatically open files in a Collapsed state you'll need to create an addin to run the "Edit.CollapsetoDefinition" when each document opens.
This isn't overly tricky - The difficult parts seems to be the that you have to run the code a few milliseconds after the document is actually opened so you need to use the threed pool to do that.
Create an Addin project for VS2008.
Add this code (see following) to the end of the OnConnection Method of the Connect class.
switch (connectMode)
{
case ext_ConnectMode.ext_cm_UISetup:
case ext_ConnectMode.ext_cm_Startup:
//Do nothing OnStartup will be called once IDE is initialised.
break;
case ext_ConnectMode.ext_cm_AfterStartup:
//The addin was started post startup so we need to call its initialisation manually
InitialiseHandlers();
break;
}
Add this method to the Connect class
private void InitialiseHandlers()
{
this._openHandler = new OnOpenHandler(_applicationObject);
}
Add a call to InitialiseHandlers() to the OnStartupComplete method of the Connect class.
public void OnStartupComplete(ref Array custom)
{
InitialiseHandlers();
}
Add this class to the project.
using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE80;
using EnvDTE;
using System.Threading;
namespace Collapser
{
internal class OnOpenHandler
{
DTE2 _application = null;
EnvDTE.Events events = null;
EnvDTE.DocumentEvents docEvents = null;
internal OnOpenHandler(DTE2 application)
{
_application = application;
events = _application.Events;
docEvents = events.get_DocumentEvents(null);
docEvents.DocumentOpened +=new _dispDocumentEvents_DocumentOpenedEventHandler(OnOpenHandler_DocumentOpened);
}
void OnOpenHandler_DocumentOpened(EnvDTE.Document document)
{
if (_application.Debugger.CurrentMode != dbgDebugMode.dbgBreakMode)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(Collapse));
}
}
private void Collapse(object o)
{
System.Threading.Thread.Sleep(150);
_application.ExecuteCommand("Edit.CollapsetoDefinitions", "");
}
}
}
And now all opened files should be fully collapsed.
It would be much easier to use the Visual Studio Macros to do the same thing. Editing the "EnvironmentEvents" Macro file in MyMacros and adding a handler for DocumentEvents.DocumentOpened with :
DTE.ExecuteCommand("Edit.CollapsetoDefinitions")
A quick way to collapse all outlining to function-definitions is to press:
Contextmenu-button*(next to your right windows button)*, L, O
I use it all the time. If there is a real hotkey for this please tell me :)
I had tried working out some Visual Basic code for a macro myself, borrowing from different places, and couldn't get anything to work. So what did I do? Why, I asked a question on StackOverflow of course! It got answered, I added the suggested code to my EnvironmentEvents macro, and now when I open CS files, after about a second, all my definitions are collapsed. :)