How to define a custom Validation for a property in DevExpress? - c#

Please consider that I have in an entity a property
public double RealVolume
{
get
{
return _RealVolume;
}
set
{
SetPropertyValue("RealVolume", ref _RealVolume, value);
}
}
then I also have
[Browsable(false)]
public VolumeType VolumeOrVolumePoints
{
get
{
return WMSSystemSetting.Get_VolumeOrVolumePoints(Session);
}
}
I want that when VolumeOrVolumePoints == VolumeType.Volume then RealVolume to be validated as an integer otherwise validated as double number.
How could I achieve this? Thanks!

For information on how to validate an object with XPO, see here. If you happen to be using DevExpress XAF then use a ValidationRule from the ValidationModule instead.
So you need something like:
protected override void OnSaving()
{
if (VolumeOrVolumePoints == VolumeType.Volume)
{
if (RealVolume % 1 != 0) // see http://stackoverflow.com/a/2751597/1077279
throw new Exception("The RealVolume value must be an integer when using Volume units.");
}
}

Related

How to retrieve user Custom Field on row selected event Acumatica

My use case is that when making a bill, the client wants to be able to specify a portion of the bill amount to which no discount is applied. (eg the total bill is $125, and the customer gets a 10% discount for paying in cash on delivery, but the discount does not apply to shipping costs. So, they want to say: "$25 of this is not discountable")
Then, I want to override the discount amount field based on this custom field.
I have added a data field to my APInvoice DAC via the Customization editor, and then activated it in my C# code as an extension:
namespace AcmeCorp.DAC
{
[PXCacheName("APInvoice")]
public class APInvoiceExt : PXCacheExtension<APInvoice>
{
public static bool IsActive() { return true; }
// Auto-Generated from ERP Customization Editor
#region UsrAmountExcludedFromDiscount
[PXDBDouble()]
[PXUIField(DisplayName = "Amt Excluded Discount")]
public virtual double? UsrAmountExcludedFromDiscount { get; set; }
public abstract class usrAmountExcludedFromDiscount : PX.Data.BQL.BqlDouble.Field<usrAmountExcludedFromDiscount> { }
#endregion
}
}
Now I want to check the value in that field to know how to adjust the discount field. How do I get this value?
[EDIT]
This allows me to get the extension, but the value is null, even though I have checked the record, and there is a value present there.
I set up a using statement first:
using APInvoiceExtension = Acme.DAC.APInvoiceExt;
And then I can use that in the GetExtension() method:
protected virtual void _(Events.RowSelected<APInvoice> e)
{
if (e.Row == null) return;
double? Amt2Exclude = null;
APInvoice apInvoice = e.Row as APInvoice;
var chkRef = apInvoice.RefNbr;
var ext = PXCache<APInvoice>.GetExtension<APInvoiceExtension>(apInvoice);
if (ext != null)
{
Amt2Exclude = ext.UsrAmountExcludedFromDiscount;
}
}
The RefNbr has a value just fine. But, as I said the value for UsrAmountExcludedFromDiscount is null.
[EDIT]
I tried Rick's suggestion and got the same error:
There's a few things I'm kind of confused about, like your DAC has the namespace AcmeCorp.DAC in the code example but you have a using statement that uses a different namespace. I would also probably use a decimal over a double for your data type in this instance, and in any instance where you are working with currencies.
For retrieving an extension, you can simply do the following.
protected virtual void _(Events.RowSelected<APInvoice> eventHandler)
{
APInvoice row = eventHandler.Row;
if (row is null) return;
APInvoiceExtension rowExt = row.GetExtension<APInvoiceExtension>();
if (rowExt != null)
{
excludedAmount = rowExt.UsrAmountExcludedFromDiscount;
}
}
I would probably avoid renaming the extension from APInvoiceExt to APInvoiceExtension on your using statement, but the above code should work to retrieve the extension. If you still have a null value in your extension, I would say that's most likely because a value never gets assigned to it, and you may need to post more of the code for us to evaluate what's actually going on here.
how about
APInvoiceExt ext = PXCache<APInvoice>.GetExtension<APInvoiceExt>(apInvoice);
EDIT:
namespace PX.Objects.AP
{
public class APRegisterExt : PXCacheExtension<PX.Objects.AP.APRegister>
{
#region UsrAmountExcludedFromDiscount
[PXDBDecimal]
[PXUIField(DisplayName="Amt Excluded Discount")]
public virtual Decimal? UsrAmountExcludedFromDiscount { get; set; }
public abstract class usrAmountExcludedFromDiscount : PX.Data.BQL.BqlDecimal.Field<usrAmountExcludedFromDiscount> { }
#endregion
}
public class APInvoiceEntry_Extension : PXGraphExtension<APInvoiceEntry>
{
#region Event Handlers
protected void APInvoice_RowSelecting(PXCache cache, PXRowSelectingEventArgs e)
{
if (e.Row == null) return;
decimal? Amt2Exclude = null;
APRegister apInvoice = e.Row as APRegister;
var chkRef = apInvoice.RefNbr;
var ext = PXCache<APRegister>.GetExtension<APRegisterExt>(apInvoice);
if (ext != null)
{
Amt2Exclude = ext.UsrAmountExcludedFromDiscount;
}
PXTrace.WriteInformation($"REF : {apInvoice.RefNbr}");
}
#endregion
}
}

