Can you be more specific?
You have UI code that does:
obj.Name = "xyz";
// here obj.IsDirty is false????
obj.Name = "abc";
// here obj.IsDirty is true (which you'd expect)?
Or do I misunderstand what you are saying?
I admit to not reading all the details - but the problem you are experiencing may be related to not updating the local variable of the BO.
e.g. calling mBO.Save is wrong.
You should have mBO = mBO.Save instead.
Joe
we are using csla.net 4 silverlight, in our case the IsDirty change very much, and the CanSave is not always what it must be, so we got mistakes with our properties set's, by debugging in BusinessBase.cs to show wich properties it was, but now sometimes when we enter a viewmodel again the bisiness object isBusy is true, I do not find the problem in businessBase.cs
cannot tel why is stays isBusy, there is still some cases where the CanSave is not what it must be...
any suggestions where to look?
businessbase - employee
children employeeDetails and employeeContactDetails and other...
employee links to EmployeeViewmodel that bind to EmployeeEdit.xaml
a child property looks like this:
private bool IsLoadingEmployeeUserDetails = false;
private static readonly PropertyInfo<EmployeeUserEC> EmployeeUserDetailsProperty = RegisterProperty<EmployeeUserEC>(p => p.EmployeeUserDetails);
public EmployeeUserEC EmployeeUserDetails
{
get
{
if (!IsLoadingEmployeeUserDetails)
{
if ((!FieldManager.FieldExists(EmployeeUserDetailsProperty) || ReadProperty(EmployeeUserDetailsProperty) == null))
{
IsLoadingEmployeeUserDetails = true;
ChildIsBusy = true;
#if SILVERLIGHT
if (!IsBusy)
MarkBusy();
if (IsNew)
{
//return EmployeeUserEC.NewEmployeeUserEC();
EmployeeUserEC.NewEmployeeUserEC(EmployeeID, (o, e) =>
{
if (e.Error != null)
throw e.Error;
EmployeeUserDetails = e.Object;
IsLoadingEmployeeUserDetails = false;
if (!IsBusyLazyLoading)
MarkIdle();
ChildIsBusy = false;
OnPropertyChanged(EmployeeUserDetailsProperty);
}
);
}
else
{
EmployeeUserEC.GetEmployeeUser(EmployeeID, (o, e) =>
{
if (e.Error != null)
throw e.Error;
EmployeeUserDetails = e.Object;
IsLoadingEmployeeUserDetails = false;
if (!IsBusyLazyLoading)
MarkIdle();
ChildIsBusy = false;
OnPropertyChanged(EmployeeUserDetailsProperty);
}
);
}
#else
//if (!FieldManager.FieldExists(EmployeeUserDetailsProperty))
// {
// EmployeeUserDetails = DataPortal.Fetch<EmployeeUserEC>().Child;
// OnPropertyChanged(EmployeeUserDetailsProperty);
// }
#endif
}
}
try
{
return GetProperty(EmployeeUserDetailsProperty);
}
catch (Exception eee)
{
return null;
}
}
set
{
LoadProperty(EmployeeUserDetailsProperty, value);
}
}
--------------------
it works fine, what is important to notice what we do is we build the tree collection over and over each time the User update and change something, each diffrent TreeViewListItem are a diffrent viewmodel and dependecyobject that are control in a diffrent xaml page, all works fine, but sometimes the isBusy stays true and the CanSave is wrong, we check all set properties all is fine now, here is the tree builder :
--------------------
xaml:
<telerikNavigation:RadTreeView x:Name="radTreeView"
BorderThickness="0"
BorderBrush="Black"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
ItemsSource="{Binding OrgStructList}"
ItemTemplate="{StaticResource RootItem}"
Width="300"
IsDragDropEnabled="True"
PreviewDragStarted="radTreeView_PreviewDragStarted"
DragStarted="radTreeView_DragStarted"
PreviewDragEnded="radTreeView_PreviewDragEnded"
DragEnded="radTreeView_DragEnded"
/>
<telerik:RadButton Name="SaveButton"
Height="Auto"
Content="Apply"
IsEnabled="{Binding Path=CanSave}"/>
<telerik:RadButton Name="CancelButton" Height="Auto" Margin="5,0,0,0"
Content="Cancel"
IsEnabled="{Binding Path=CanCancel}"/>
------------------
private ObservableCollection<TreeViewListItem> OrgStructListProperty;
public ObservableCollection<TreeViewListItem> OrgStructList
{
get
{
if (Model.Company != null) //&& (ReadProperty(OrgStructureList_) == null))
OrgStructListProperty = BuildTreeList();
return OrgStructListProperty;
}
set
{
OrgStructListProperty = value;
}
}
-------------------------
private ObservableCollection<TreeViewListItem> BuildTreeList()
{
StatusIsBusy = true;
ObservableCollection<TreeViewListItem> _treeViewListCopy = new ObservableCollection<TreeViewListItem>();
if (_treeViewList == null)
_treeViewList = new ObservableCollection<TreeViewListItem>();
else
{
_treeViewListCopy = _treeViewList;
_treeViewList = new ObservableCollection<TreeViewListItem>();
}
//2. Build new Tree
string imageURL = "/StaffSense;component/Images/Company.jpg";
string unitsImageURL = "/StaffSense;component/Images/EmployeeUnits.jpg";
string companyGroupImageURL = "/StaffSense;component/Images/CompanyGroup2.jpg";
TreeViewListItem companyTreeListItem =
new TreeViewListItem(_treeViewList, new CompanyItemViewModel { Model = Model.Company }, TreeViewListItemObjectType.Company, Model.Company.CompanyName, imageURL, Model.Company.CompanyID,0);
_treeViewList.Add(companyTreeListItem);
//Add company groups to company recursively
foreach (var item in Model.Company.CompanyGroup)
{
TreeViewListItem treeViewListItem = new TreeViewListItem(companyTreeListItem.Children, new CompanyGroupItemViewModel { Model = item }, TreeViewListItemObjectType.CompanyGroup, item.Name, companyGroupImageURL, item.CompanyGroupID, 0);
companyTreeListItem.Children.Add(treeViewListItem);
HandleTreeItems(treeViewListItem.Children, item);
}
TreeViewListItem EmployeeUnitBoxTreeListItem =
new TreeViewListItem(_treeViewList, new EmployeeUnitBoxItemViewModel { Model = Model.EmployeeUnitBox }, TreeViewListItemObjectType.EmployeeUnitBox, "Employee Units", unitsImageURL, -1,0);
_treeViewList.Add(EmployeeUnitBoxTreeListItem);
foreach (var item in Model.EmployeeUnitBox.EmployeeUnit)
{
TreeViewListItem treeEmployeeUnitViewListItem =
new TreeViewListItem(EmployeeUnitBoxTreeListItem.Children, new EmployeeUnitItemViewModel { Model = item }, TreeViewListItemObjectType.EmployeeUnit, item.Name, unitsImageURL, item.EmployeeUnitID,0);
EmployeeUnitBoxTreeListItem.Children.Add(treeEmployeeUnitViewListItem);
HandleTreeItems(treeEmployeeUnitViewListItem.Children, item);
}
//3. Compare Tree's
//4. Change New Tree: Add Structrue from old Copy Tree to new Tree like IsExpand properties
foreach (var item in _treeViewList)
foreach (var itemCopy in _treeViewListCopy)
{
if (item.Equals(itemCopy))
{
//item.Parent_IsItemCollectionExpanded = itemCopy.Parent_IsItemCollectionExpanded; // for companygroups
item.IsItemCollectionExpanded = itemCopy.IsItemCollectionExpanded; // for items
HandleIsExpandedTreeItems(item.Children, itemCopy.Children);
break;
}
}
StatusIsBusy = false;
return _treeViewList;
}
public void HandleTreeItems(ObservableCollection<TreeViewListItem> list, CompanyGroupEC parent)
{
string employeeImageURL = "/StaffSense;component/Images/Employee3.jpg";
string AttendanceOfficerImageURL = "/StaffSense;component/Images/AttendanceOfficer2.jpg";
string companyGroupImageURL = "/StaffSense;component/Images/CompanyGroup2.jpg";
if (ListByMethod == 2)
{
//add employees
foreach (EmployeeEC employee in parent.Employee)
{
TreeViewListItem treeViewListItem = new TreeViewListItem(list, new EmployeeItemViewModel { Model = employee }, TreeViewListItemObjectType.Employee, employee.Name, employeeImageURL, employee.EmployeeID,0);
list.Add(treeViewListItem);
}
}
if (ListByMethod == 3)
{
//add Attendance officers
//sort:
IEnumerable<AttendanceOfficerEC> subCollection = from ao in parent.AttendanceOfficer orderby ao.Priority select ao;
foreach (AttendanceOfficerEC AttendanceOfficer in subCollection)
{
TreeViewListItem treeViewListItem = new TreeViewListItem(list, new AttendanceOfficerItemViewModel { Model = AttendanceOfficer }, TreeViewListItemObjectType.AttendanceOfficer, AttendanceOfficer.Name, AttendanceOfficerImageURL, AttendanceOfficer.EmployeeID, AttendanceOfficer.Priority);
list.Add(treeViewListItem);
}
}
//Always add subgroups
if (parent.CompanyGroup.Count > 0)
{
foreach (CompanyGroupEC companyGroup in parent.CompanyGroup)
{
TreeViewListItem treeViewListItem = new TreeViewListItem(list, new CompanyGroupItemViewModel { Model = companyGroup }, TreeViewListItemObjectType.CompanyGroup, companyGroup.Name, companyGroupImageURL, companyGroup.CompanyGroupID,0);
list.Add(treeViewListItem);
HandleTreeItems(treeViewListItem.Children, companyGroup);
}
}
}
public void HandleTreeItems(ObservableCollection<TreeViewListItem> list, EmployeeUnitEC parent)
{
string employeeImageURL = "/StaffSense;component/Images/Employee3.jpg";
string AttendanceOfficerImageURL = "/StaffSense;component/Images/AttendanceOfficer2.jpg";
string unitsImageURL = "/StaffSense;component/Images/EmployeeUnits.jpg";
if (ListByMethod == 2)
{
//add employees
foreach (EmployeeEC employee in parent.Employee)
{
TreeViewListItem treeViewListItem = new TreeViewListItem(list, new EmployeeItemViewModel { Model = employee }, TreeViewListItemObjectType.Employee, employee.Name, employeeImageURL, employee.EmployeeID, 0);
list.Add(treeViewListItem);
}
}
if (ListByMethod == 3) //no AttendanceOfficer for units
{
////add Attendance officers
//foreach (AttendanceOfficerEC AttendanceOfficer in parent.AttendanceOfficer)
//{
// TreeViewListItem treeViewListItem = new TreeViewListItem(list, new AttendanceOfficerItemViewModel { Model = AttendanceOfficer }, TreeViewListItemObjectType.AttendanceOfficer, AttendanceOfficer.Name, AttendanceOfficerImageURL, AttendanceOfficer.EmployeeID, AttendanceOfficer.Priority);
// list.Add(treeViewListItem);
//}
}
//Always add subgroups
if (parent.EmployeeUnit.Count > 0)
{
foreach (EmployeeUnitEC EmployeeUnit in parent.EmployeeUnit)
{
TreeViewListItem treeViewListItem = new TreeViewListItem(list, new EmployeeUnitItemViewModel { Model = EmployeeUnit }, TreeViewListItemObjectType.EmployeeUnit, EmployeeUnit.Name, unitsImageURL, EmployeeUnit.EmployeeUnitID, 0);
list.Add(treeViewListItem);
HandleTreeItems(treeViewListItem.Children, EmployeeUnit);
}
}
}
lol
it was a password field hidden with a invisibilityconverter when isCheckedbox was not check, the IsAsync validationrule run the password required, but it was not required, so I just add some functionality to fix the validatin problem, ...
>3. Save, which is successfully saved onto the db
>4. Save again without any changes
And between 3 and 4 you rebind the UI to the result of Save() right?
Remember that Save() returns the resulting object, and the
original object must be discarded. I suspect you are allowing the UI to
continue to use the original (unupdated) object.
If you are using a local data portal configuration, this is a
change in default behavior from 3.0 to 3.5 – it is one of the breaking
changes noted in the change log – see the one about AutoCloneOnUpdate.
I recommend allowing the clone operation to occur (which is the
default in 3.5) and changing your code accordingly. But if you want to continue
to use the old approach (which is really broken, hence the change in 3.5) you
can set AutoCloneOnUpdate to get the old behavior.
Rocky
Copyright (c) Marimer LLC