Prevent saving an invalid field value in Sitecore - c#

I have a custom validator class, which checks the following:
The value of the text field should have a length 5 characters.
The
first 2 chars. should be numbers.
The last 3 chars. should be
alphabets.
When setting the Standard Value of the template (eg: 12a), the indicator shows Red and the appropriate message. But after pressing Ctrl + S, it shows a dialog asking to save even when there is an error. After clicking OK, there is a similar dialog. Clicking on OK, saves 12a as the standard value for the field. When I refresh the content editor the value is 12a.
Is this normal Sitecore behavior. I'm expecting that the value shouldn't be saved at all, if it is invalid.
namespace CustomValidators
{
[Serializable]
public class testValidator : StandardValidator
{
private readonly Regex numbersRegex = new Regex(#"^\d+$");
private readonly Regex lettersRegexnew = new Regex(#"^[A-Za-z]+$");
protected override ValidatorResult Evaluate()
{
string value = base.GetControlValidationValue();
if (!string.IsNullOrEmpty(value) && value.Length == 5)
{
string firstPart = value.Substring(0, 2);
string secondPart = value.Substring(3, 3);
if (numbersRegex.IsMatch(firstPart) && lettersRegexnew.IsMatch(secondPart))
{
return ValidatorResult.Valid;
}
}
base.Text = "invalid value";
return base.GetFailedResult(ValidatorResult.FatalError);
}
protected override ValidatorResult GetMaxValidatorResult()
{
return base.GetFailedResult(ValidatorResult.FatalError);
}
public override string Name
{
get { return "testValidator"; }
}
}
}

Only people in certain roles, even get the option of forcing a save. Admins and I think people in the "Sitecore Developer" role.
As such, you are given the option of forcing a save through. This is normal behaviour.
Your regular editor users would not be able to save.

Related

Syncfusion's Masked Edit keeps on appending the old value to the new value even after I create a new instance

I've encountered a weird interaction with Syncfusion's Masked Edit, it's a 3rd party WPF control that's basically a text box that takes a string mask and lets you choose how you'd want to interpret it (the one I currently set it to is RegEx) to filter out your text.
After creating a new NewCustomer through my Reset Command, if I try to type on a Masked Edit that had a value before creating the new instance it somehow still remembers that old value and appends it to the new value. Here's a clip of it (https://gfycat.com/embellishedpalefantail)
What I've tried:
I've tried removing the Mask and MaskType properties and ran the program to see if there was something else causing it but since it ran fine, the problem seems to lie on the Mask itself,
Could it be that they interpret my mask differently and it's behaving how they wanted it to?
Or is my Mask simply just wrong and it's interpreting it normally? Because with my mask of [0-9a-zA-Z ]{0,20}, I expected it to only allow alphanumeric characters and a space with a minimum length of 0 and maximum length of 20.
Here's a code of everything that's involved with my problem:
C#
The property the Text Value is bound to:
private Customer newCustomer;
public Customer NewCustomer
{
get { return newCustomer; }
set { newCustomer = value; RaisePropertyChanged("NewCustomer"); }
}
public class Customer : BaseSearchableCollectionClass
{
private string firstName;
public string FirstName
{
get { return firstName; }
set { firstName = value; RaisePropertyChanged("FirstName"); }
}
}
// The BaseSearchableCollectionClass contains the INotify implementation and other unrelated code
The reset command:
public RelayCommand ResetNewCustomerCommand { get; private set; }
public void ResetNewCustomer(object msg)
{
NewCustomer = new Customer();
}
public bool ResetNewCustomerCanUse(object msg)
{
if (HasChange() == true)
return true;
return false;
}
XAML
SFMaskedEdit:
<chart:SfMaskedEdit
Text="{Binding NewCustomer.FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Mask="[0-9a-zA-Z ]{0,20}"
MaskType="RegEx"
/>
Reset Button:
<Button
Command="{Binding ResetNewCustomerCommand}"
Content="Reset"
/>
What I expected:
I expected it to run exactly like this:
I type something on the Masked Edit
Masked Edit filters out any unwanted characters and maintains length requirements
I press the reset button to create a New Customer, effectively clearing out any existing values
I type a new value on the Masked Edit as if it were a new text box (Unfortunately the problem lies here where it appends the cleared value to the new value)
The reported case is an defect with SfMaskedEdit control and we will include fix for issue in our upcoming release.
Regards,
Magesh S

How to use a string in an if statement with properties Settings?

I am currently working in winforms c#. I have a string "Flippo1SN" and that string is determined in my form. "Flippo1SN" can change in 'serial number names' that matches the same name as in Properties.Settings . The value of the serial numbers in Properties.Settings are integers. I want to use an if statement without summing up all the integers in my Properties.Settings, instead, I want to use a string so I could write my code in 1 if statement instead of more. I tried to use a code like this:
if (Properties.Settings.Default.Flippo1SN == 0) {}
Which, as you may have guessed, did not work. I've tried more ways as:
if (Properties.Settings.Default.(Flippo1SN) == 0) {}
if (Properties.Settings.Default.("Flippo1SN") == 0) {}
if (Properties.Settings.Default.[Flippo1SN] == 0) {}
if (Properties.Settings.Default.["Flippo1SN"] == 0) {}
It gives me an error saying that there is an indentifier expected.
How can I solve this? This question may have already been asked in the past but I couldn't find it.
Thanks in advance.
Edit 1:
Flippo1SN Does not exist in Properties.Settings, it is its value that does. Its value is something like: GF01, GF02, ... I am trying to refer to those.
Edit 2:
These are the variables I am trying to refer to. Flippo1SNs name changes to GF01 or GF02 etc. I don't want to put a code like this:
if (Flippo1SN == "GF01")
{
if (Properties.Settings.Default.GF01 == 0) {//Do action}
}
Instead, I want to refer to GF01 immediately by using Flippo1SN in the second if statement. That would cost me a lot of time and writing because I have a lot of Integers in Properties.Settings .
Edit 3
I'm going to explain what I am creating so you guys understand what I'm doing.
I am creating a 'Collecting Game' where you collect Flippos (Pogs or milk caps is what it's called in English I guess?). To get those Flippos, you open a giftbox and receive 3 random flippos. The output is something like this:
Screenshot ("Verzamel" means collect)
In this image, you see 3 flippos. In the top left corner, you see the Serial Numbers of each flippo (MF06, GF16, OF12). 'MF' stands for 'Mega Flippo', 'GF' for Green Flippo and 'OF' stands for 'Orange Flippo'.
In the code, I have used random to choose which one you get (50% chance for green, 30% chance for orange and 20% chance for mega. The percentage is determined by the amount of a specific group). I have also 3 strings in my code that holds the serial number of these flippos (Flippo1SN, Flippo2SN, and Flippo3SN). These serial numbers refer to the ones in my database (or just the properties.settings tab). In this scenario, MF06, GF16 and OF12 increments by 1.
Now, I want to check if you've received a flippo you didn't have before. If you do so, a label will appear above the picture and the text of that label will be "New".
To do so, I first need to check which one you have received, then check if you already have that in your database. The first if checks if you have received GF01 and the second if checks if you already have it:
if (Flippo1SN == "GF01")
{
if (Properties.Settings.Default.GF01 == 0)
{
label1.Show();
}
}
else if (FLippo1SN == "GF02") {//ETC}
Flippo1SN is already determined. I am not trying to change the Flippo1SNs value. I am just using this string to check which flippo you have received. All flippos have a serial number and Flippo1SN holds a serial number to refer to which flippo you have received.
What I now am asking is, is there a more fast way to do this? Can't I use the value of Flippo1SN immediately in an if statement so I could avoid multiple if statements?
I really hope I made things clear now.
First you have to check if it exists then you can do whatever you want with it:
if(Properties.Settings.Default.ContainsKey(Flippo1SN))
{
if(Properties.Settings.Default[Flippo1SN] == 0)
{
// ....
}
}
You can also write handlers to get rid of the if checks:
interface ISettingsHandler
{
void Handle(int value);
bool CanHandle(string name);
}
class GF1Handler : ISettingsHandler
{
public void Handle(int value){
// do action
}
public bool CanHandle(string propertyName){
return propertyName.Equals("GF1");
}
}
class GF2Handler : ISettingsHandler
{
public void Handle(int value){
// do action
}
public bool CanHandle(string propertyName){
return propertyName.Equals("GF2");
}
}
You can then initialize a list of handlers, and use the one that can handle the selected property:
var handler = listOfHandler.FirstOrDefault(h => h.CanHandle(Flippo1SN))
if( != null)
handler.Handle(Properties.Settings.Default[Flippo1SN]);
By using Properties.Settings.Default you can retrieve all the properties in your Project.
Then, by iterating through them you can check the Name of each property to see if it matches your Flippo1SN like so:
string Flippo1SN = "GF02";
var props = Properties.Settings.Default;
foreach(var prop in props.Properties)
{
var settingProperty = (SettingsProperty)prop;
if (settingProperty.Name == Flippo1SN)
{
// Now you found the property that matches Flippo1SN.
// Get its value.
var value = settingProperty.DefaultValue;
}
}
Edit:
How to check if value of the property is zero:
string Flippo1SN = "GF02";
foreach (SettingsProperty prop in Properties.Settings.Default.Properties)
{
if (prop.Name == Flippo1SN)
{
if (int.TryParse(prop.DefaultValue.ToString(), out int result))
{
if (result == 0)
{
// The value is zero.
}
}
}
}
EDIT: NEW CODE AND SCREENSHOTS
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
dynamic props = Properties.Settings.Default;
string Flippo1SN = "200";
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string output = "Properties.Settings.Default.test : ";
if (Flippo1SN == props.test.ToString())
{
output += $"{props.test}";
}
if (Flippo1SN == props.test2.ToString())
{
output += $"{props.test2}";
}
if (Flippo1SN == props.test3.ToString())
{
output += $"{props.test3}";
}
MessageBox.Show(output);
}
}
}

WPF - MVVM Textbox restrict to specific characters

I am trying to make text box accept only specific characters.
My TextBox is bound to the following:
private string _CompanyID;
public string CompanyID
{
get { return _CompanyID; }
set
{
_CompanyID = UniversalHelpers.sReturnCorrectColumnName(value);
OnPropertyChanged("CompanyID");
}
}
Where this is the function that is being called:
public static string sReturnCorrectColumnName(string sInput)
{
if(!string.IsNullOrWhiteSpace(sInput))
return Regex.Replace(sInput, #"[^a-zA-Z]", string.Empty).ToUpper();
else
return sInput;
}
(I am allowing only a-z & A-Z, nothing else).
Finally my TextBox looks like this:
<TextBox Text="{Binding ExcelBindings.CompanyID, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
What I don't understand is, that user can still write anything he wants, even though my Mode is set to TwoWay.
What am I doing wrong?
You should use a custom UI element there that restricts the input on the view-side using “classic” solutions like change listeners.
For example, you can just create a simple subtype of TextBox that overrides the OnPreviewTextInput method. There, you can decide when some input should go through, or when you want to prevent it.
For example, this is a custom TextBox that takes only characters from the ASCII alphabet:
public class AlphabetTextBox : TextBox
{
private static readonly Regex regex = new Regex("^[a-zA-Z]+$");
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
if (!regex.IsMatch(e.Text))
e.Handled = true;
base.OnPreviewTextInput(e);
}
}
Of course, you could also make the regular expression a property of the text box and allow people to set it from XAML. That way, you would get a very reusable component which you can use for various applications.
I do this with the PreviewtextInput event. I have a generic event used for multiple TextBoxes which takes the regex from a configuration table, but I have hard-coded the regex in this example.
private void GenericTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
e.Handled = !IsTextAllowed(e.Text, #"[^a-zA-Z]");
}
private static bool IsTextAllowed(string Text, string AllowedRegex)
{
try
{
var regex = new Regex(AllowedRegex);
return !regex.IsMatch(Text);
}
catch
{
return true;
}
}
The problem is, humans type in numbers sequentially, the fools.
To type in "0.1", a legitimate string, you have to type in "0.", which fails.
Also, re the accepted answer from #poke (which is great), the e.Text value is the change to the textbox (keystroke).
You must add this change to the current textbox string, and then validate the concatenated candidate string, and see if that is valid.
Humans are also wiley, so they will paste from the clipboard to get around the restriction.
With a textbox, you will never be able to block all garbage in, because at some point the user will have to go through garbage, to get to a valid string.
So you can block illegal character entry using e.Text, or allow sequential step failure. But you will still have to check the final string for validity too.
Below is an example of a textbox that allows users to type in a decimal value with a max of 8 dec places in, but they could still cheat this by pasting from the clipboard.
////////////////////////
// REGEXTEXTBOX CLASS //
////////////////////////
using System.Windows.Controls; // Textbox
using System.Windows.Input;
using System.Text.RegularExpressions; // Regex
namespace MyNamespace
{
public class RegexTextBox : TextBox
{
private Regex _regex = null;
public Regex Regex
{
get { return _regex; }
set { _regex = value; }
}
///////////////////////////////////////////////////////////////////////
// MEMBERS
protected override void OnPreviewTextInput(TextCompositionEventArgs e)
{
var prefix = "OnPreviewTextInput() - ";
logger.Debug(prefix + "Entering");
string currentText = this.Text;
string candidateText = currentText + e.Text;
// If we have a set regex, and the current text fails,
// mark as handled so the text is not processed.
if (_regex != null && !_regex.IsMatch(candidateText))
{
e.Handled = true;
}
base.OnPreviewTextInput(e);
}
} // end of class RegexTextbox
} // end of MyNamespace
/////////////////////
// MAINWINDOW.XAML //
/////////////////////
//(Window class needs to know your namespace so it needs xmlns:myNamespace="clr-namespace:MyNamespace")
<myNamespace:RegexTextBox
x:Name="textboxPayToAmount"
Text="{Binding PayToAmount}">
</myNamespace:RegexTextBox>
////////////////////////
// MAINWINDOW.XAML.CS //
////////////////////////
namespace MyNamespace
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
textboxPayToAmount.Regex =
new System.Text.RegularExpressions.Regex(#"^\d*(\.\d{0,8})?$");
}
}
}
Public Shared Function GetWordCount(str As String) As Integer
Dim collection As MatchCollection = Regex.Matches(str, "\S+")
Return collection.Count
End Function
Public Shared Function GetInWordLimit(str As String, max_words As Integer) As String
Dim final As String = ""
Dim count As Integer = Core.StringOperations.GetWordCount(str)
Dim avg_max_length As Integer = max_words * 7
Dim words = str.Split(" ")
If (words.Length > max_words - 1 And count > max_words - 1) Then
Dim index As Integer = 0
For Each word In words
If index >= max_words Then Exit For
final &= word & " "
If Not (Char.IsSeparator(word) Or Char.IsWhiteSpace(word) Or word = "") Then
index += 1
End If
Next
final = final.TrimEnd
Else
final = str
End If
If final.Length > avg_max_length - 1 Then final = final.Substring(0, avg_max_length)
Return final
End Function