How to roll back to original value iPropertyChanged? C#

I have a datagrid in my xamarin form app and it got a editable column. The values in the column are from MySql database and user can change the value and store to db. I used IPropertyChanged interface to allow user make the changes to the value. There is one condition when editing the value. The new value must be equal or bigger than the original value. My problem is whenever I enter a value bigger than the original, I cannot edit the value again to previous value. For example, the original value is 10. The new value I enter is 30. If I want to change the value again and this time I set it to 20, it is not allowing me because now the original value is 30 not 10 and 20 is less than 30. How can I retain the original value and compare with it?
public int ActualReading
{
get
{
return _ActualReading;
}
set
{
if (value >= _ActualReading)
{
_ActualReading = value;
RaisePropertyChanged("ActualReading");
}
else
{
UserDialogs.Instance.Alert("Meter readings should not be smaller than previous value.","Error","Ok");
}
}
}
private void RaisePropertyChanged(String Name)
{
if (PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(Name));
}
You have to store the original value. I'm using the following pattern.
Assuming you have a model like this
public class Model
{
public int ActualReading {get; set;}
}
and a viewmodel like this (I removed the INotifyPropertyChanged part for better reading)
public class ViewModel
{
private readonly Model MyModel;
private int _actualReading;
public int ActualReading
{
get { return _actualReading; }
set { _actualReading = value; }
}
public ViewModel(Model model)
{
MyModel = model;
ActualReading = model.ActualReading;
}
public Model GetModel()
{
MyModel.ActualReading = ActualReading;
return MyModel;
}
}
When you create the ViewModel instances you initialize it with the coresponding Model instance. When you have implemented this you can add your check in an easy way like this.
private int _actualReading;
public int ActualReading
{
get { return _actualReading; }
set
{
if (value >= MyModel.ActualReading)
{
_actualReading = value;
}
else
{
UserDialogs.Instance.Alert("Meter readings should not be smaller than previous value.", "Error", "Ok");
}
}
}

Forcing all WPF bound controls to update with GetBindingExpression().UpdateTarget()

