C# Wizard control has the event ActiveStepChanged that is triggered when we move through the steps of the wizard. The current step is stored in the property called ActiveStepIndex. I need to retrieve the step immediately preceding the current ActiveStepIndex.
I'm trying this way but without results up to now:
ICollection s = wizTransferSheet.GetHistory();
IList steps = s as IList;
WizardStep lastStep = steps[steps.Count].Name;
Depending on how complex your wizard is, that can be tricky sometimes. You can't always use the ActiveStepIndex. Luckily, the wizard control logs a history of the steps visited, and you can leverage this to retrieve the last step that was visited:
You can use this function to get the last step that was visited:
/// <summary>
/// Gets the last wizard step visited.
/// </summary>
/// <returns></returns>
private WizardStep GetLastStepVisited()
{
//initialize a wizard step and default it to null
WizardStep previousStep = null;
//get the wizard navigation history and set the previous step to the first item
var wizardHistoryList = (ArrayList)wzServiceOrder.GetHistory();
if (wizardHistoryList.Count > 0)
previousStep = (WizardStep)wizardHistoryList[0];
//return the previous step
return previousStep;
}
Here's some sample code from one of our wizards. The wizard is pretty complex, and there is a lot of potential branching based on what the user does. Because of that branching, navigating the wizard can be a challenge. I don't know if any of this will be useful to you, but I figured it was worthwhile including it just in case.
/// <summary>
/// Navigates the wizard to the appropriate step depending on certain conditions.
/// </summary>
/// <param name="currentStep">The active wizard step.</param>
private void NavigateToNextStep(WizardStepBase currentStep)
{
//get the wizard navigation history and cast the collection as an array list
var wizardHistoryList = (ArrayList)wzServiceOrder.GetHistory();
if (wizardHistoryList.Count > 0)
{
var previousStep = wizardHistoryList[0] as WizardStep;
if (previousStep != null)
{
//determine which direction the wizard is moving so we can navigate to the correct step
var stepForward = wzServiceOrder.WizardSteps.IndexOf(previousStep) < wzServiceOrder.WizardSteps.IndexOf(currentStep);
if (currentStep == wsViewRecentWorkOrders)
{
//if there are no work orders for this site then skip the recent work orders step
if (grdWorkOrders.Items.Count == 0)
wzServiceOrder.MoveTo(stepForward ? wsServiceDetail : wsSiteInformation);
}
else if (currentStep == wsExtensionDates)
{
//if no work order is selected then bypass the extension setup step
if (grdWorkOrders.SelectedItems.Count == 0)
wzServiceOrder.MoveTo(stepForward ? wsServiceDetail : wsViewRecentWorkOrders);
}
else if (currentStep == wsSchedule)
{
//if a work order is selected then bypass the scheduling step
if (grdWorkOrders.SelectedItems.Count > 0)
wzServiceOrder.MoveTo(stepForward ? wsServicePreview : wsServiceDetail);
}
}
}
}
Related
I try the new Shell of Xamarin Form 4 for a small project.
I have a list of order, then someone chooses an order and start picking some inventory for this order with barcode. To be simple, I use 2 views and 2 viewmodel.
The process is:
1. User select an order the click a button "pickup"
2. I use ViewModelLocator (TinyIoc) to resolve the correct ViewModel of the pickup view
3. I call Initialize on the new ViewModel and pass some parameters needed. Like a list of items needed to be pickup.
4. Open the view in modal state.
I don't understand that if I change some qty in the list I pass on the second viewmodel, then hit the back button (modal view close). The quantity changed is now in the original viewmodel. How this is possible? I was thinking that passing parameter to a function do not share the same variable but just copy it??
Viewmodel of the first view (look at the Initialize function the List passed and the JobEnCours object)
private async Task GoPickup()
{
Device.BeginInvokeOnMainThread(async () =>
{
if (this.CodeJob != null && this.CodeJob != "")
{
this.IsBusy = true;
PrepChariotSP3ViewModel vm = ViewModelLocator.Resolve<PrepChariotSP3ViewModel>();
await vm.InitializeAsync(this._jobEnCours, this.ComposantesPick.ToList()) ;
await Shell.Current.Navigation.PushAsync(new PrepChariotSP3Page(vm));
this.IsBusy = false;
}
});
}
the the Initialize code on the second Viewmodel (look I set the JobEnCours.Description=Test)
public async Task InitialiseAsync(Job jobEnCours, List<ComposantePick> composantePick)
{
Title = "Ceuillette: " + jobEnCours.CodeProd;
this._jobEnCours = jobEnCours;
this.ComposantesPick = new ItemsChangeObservableCollection<ComposantePick>();
foreach(ComposantePick c in composantePick)
{
this.ComposantesPick.Add(c);
}
jobEnCours.Description = "test";
So, If I do the back button, then in the first VM the JobEnCours.Description is now set to "test" how this is possible?!
Any idea?
Im working on a piece of code using DevExpress XAF, I noticed that if im using the event OnSaving that the code executes 2 times, how can i prevent that
protected override void OnSaving()
{
if (PrestamoP != null)
{
PrestamoP.Prestado -= Monto;
PrestamoP.Save();
}
else if (PrestamoG != null)
{
PrestamoG.Prestado -= Monto;
PrestamoG.Save();
}
base.OnSaving();
}
XPO does not guarantee that the OnSaving method is called once. See the corresponding note in the XPO Best Practices article.
I can see that you are changing the PrestamoP.Prestado property based on the value of the Monto property. This code is fine if you execute it only once and only when the Monto property is specified for the first time. This code is not fine if you:
Save this object without changing the Monto property;
Update the early specified Monto value.
So, it appears that a more complex logic is required for the PrestamoG.Prestado property. First, I would move it to the Monto property setter and take the previous value into account (do not forget to check the IsLoading property in this case). Second, I would consider calculating the Prestado value dynamically instead of storing its value. This will allow you to resolve issues with the duplicate business logic execution. See an example here: How to: Calculate a Property Value Based on Values from a Detail Collection.
I can offer different methods for CRUD functions on onSaving method.
IsNewObject, IsDeleted.
// insert
if (Session.IsNewObject(this))
{
a = new a(Session);
a.CreateReferencedProperties(this);
}
// delete
else if (IsDeleted)
{
a= Session.FindObject<A>(PersistentCriteriaEvaluationBehavior.InTransaction, CriteriaOperator.Parse("A=?", this));
if (a!= null)
a.Delete();
}
// update
else
{
a= Session.FindObject<A>(PersistentCriteriaEvaluationBehavior.InTransaction, CriteriaOperator.Parse("A=?", this));
if (a!= null)
a.CreateReferencedProperties(this);
}
You can use the code below to prevent xaf from entering on saving twice.
base.OnSaving();
SessionObjectLayer sessionObjectLayer = this.Session.ObjectLayer as SessionObjectLayer;
if (sessionObjectLayer == null || sessionObjectLayer.ParentSession == null)
{
//Enter only once
}
I have a function called on every single page:
/// <summary>
/// Gets the date of the latest blog entry
/// </summary>
public static DateTime GetNewestBlogDate()
{
DateTime ReturnDate = DateTime.Now.AddDays(30);
using (var db = new DataClassesDataContext())
{
var q = (from d in db.tblBlogEntries orderby d.date descending select new {d.date}).FirstOrDefault();
if (q != null)
ReturnDate = q.date;
}
return ReturnDate;
}
It works like this website, it gets the latest blog entry date and if it's greater than the users cookie value it displays a new icon next to the blog link.
It seems rather wasteful to keep calling this function per page request, called 1:1 on the number of page requests you have. Say you have 30,000 page views per day, that's 1,250 database queries per hour.
Is there any way I can cache this results, and have it expire say every hour?
I'm aware it's a bit of a micro optimisation, but given 10 or so similar functions per page it might add up to something worthwhile. You could denormalise it into a single table and return them all in one go, but I'd rather cache if possible as it's easier to manage.
Since it's not based on the user (the cookie is, but the query doesn't seem to be) - you can just use the standard ASP.NET Cache.
Just insert the result with an expiration of 1 hour. If you like, you can even use the callback to automatically refresh the cache.
Assuming you've stored it into MS-SQL, you could even use a SqlCacheDependency to invalidate when new data is inserted. Or, if your inserting code is well-factored, you could manually invalidate the cache then.
Just use the ASP.NET Cache object with an absolute expiration of 1 hour. Here's an example of how you might implement this:
public static DateTime GetNewestBlogDate()
{
HttpContext context = HttpContext.Current;
DateTime returnDate = DateTime.Now.AddDays(30)
string key = "SomeUniqueKey"; // You can use something like "[UserName]_NewestBlogDate"
object cacheObj = context.Cache[key];
if (cacheObj == null)
{
using (var db = new DataClassesDataContext())
{
var q = (from d in db.tblBlogEntries orderby d.date descending select new { d.date }).FirstOrDefault();
if (q != null)
{
returnDate = q.date;
context.Cache.Insert(key, returnDate, null, DateTime.Now.AddHours(1), Cache.NoSlidingExpiration);
}
}
}
else
{
returnDate = (DateTime)cacheObj;
}
return returnDate;
}
You haven't indicated what is done with the returned value. If the returned value is displayed the same way on each page, why not just place the code along with the markup to display the result in a user control (ASCX) file? You can then cache the control.
Make it a webmethod with a CacheDuration?
[WebMethod(CacheDuration=60)]
public static DateTime GetNewestBlogDate()
I have a DropdownList that shows a list of providers & the Provider associated with that Patient must be selected.
The Dropdown list:
<s:DropDownList id="providerList"
width="80%"
fontSize="12"
fontWeight="bold"
selectionColor="white"
creationComplete="providerList_creationCompleteHandler(event)"
dataProvider="{model.practiceProviderList.practiceProviders}"/>
where practiceProviders is an ArrayCollection
The CreationCompleteHandler function:
protected function providerList_creationCompleteHandler(event:FlexEvent):void
{
var firstN:String;
var lastN:String;
var providerObj:Provider = new Provider();
if (model.patientDetails.patientDetail.patientProviders != null && model.patientDetails.patientDetail.patientProviders.length > 0)
{
firstN = patientDetailsModel.patientDetails.patientDetail.patientProviders.getItemAt(0).provider.providerName.firstName;
lastN = patientDetailsModel.patientDetails.patientDetail.patientProviders.getItemAt(0).provider.providerName.lastName;
for (var count:int = 0; count < patientDetailsModel.practiceProviderList.practiceProviders.length; ++count)
{
providerObj = patientDetailsModel.practiceProviderList.practiceProviders.getItemAt(count, 0).provider as Provider;
if (providerObj.providerName.firstName == firstN && providerObj.providerName.lastName == lastN)
{
this.providerList.selectedIndex = count;
}
}
}
}
The issue is when I go to this page the first time, the error is :
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at com.newwavetechnologies.modules::demographics/providerList_creationCompleteHandler()[C:\harish\flex\apps\workspace\dataCollection-flexUserInterface\src\com\newwavetechnologies\modules\demographics.mxml:166]
at com.newwavetechnologies.modules::demographics/__providerList_creationComplete()[C:\harish\flex\apps\workspace\dataCollection-flexUserInterface\src\com\newwavetechnologies\modules\demographics.mxml:359]
at flash.events::EventDispatcher/dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at mx.core::UIComponent/dispatchEvent()[E:\dev\4.0.0\frameworks\projects\framework\src\mx\core\UIComponent.as:12266]
at mx.core::UIComponent/set initialized()[E:\dev\4.0.0\frameworks\projects\framework\src\mx\core\UIComponent.as:1577]
at mx.managers::LayoutManager/doPhasedInstantiation()[E:\dev\4.0.0\frameworks\projects\framework\src\mx\managers\LayoutManager.as:759]
at mx.managers::LayoutManager/doPhasedInstantiationCallback()[E:\dev\4.0.0\frameworks\projects\framework\src\mx\managers\LayoutManager.as:1072]
where line 166 is:
if (providerObj.providerName.firstName == firstN && providerObj.providerName.lastName == lastN)
The providerObj is null the first time. But when hit back and come to the same page again, everything works fine and 1 of the providers in the list is selected correctly.
Probably I think the first time, the creationComplete handler method is called before the List is populated. The 2nd time when the call is made, the list is populated and the handler works fine. It would be great if someone can help me in this regard on how to go about this.
Thanks
Harish
It's hard to tell what's going on here, but the problem lies here:
providerObj = patientDetailsModel.practiceProviderList.practiceProviders.getItemAt(count, 0).provider as Provider;
There's a tonne of places in that line that Null pointer exceptions could occur.
Most likely - the practiceProvider returned at position count doesn't have a provider set. We can't see how this value is populated, but given this code works later, I'd say you've got a race condition happening - the data is being accessed before it's been set.
At very least, you should add a guardClause for this:
var practiceProviders:ArrayCollection = patientDetailsModel.practiceProviderList.practiceProviders;
for (var count:int = 0; count < practiceProviders.length; ++count)
{
providerObj = practiceProviders.getItemAt(count, 0).provider as Provider;
if (!providerObj)
continue;
// etc
}
The race condition is a little trickier, given the asyncronous natoure of flex server calls. (I'm assuming that you're loading the data from a remote server).
There's two approaches to solve this - either
defer execution of this method until the data has loaded - you could do this by adding an eventListener to the ResultEvent of the RemoteService
or
Don't worry about it the first time around, but re-execute the method whenever the data changes.
eg:
protected function providerList_creationCompleteHandler(event:FlexEvent):void
{
dataProvider.addEventListener(CollectionEvent.COLLECTION_CHANGE,onCollectionChange,false,0,true);
updateProviders();
// Rest of existing creationComplete code moved to updateProviders();
}
private function updateProviders()
{
// Code from existing creationComplete handler goes here
}
private function onCollectionChange(event:CollectionEvent):void
{
updateProviders();
}
I'm tearing my hair off for this amazing problem.
I'm binding 2 LookUpEdit from code:
MyBinding.DataSource = typeof(MyObject);
MyBinding.DataSource = _dataObject.GetMyList();
firstLookUp.DataBindings.Add("EditValue", MyBinding, "Code");
firstLookUp.Properties.DataSource = MyBinding;
firstLookUp.Properties.ValueMember = "Code";
firstLookUp.Properties.DisplayMember = "Code";
secondLookUp.DataBindings.Add("EditValue", MyBinding, "Info");
secondLookUp.Properties.DataSource = MyBinding;
secondLookUp.Properties.ValueMember = "Info";
secondLookUp.Properties.DisplayMember = "Info";
First problem is: Changing the value on one of the two LookUps not reflecting changing the other one! But im using the same BindingSource, isn't the position the same?
Another one is: They both populate automatically the columns, i dont want to show all columns, tried to remove, exception column not found, if i add, i get duplicate columns!
I don't get it!!!
Changing LookupEdit's EditValue is not directly bound to the BindingSource.Current position.
You have to use something like
lookUpEdit1.Properties.GetDataSourceRowByKeyValue(lookUpEdit1.EditValue)
If you want both LookupEdits linked you are probably better off setting the edit value of the one when the other is changed.
Secondly You should be able to clear the list of Columns like so:
lookUpEdit1.Properties.Columns.Clear();
lookUpEdit1.Properties.Columns.Add(new LookUpColumnInfo("FirstName"));
As said here
http://www.devexpress.com/Support/Center/p/B138420.aspx
http://www.devexpress.com/Support/Center/p/A2275.aspx
LookupEdit does update the Current Property of the BindingSource.
We use the following Code as a workaround:
/// <summary>
/// Wrapper around DevExpress.XtraEditors.LookUpEdit to fix bug with adjusting the BindingSources Current Position
/// </summary>
public sealed class LookUpEditWithDataSource : LookUpEdit
{
private bool firstCall = true;
/// <summary>
/// Called when the edit value changes.
/// </summary>
protected override void OnEditValueChanged()
{
base.OnEditValueChanged();
if (this.Properties.DataSource == null)
{
return;
}
if (this.BindingContext == null)
{
return;
}
if (this.firstCall)
{
this.firstCall = false;
// HACK
// starting and selecting the first item
// doesn't work so we change the position to the first item
this.BindingContext[this.Properties.DataSource].Position = 1;
}
this.BindingContext[this.Properties.DataSource].Position = this.Properties.GetDataSourceRowIndex(this.Properties.ValueMember, this.EditValue);
}
}