Multiline textbox - spaces added to beginning on postback

I have a Textbox added to an .ascx control page as follows:
<asp:TextBox id="txtDescription" runat="server" Width="500" TextMode="MultiLine" Rows="2"></asp:TextBox>
And it would show something like this when first loaded (4 spaces at the start included):
Description goes here
Put simply, it's a Multiline Textbox with 2 lines. However, whenever we do a Postback to the server, when the page reloads, we get this:
Description goes here
Another Postback.
Description goes here
And so on. Basically, every time the page is refreshed after a Postback, it adds 4 spaces to the start of the textbox. On some pages, this isn't an issue, however, if the user is entering a fair bit of data into a Gridview or some other Control, the contents of the Textbox can end up shunted 20 or so characters to the right, sometimes out of the bounds of the Textbox.
Put simply, this is an issue for our company, as it is occurring across all of our pages. One of our clients has several times made a pass "...and could you do something about the spaces at the beginning of the textboxes at some point?"
Now, a temporary fix we have employed is the following code in our PageLoad function, however, we are still left with 4 spaces at the beginning of the Textbox. And rolling it out across 100's of .ascx and .aspx controls/pages isn't really a solution.
if (IsPostBack)
txtDescription.Text = txtDescription.Text;
Now the big question is, does anyone know how to remove these mysterious 4 spaces that keep getting added to the start of a Multiline Textbox?
A typical html textarea (multiline textbox) looks like this:
<textarea>This is some content</textarea>
Now if your output looks like:
<textarea>
This is some content
</textarea>
Then you will introduce your space issue.
When you save this content you gain the 4 spaces at the beginning (the indentation). Now you load those 4 spaces back and suddenly you have 8 spaces: 4 from the markup and 4 from the saved content. This pattern continues each time you save the content.
I am unfamiliar with how you (ASP Web Forms) are generating your content, but this should give you a point of investigation.
I encounter this problem too. May be mono asp.net bug.
This is my found way,
build a custom server control to replace multiline textbox.
It's work with asp.net integrated validation framework.
Somebody have better idea?
[ValidationProperty("Text")]
[ControlValueProperty("Text")]
[DefaultProperty("Text")]
public class TextArea: WebControl, IPostBackDataHandler, IEditableTextControl
{
protected override HtmlTextWriterTag TagKey
{
get { return HtmlTextWriterTag.Textarea; }
}
public bool CausesValidation
{
get
{
if (ViewState["CausesValidation"] == null)
return false;
return (bool)ViewState["CausesValidation"];
}
set
{
ViewState["CausesValidation"] = value;
}
}
public string ValidationGroup
{
get
{
return (string)ViewState["ValidationGroup"] ?? "";
}
set
{
ViewState["ValidationGroup"] = value;
}
}
public string Text
{
get
{
return (string)ViewState["Text"] ?? "";
}
set
{
ViewState["Text"] = value;
}
}
public bool ReadOnly
{
get
{
if (ViewState["Readonly"] == null)
return false;
return (bool)ViewState["Readonly"];
}
set
{
ViewState["Readonly"] = value;
}
}
public int Rows
{
get
{
if (ViewState["Rows"] == null)
return 0;
return (int)ViewState["Rows"];
}
set
{
ViewState["Rows"] = value;
}
}
public int Columns
{
get
{
if (ViewState["Columns"] == null)
return 0;
return (int)ViewState["Columns"];
}
set
{
ViewState["Columns"] = value;
}
}
protected override void AddAttributesToRender(HtmlTextWriter writer)
{
writer.AddAttribute(HtmlTextWriterAttribute.Name, UniqueID);
if (Enabled && !IsEnabled)
writer.AddAttribute(HtmlTextWriterAttribute.Disabled, "disabled");
if (ReadOnly)
writer.AddAttribute(HtmlTextWriterAttribute.ReadOnly, "readonly");
if (Rows != 0)
writer.AddAttribute(HtmlTextWriterAttribute.Rows, Rows.ToString(NumberFormatInfo.InvariantInfo));
if (Columns != 0)
writer.AddAttribute(HtmlTextWriterAttribute.Cols, Columns.ToString(NumberFormatInfo.InvariantInfo));
base.AddAttributesToRender(writer);
}
public override void RenderControl(HtmlTextWriter writer)
{
RenderBeginTag(writer);
writer.WriteEncodedText(Text);
RenderEndTag(writer);
}
public bool LoadPostData(string postDataKey, NameValueCollection postCollection)
{
var strPostBack = postCollection[postDataKey];
if (ReadOnly || Text.Equals(strPostBack, StringComparison.Ordinal))
return false;
Text = strPostBack;
return true;
}
public void RaisePostDataChangedEvent()
{
if (!Page.IsPostBackEventControlRegistered)
{
Page.AutoPostBackControl = this;
if (CausesValidation)
Page.Validate(ValidationGroup);
}
TextChanged(this, EventArgs.Empty);
}
public event EventHandler TextChanged = delegate { };
}
I also had same issue. To remove extra spaces use textbox_TextChanged event and bind it to textbox.
protected void txtDescription_TextChanged(object sender, EventArgs e)
{
String strReplace = Regex.Replace(txtDescription.Text.Trim(),#"\t|\n|\r","");
txtDescription.Text = strReplace;
}
Bind TextChanged event to textbox:
<asp:TextBox id="txtDescription" runat="server" Width="500" TextMode="MultiLine" Rows="2" OnTextChanged="txtDescription_TextChanged"></asp:TextBox>
Note: It won't remove the first space, but will work with subsequent spaces of postback.
Your only option is to breakpoint the code and see where they are appearing from.

Correct way to implement web part personalisation for listboxes

Trying to work out this whole web part personalisation, and trying to implement it for a list box.
Well the end result will be two list boxes, with interchangeable values (ie, a value will only exist in one of the listboxes)
But I can't maintain the datasource for it. So maybe I'm going about it wrong?
This is what I have for a test H2 tag on the page
[Personalizable(PersonalizationScope.User)]
public string LabelText {
get { return h2Test.InnerText; }
set { h2Test.InnerText = value; }
}
And it works fine, if I have a textbox and use it to change the value of LabelText, then when I close the browser it automagically persists the change.
So I thought, ok, then maybe the same will work with a list box
[Personalizable(PersonalizationScope.User)]
public DomainList Domains {
get { return (DomainList)lstBxDomains.DataSource; }
set {
lstBxDomains.DataSource = value;
lstBxDomains.DataBind();
}
}
Where DomainList is just a class which extends List, and Domain is just a three field class, int, string, string.
But it doesn't, so is this too complicated for the webpart personalisation automagican, or have i just implement it wrongly (Which is more than likely)
This is my event handler to remove the items from the list:
protected void btnRemDomain_Click(object sender, EventArgs e) {
if (IsPostBack && lstBxDomains.SelectedIndex > -1) {
for (int i = 0; i < lstBxDomains.Items.Count; i++) {
if (lstBxDomains.Items[i].Selected) {
Domains.Remove(Domains.Find(d => d.ID.ToString() == lstBxDomains.Items[i].Value));
}
}
Domains = Domains;
}
}
The Domains=Domains; line is in there to see if explicitly setting the value made a difference (as Removing doesn't acutally reset the value of the field), but it doesn't. I've also tried creating a new local DomainList setting it to the global one, and then doing the remove/find on it, and then setting the local one to the global. But not working either.
I have managed to resolve this by using WebPart.SetPersonalizationDirty(this); in the set accessor of Domains, but would someone mind confirming if this is an appropriate way to do it?

Categories

Resources