I have a WPF application that includes ~50 controls that are bound to properties on my business object which implements INotifyPropertyChanged. Here's a quick snippet of my business object:
public class MyBusinessObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
// properties begin here
private string _name;
public string Name
{
get { return _name; }
set
{
if (_name == value)
{
return;
}
_name = value;
OnPropertyChanged(new PropertyChangedEventArgs("Name"));
}
}
// constructor, etc. not shown
}
I also have several validation rules that are used to validate the user input in these controls. I'm using command binding to prevent my user from saving the data if there are any validation errors. My application also includes a "Reset default values" button which, obviously, will reset the default value for all of the properties on my business object. This all works exactly as I'd like it to with one exception. If my user enters invalid data into one or more controls and then clicks the "Reset default values" button, the controls that contain invalid data don't always update as I'd expect. This happens because of the following code in my property setters:
if (_name == value)
{
return;
}
This code exists to prevent unnecessary property changed notifications from occurring when the value entered by my user in the bound UI control is the same value that the property is already set to. As an example, I have an IntegerUpDown control in my UI (this control is part of the Extended WPF Toolkit from Xceed). The default value of the property that my control is bound to is 10. My user deletes the value from the control and my validation rule is triggered which results in a validation error and the UI is updated appropriately with an error adorner, etc. The value of the property that this control is mapped to hasn't been changed so it's still set to 10. Now my user clicks the "Reset default values" button which will result in the default value (10) for the property being reset. However, the value for the property is already set to 10 so the short circuit logic in my setter will return instead of setting the property value.
So now, after my user clicks "Reset default values", I am also forcing an update on my binding target like this:
this.myIntegerUpDown.GetBindingExpression(Xceed.Wpf.Toolkit.IntegerUpDown.ValueProperty).UpdateTarget();
This solves my problem but only for this particular control. Is there any easy way to do this for all of my bound controls without having to specify each one? Thanks.
OnPropertyChanged(new PropertyChangedEventArgs(string.Empty));
This is intended to imply that ALL properties on that object have changed.
Could you do one of the following?
1) Reset the DataContext - Either recreate it, or re-set the property
var context = this.DataContext;
this.DataContext = null;
this.DataContext = context;
2) Loop through all properties programmatically via reflection and manually call OnPropertyChanged with the relevant property names.
var properties = typeof(ViewModel).GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in properties)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(property.Name));
}
You've mentioned validation and reset values, and of course the obvious one is to persist it.
Why don't you implement IEditableObject Interface on your entity that has three signature methods. BeginEdit(), CancelEdit() and EndEdit()
That way you can easily roll back your entity to the whatever you want, or validate it and lastly persist it. A good example is found here
Sample code
public class Customer : IEditableObject
{
struct CustomerData
{
internal string id ;
internal string firstName ;
internal string lastName ;
}
private CustomersList parent;
private CustomerData custData;
private CustomerData backupData;
private bool inTxn = false;
// Implements IEditableObject
void IEditableObject.BeginEdit()
{
Console.WriteLine("Start BeginEdit");
if (!inTxn)
{
this.backupData = custData;
inTxn = true;
Console.WriteLine("BeginEdit - " + this.backupData.lastName);
}
Console.WriteLine("End BeginEdit");
}
void IEditableObject.CancelEdit()
{
Console.WriteLine("Start CancelEdit");
if (inTxn)
{
this.custData = backupData;
inTxn = false;
Console.WriteLine("CancelEdit - " + this.custData.lastName);
}
Console.WriteLine("End CancelEdit");
}
void IEditableObject.EndEdit()
{
Console.WriteLine("Start EndEdit" + this.custData.id + this.custData.lastName);
if (inTxn)
{
backupData = new CustomerData();
inTxn = false;
Console.WriteLine("Done EndEdit - " + this.custData.id + this.custData.lastName);
}
Console.WriteLine("End EndEdit");
}
public Customer(string ID) : base()
{
this.custData = new CustomerData();
this.custData.id = ID;
this.custData.firstName = "";
this.custData.lastName = "";
}
public string ID
{
get
{
return this.custData.id;
}
}
public string FirstName
{
get
{
return this.custData.firstName;
}
set
{
this.custData.firstName = value;
this.OnCustomerChanged();
}
}
public string LastName
{
get
{
return this.custData.lastName;
}
set
{
this.custData.lastName = value;
this.OnCustomerChanged();
}
}
internal CustomersList Parent
{
get
{
return parent;
}
set
{
parent = value ;
}
}
private void OnCustomerChanged()
{
if (!inTxn && Parent != null)
{
Parent.CustomerChanged(this);
}
}
public override string ToString()
{
StringWriter sb = new StringWriter();
sb.Write(this.FirstName);
sb.Write(" ");
sb.Write(this.LastName);
return sb.ToString();
}
}
Wouldn't it be easier to just always call OnPropertyChanged regardless of whether its the same? How much of a performance boost does that give you?

Get property name inside setter

