I want to allow the user to input only text that is validated by the TextBox binding validation rules. I figured out a way of doing that:
public static void PreviewTextChanged(
object sender,
PreviewTextChangedEventArgs e)
{
var textBox = e.Source as TextBox;
var bindingExpression = textBox.GetBindingExpression(TextBox.TextProperty);
if (!ReferenceEquals(null, bindingExpression))
{
// save original parameters for possible restoration
var originalSelectionStart = textBox.SelectionStart;
var originalSelectionLength = textBox.SelectionLength;
var originalText = textBox.Text;
// check validation
textBox.Text = e.Text;
if (!bindingExpression.ValidateWithoutUpdate())
{
// restore original values
textBox.Text = originalText;
bindingExpression.UpdateSource();
textBox.SelectionStart = originalSelectionStart;
textBox.SelectionLength = originalSelectionLength;
}
else
{
// correct the selection
var selectionStart = originalSelectionStart +
originalSelectionLength +
e.Text.Length -
originalText.Length;
textBox.SelectionStart = Math.Max(selectionStart, 0);
textBox.SelectionLength = 0;
}
e.Handled = true;
}
}
The code above works. But it would be much simpler, and less bug-prone, if I could find a way to check if the new value is valid without updating the binding target. Is there one?
I think it's easier in this case to relay on Binding Converters.
The converter will be called every time the data should be assigned to the binded field.
Inside methods of it, you can validate the input of the data and based on validation result return or old data (cause validation fails) recovered from ModelView or accept it, in case when validation succeeded.
Hope this helps.
Related
I have a gridview that is bound to a sql database. As the user enters data they must indicate whether that cell's information is complete or not. To do this they enter /end/ at the end of their statement and it will automatically change the cell color. If nothing is entered then nothing happens.
Here is the code:
if (dataItem != null)
{
var label = dataItem["Status"].FindControl("Statuslbl") as Label;
if (label != null)
{
var item = dataItem;
var text = label.Text;
if (text.Contains("/end/"))
{
item["Status"].BackColor = Color.Lime;
item["Status"].Text = item["Status"].Text.Replace(#"/end/", #"");
}
else
{
item["Status"].BackColor = Color.Salmon;
}
}
}
Instead of just hiding the '/end/' like I need it to, it hides the entire cells contents.
How can I go about fixing this?
Discovered all I would need to do would be the following to achieve my result:
if (text.Contains("/end/"))
{
item["Test"].BackColor = Color.Lime;
item["Test"].Text = label.Text.Replace("/end/", " ");
}
Really simple, I just needed to use label.Text.
I am experiencing issued performing validation from the codebehind. My data is displayed in a datagrid. One of the columns (type) is a drop down and when the drop down menu is changed it triggers a DropDownClosed Event which is handled in the code behind.
What I am trying to achieve is to validate the content of the following column to match the newly selected type in the drop down. If it does not match i want a validation error to be displayed on the grid. I implemented my validation using the INotifyDataErrorInfo interface and it works really well except when I use it in the code behind. When the code behind calls the validation the ValidationSummary of the datagrid is never updated. What I am doing wrong here ??? When using the debugger I can clearly see the errors being added to the Errors dictionnary of the interface...
Here is the handler:
private void TypeBoxChanged(object sender, EventArgs e)
{
ComboBox box = (sender as ComboBox);
IncomingPolicy row = (IncomingPolicy)box.DataContext;
string ruleTypeValue = TypeList.GetKeyForText(box.SelectedItem.ToString());
//check if the type is the same
if(row.TypeWrapper == ruleTypeValue)
return;
if (row.ValidateRule(ruleTypeValue))
{
//SAVE the record
}
else
{
row.RaiseErrorsChanged("RuleWrapper");
}
}
The validate rule method will based on the ruletypevalue call this method
public bool ValidateRegularExpression(string property, string value, string expression, string errorMessage)
{
bool isValid = true;
Regex regex = new Regex(expression);
Match match = regex.Match(value);
if (match.Success)
{
RemoveError(property, errorMessage);
}
else
{
AddError(property, errorMessage, false);
isValid = false;
}
return isValid;
}
I followed the sample implementation on MSDN http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifydataerrorinfo%28VS.95%29.aspx
Some time earlier I've implemented validation helpers and created the sample solution for both interfaces IDataErrorInfo and INotifyDataErrorInfo:
http://vortexwolf.wordpress.com/2011/10/01/wpf-validation-with-idataerrorinfo/
Source code
The main implementation is here:
this.PropertyChanged += (s, e) =>
{
// if the changed property is one of the properties which require validation
if (this._validator.PropertyNames.Contains(e.PropertyName))
{
this._validator.ValidateProperty(e.PropertyName);
OnErrorsChanged(e.PropertyName);
}
}
You should always call the OnErrorsChanged (or RaiseErrorsChanged in your case) method regardless of success of validation: if the property is invalid - the red border will be displayed, if it is valid - the bound control will be returned to its normal state.
I'm trying to implement a search as you type (like in iTunes). I am using an ObjectListView. Further, I have a textbox that is used to do the search as shown below:
private void textBoxSearch_TextChanged(object sender, EventArgs e)
{
string txt = textBoxSearch.Text;
TextMatchFilter filter = null;
if (!String.IsNullOrEmpty(txt))
{
filter = TextMatchFilter.Contains(myObjectListView, txt);
}
// Setup a default renderer to draw the filter matches
if (filter == null)
myObjectListView.DefaultRenderer = null;
else
{
myObjectListView.DefaultRenderer = new HighlightTextRenderer(filter);
// Uncomment this line to see how the GDI+ rendering looks
myObjectListView.DefaultRenderer = new HighlightTextRenderer { Filter = filter, UseGdiTextRendering = false };
}
// Some lists have renderers already installed
HighlightTextRenderer highlightingRenderer = myObjectListView.GetColumn(0).Renderer as HighlightTextRenderer;
if (highlightingRenderer != null)
highlightingRenderer.Filter = filter;
myObjectListView.ModelFilter = filter;
}
Can someone figure out why this doesn't work?
The above code is meant to filter search results as the user types in the textbox (Like iTunes does, if you have ever used itunes). Apparently, up to this point, nothing happens. It seems like this code does not even execute.
Per this, the ObjectListView has a property named UseFiltering that is false by default and must be set to true to enable filtering.
I have TextBox which allow insert only numeric values (filtering), But when I paste copied text it's allow any kind of symbol. How can I prevent or filter text before pasting?
You could backup your text before any manual input and then when the input provided isn't valid restore the previous text like so:
_backupText = string.Empty;
doNotPasteTextBox.TextInputStart += (sender, e) =>
{
int textParsed;
if(int.TryParse(e.Text,out textParsed))
{
_backupText = doNotPasteTextBox.Text.Insert(doNotPasteTextBox.SelectionStart, e.Text);
}else
{
e.Handled = true;
}
};
doNotPasteTextBox.TextChanged += (sender, e) =>
{
int textParsed;
int selectionStart = doNotPasteTextBox.SelectionStart;
if(!int.TryParse(doNotPasteTextBox.Text, out textParsed))
{
doNotPasteTextBox.Text = _backupText;
}
doNotPasteTextBox.SelectionStart = selectionStart;
};
I wouldn't recommend trying to capture the control keys or anything because when you're on a mac or on linux then you're screwed.
Adjust my sample and pour it inside a new textbox control to make it cleaner but you get the idea.
You could use Clipboard.GetText() to get the text that is inserted into the textbox, but this will pop up a message, and the user must give the application access to the Clipboard.
If its no problem for you then i would use this.
Is it possible to do this? I need to use:
this.ControlName.DataBindings.Add (...)
so I can't define logic other than bind my enum value to a bool.
For example:
(DataClass) Data.Type (enum)
EDIT:
I need to bind Data.Type which is an enum to a checkbox's Checked property. So if the Data.Type is Secure, I want the SecureCheckbox to be checked, through data binding.
Winforms binding generates two important and useful events: Format and Parse.
The format event fires when pulling data from a source into a control and the Parse event fires when pulling data from a control back into the data source.
If you handle these events you can alter/retype the values going back and forth during binding.
For example here are a couple of example handlers for these events:
public static void StringValuetoEnum<T>(object sender, ConvertEventArgs cevent)
{
T type = default(T);
if (cevent.DesiredType != type.GetType()) return;
cevent.Value = Enum.Parse(type.GetType(), cevent.Value.ToString());
}
public static void EnumToStringValue<T>(object sender, ConvertEventArgs cevent)
{
//if (cevent.DesiredType != typeof(string)) return;
cevent.Value = ((int)cevent.Value).ToString();
}
And here is some code attaching these event handlers:
List<NameValuePair> bts = EnumHelper.EnumToNameValuePairList<LegalEntityType>(true, null);
this.cboIncType.DataSource = bts;
this.cboIncType.DisplayMember = "Name";
this.cboIncType.ValueMember = "Value";
Binding a = new Binding("SelectedValue", this.ActiveCustomer.Classification.BusinessType, "LegalEntityType");
a.Format += new ConvertEventHandler(ControlValueFormatter.EnumToStringValue<LegalEntityType>);
a.Parse += new ConvertEventHandler(ControlValueFormatter.StringValuetoEnum<LegalEntityType>);
this.cboIncType.DataBindings.Add(a);
So in your case you can just create a SecEnum to Bool handler for the format event and within that do something like:
SecEnum se = Enum.Parse(typeof(SecEnum), cevent.Value.ToString());
cevent.Value = (bool)(se== SecEnum.Secure);
and then reverse that during parse.
Well if you are binding to your class you could always have a property on it like this:
public bool IsSecured
{
get
{
if (myEnum == SecEnum.Secured)
return true;
else
return false;
}
}
Just reverse for the setter if needed.