I want to preserve a property between postbacks in an ASP.Net application. Currently doing this:
public int MyIndex
{
get
{
return (int)Session[ToString() + "MyIndex"];
}
}
but would prefer something like:
public int MyIndex
{
get
{
return (int)Session[ToString() + #code_that_returns_property_name#];
}
}
Setter omitted, but it just pushes value into Session using the same string.
Is there some way to use reflection, or a different better solution?
public static int Dummy {
get {
var propertyName = MethodBase.GetCurrentMethod().Name.Substring(4);
Console.WriteLine(propertyName);
return 0;
}
}
Using CallerMemberName is a lot faster and it can be copied and pasted easily for additional properties.
private static object GetSessionValue([CallerMemberName]string propertyName = "")
{
return Session[propertyName];
}
private static void SetSessionValue(object value, [CallerMemberName]string propertyName = "")
{
Session[propertyName] = value;
}
public int MyIndex
{
get { return (int)GetSessionValue(); }
set { SetSessionValue(value); }
}
No, there isn't a simple way to do what you want to do. I think you are much better off with the code you have already written.
Edit: This answer has received quite a few downvotes and I do understand why. While it is possible to do what the OP wants to do perhaps we should all stop and think whether or not it is advisable to do so. To paraphrase the immortal words of Dr. Ian Malcom, just because you can do something doesn't mean you should.
You can use MethodInfo.GetCurrentMethod().Name to return the name of the current method:
public int MyIndex
{
get
{
return (int)Session[ToString() + MethodInfo.GetCurrentMethod().Name];
}
}
Since properties are implemented as methods under the hood, that will return a name like "get_MyIndex". If you don't want the "get_" part, you can substring out a few characters:
public int MyIndex
{
get
{
return (int)Session[ToString() + MethodInfo.GetCurrentMethod().Name.Substring(4)];
}
}
You can use an expression tree to get the member name. It's a bit of a hock but it works. Here is the code.
private string GetPropertyName<TValue>(Expression<Func<BindingSourceType, TValue>> propertySelector)
{
var memberExpression = propertySelector.Body as MemberExpression;
if (memberExpression != null)
{
return memberExpression.Member.Name;
}
else
{
return string.empty;
}
}
With that code you can do the following
return (int)Session[ToString() + GetPropertyName(MyIndex)];
Code ruthlessly stolen from Romain's answer on the following thread
Get class property name
You should rather use the ViewState property of your control:
public int MyIndex {
get {
object index = ViewState["MyIndex"];
return (null == index) ? -1 : (int)index;
}
set {
ViewState["MyIndex"] = value;
}
}
While Brian Rice answered the optimized version, I needed it for another usage. Without optimization for a session/dictionary code would look like this:
private static string GetPropertyName([CallerMemberName] string propertyName = "")
{
return propertyName;
}
And usage:
public int MyIndex
{
get
{
return (int)Session[ToString() + GetPropertyName()];
}
//or one liner
get=>(int)Session[ToString() + GetPropertyName()];
}

Validator not passing ErrorMessage to ValidationSummary

I have written my own Validator and although the validator appears to be working (as it does display the Text property when invalid) the ValidationSummary does not display the ErrorMessage property, or anything, when validation fails. Interestingly, it appears that it fails to even display the Text property when I add another control with a validator to the page. What am I doing wrong?
public class RequiredCheckBoxListValidator : BaseValidator
{
private CheckBoxList _list;
private int _requiredCount = 1;
public int RequiredCount
{
get { return _requiredCount; }
set { _requiredCount = value; }
}
public RequiredCheckBoxListValidator()
{
EnableClientScript = false;
}
protected override bool ControlPropertiesValid()
{
Control control = FindControl(ControlToValidate);
if (control != null)
{
_list = (CheckBoxList)control;
return (_list != null);
}
else
{
return false;
}
}
protected override bool EvaluateIsValid()
{
return (_list.Items.Cast<ListItem>().Where(li => li.Selected).Count() == _requiredCount);
}
}
It would help to see your clientside info.
Without that, my guesses are to check ShowSummary on the validtorsummary to make sure it is not hiding the summary, and to see if the validators and summary are in separate UpdatePanels.

Categories