How to avoid default text in textbox? - wpf

I have a textbox, which uses multi-binding usingStringFormat...as shown below.
But it displays the default value as
{DependencyProperty.UnsetValue},{DependencyProperty.UnsetValue}
How to avoid this ?
<StackPanel Orientation="Horizontal">
<TextBlock Width="70" Text="Name:" Margin="5,2,2,2"></TextBlock>
<TextBox Width="160" DataContext="{Binding }" IsReadOnly="True" Margin="2">
<TextBox.Text>
<MultiBinding StringFormat="{}{0},{1}">
<Binding Path="LastName"/>
<Binding Path="FirstName"/>
</MultiBinding>
</TextBox.Text>
</TextBox>
</StackPanel>
Please help me.

Something is wrong with the object you are binding to. I just created an application from scratch with a Person class that inherits from DependencyObject. I left the first and last name properties unset and I did not see DependencyProperty.UnsetValue, but rather a blank TextBox with just a comma in it.
(In general, you shouldn't use dependency properties on your business objects anyway. Stick to INotifyPropertyChanged and save yourself a ton of headaches.)
Post the code to your bound object and maybe I can spot the issue.
public class Person : DependencyObject
{
public static readonly DependencyProperty FirstNameProperty = DependencyProperty.Register("FirstName", typeof(string), typeof(Person), new FrameworkPropertyMetadata());
public string FirstName {
get { return (string)GetValue(FirstNameProperty); }
set { SetValue(FirstNameProperty, value); }
}
public static readonly DependencyProperty LastNameProperty = DependencyProperty.Register("LastName", typeof(string), typeof(Person), new FrameworkPropertyMetadata());
public string LastName {
get { return (string)GetValue(LastNameProperty); }
set { SetValue(LastNameProperty, value); }
}
}
-
<TextBox IsReadOnly="True">
<TextBox.Text>
<MultiBinding StringFormat="{}{1}, {0}">
<Binding Path="FirstName" />
<Binding Path="LastName" />
</MultiBinding>
</TextBox.Text>
</TextBox>
-
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
}
private void Window_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
var p = new Person();
//p.FirstName = "Josh";
//p.LastName = "Einstein";
DataContext = p;
}
}

just give the fallbackvalue="" and see
<TextBox.Text>
<MultiBinding StringFormat="{}{0},{1}">
<Binding Path="LastName" FallbackValue=""/>
<Binding Path="FirstName" FallbackValue=""/>
</MultiBinding>
</TextBox.Text>
If a binding is not successful, i.e. the path to the binding source is not found or the value converter , if any, fails, a DependencyProperty.UnsetValue is returned, then the target property is set to the FallbackValue, if you defined one of course.

Related

WPF: How to reuse XAML Fragment?

I want to reuse an XAML fragment with substitution strings as parameters.
sort of like a #define with some function style arguments.
Can I do this?
If so, how is the best way to go around doing it?
So, here's invalid XAML of what I want to do
<Template Base="TextBox" key="ValidatedTextBox">
<TextBox.Text>
<Binding NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged" Path="{SomeAttributeName}">
<Binding.ValidationRules>
<local:SomeRule></local:SomeRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</Template>
...
and then elsewhere in XAML, instead of using a TextBox, I'd do
<ValidatedTextBox SomeAttributeName="MyPropertyToBeBound" AttributeNotOnTemplate="Value">
<ElementNotOnTemplate />
</ValidatedTextBox>
In particular, I want to be able to customize instances of this template.
I'm happy to read docs, but I don't know what to search for to find appropriate docs that aren't hilariously complicated for essentially a find-and-replace mechanism.
I'm not sure whether you're just trying to add validation to a TextBox or create a custom control. If you're struggling with the validation this post gives a very nice overview. In summary, you can do something like this
public class ViewModel : System.ComponentModel.IDataErrorInfo
{
public ViewModel()
{
/* Set default age */
this.Age = 30;
}
public int Age { get; set; }
public string Error
{
get { return null; }
}
public string this[string columnName]
{
get
{
switch (columnName)
{
case "Age":
if (this.Age < 10 || this.Age > 100)
return "The age must be between 10 and 100";
break;
}
return string.Empty;
}
}
}
And then use it like this
<TextBox Text="{Binding Path=Age, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"/>
If you'd like to create your own error template you can do it for an individual TextBox like this
<TextBox Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}">
<Validation.ErrorTemplate>
<ControlTemplate>
<StackPanel>
<!-- Placeholder for the TextBox itself -->
<AdornedElementPlaceholder x:Name="textBox"/>
<TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
</StackPanel>
</ControlTemplate>
</Validation.ErrorTemplate>
</TextBox>
Alternatively, you can define a style like this
<Style TargetType="x:TextBox">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<AdornedElementPlaceholder/>
<TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If you're trying to create a custom control this post and this question are worth looking at. The key part is adding a DependencyProperty to your control like this
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(MyTextBox), new PropertyMetadata(string.Empty, OnTextChangedCallback));
private static void OnTextChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
// Validation in here
}
Then you could use it like this
<MyTextBox Text="My Text" .../>

MultiValueConverter reading from ObservablleCollection

I am working on a wpf mvvm project. In a user control I have a datagridControl from Devexpress that is bound to data from a Observable collection.
<xcdg:DataGridControl x:Name="DataGridName" HorizontalAlignment="left" VerticalAlignment="Stretch"
AutoCreateColumns="False"
ItemsSource="{Binding ViewModel.Items}"
ItemScrollingBehavior="Immediate" SynchronizeCurrent="True" TabIndex="69" >
<xcdg:DataGridControl.Columns >
<xcdg:Column FieldName="Name" AllowSort="False" Title="Name" ShowInColumnChooser="False" />
</xcdg:DataGridControl.Columns>
</xcdg:DataGridControl>
The class in the Observable collection Contains a Name (string) and IsVerified (Boolean).
private ObservableCollection<myData> _items = new ObservableCollection<myData>();
public ObservableCollection<myData> Items
{
get { return _items; }
set { _items = value; }
}
public class myData
{
public string Name { get; set; }
public bool IsVerfied { get; set; }
}
I also have a textblock that I use to display an error message above the dataGrid when the Value of IsVerfied is false.
<TextBlock Name="textBlockErrrMessage" Foreground="IndianRed">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MultiValueConverter}">
<Binding Path="DataContext.IsVerified" RelativeSource="{RelativeSource AncestorType=xcdg:DataRow}" ElementName="DataGridName" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
To do this I plan on having a multivalueconverter (I am also doing the same thing but for a different control so that is why I choose a MultiValueConverter) that I would like to send the IsVerfied value from the Collection and return the message. My issue is how do I set the Binding in the MultiBinding to read the IsVerfied value from the Observablecollection. This particular line is what I believe is the issue in locating the Collection value
<Binding
Path="DataContext.IsVerified"
RelativeSource="{RelativeSource AncestorType=xcdg:DataRow}"
ElementName="DataGridName" />
In your Binding, you want to use either RelativeSource or ElementName, but not both. See this post for a good clarification on the differences between the two.

Binding as a Resource

Can I define a Binding as a Resource and then reuse it with different Controls properties?
Example:
Binding:
<Window.Resources>
<Binding x:Key="MyBinding" Path="MyProperty" Mode="TwoWay" />
</Window.Resources>
Reuse in XAML:
<TextBox Text="{StaticResource MyBinding}" />
After declaring Binding as above I got the error:
"The name 'InitializeComponent' does not exist in the current
context"
Is there any way to reuse the same Binding in different contexts?
Direct answer to your question is "yes, you can define a binding as a resource". The problem here is how do you then make any use of it? One possibility is to create an extension class which would pull the binding from the resources and apply it:
public class BindingResourceExtension : StaticResourceExtension
{
public BindingResourceExtension() : base() { }
public BindingResourceExtension(object resourceKey) : base(resourceKey) { }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var binding = base.ProvideValue(serviceProvider) as BindingBase;
if (binding != null)
return binding.ProvideValue(serviceProvider);
else
return null; //or throw an exception
}
}
Usage example:
<Window.Resources>
<ResourceDictionary>
<Binding x:Key="MyBinding" Path="MyProperty" Mode="TwoWay" />
</ResourceDictionary>
</Window.Resources>
(...)
<TextBox Text="{ns:BindingResource MyBinding}" />
Can this solution be used in MultiBinding?
Yes, it can:
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="First: {0}, Second: {1}">
<Binding Path="SomeProperty" />
<ns:BindingResource ResourceKey="MyBinding" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
There is however one drawback to this - although everything will work in run-time, the XAML Designer will complain that BindingResourceExtension is not of proper type to be put in the MultiBinding.Bindings collection. But, thankfully, there is a quick solution - simply use StaticResourceExtension instead! So this, while being functionally equivalent in run-time, will be accepted by the designer:
<TextBlock>
<TextBlock.Text>
<MultiBinding StringFormat="First: {0}, Second: {1}">
<Binding Path="SomeProperty" />
<StaticResource ResourceKey="MyBinding" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Here are two ways to not do exactly what you want:
1. Using a custom markup extension
Skipped all nullchecks etc. to keep it short.
using System;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
public class BindingDefinition
{
public PropertyPath Path { get; set; }
public BindingMode Mode { get; set; }
}
[MarkupExtensionReturnType(typeof(BindingExpression))]
public class ApplyBindingDefinition : MarkupExtension
{
public BindingDefinition Definition { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var binding = new Binding
{
Path = this.Definition.Path,
Mode = this.Definition.Mode
};
return binding.ProvideValue(serviceProvider);
}
}
<Window.Resources>
<local:BindingDefinition x:Key="MyProperty"
Mode="TwoWay"
Path="MyProperty" />
</Window.Resources>
<TextBox>
<TextBox.Text>
<!-- using element style here as the parser chokes on parsing nested markupextensions -->
<local:ApplyBindingDefinition Definition="{StaticResource MyProperty}" />
</TextBox.Text>
</TextBox>
2. Making the PropertyPath a resource
May or may not be enough for your needs.
<Window.Resources>
<PropertyPath x:Key="MyPropertyPath">MyProperty</PropertyPath>
</Window.Resources>
...
<TextBox Text="{Binding Path={StaticResource MyPropertyPath}}" />

WPF validation launched on focus gained

I'm developing a IDataErrorInfo to validate the textboxes I have inside my application. I have the following code:
The .cs class to validate:
public class UserInformation : IDataErrorInfo
{
public string _name;
public string _surname;
public string Name
{
get { return _name; }
set { _name = value; }
}
public string Surname
{
get { return _surname; }
set { _surname = value; }
}
public override string ToString()
{
return Name + " " + Surname;
}
public string this[string columnName]
{
get
{
if (columnName == null) return string.Empty;
string result = string.Empty;
if (columnName.Equals("Name"))
{
if (string.IsNullOrEmpty(_name))
result = "Name cannot be empty.";
}
return result;
}
}
public string Error { get; private set; }
}
The .xaml:
<TextBox Grid.Column="3" Grid.Row="0" Name="TextBoxName"
Style="{DynamicResource InnerTextBox}"
Validation.ErrorTemplate="{StaticResource ValidationErrorTemplate}">
<TextBox.Text>
<Binding Path="Name" Source="{StaticResource UserInformation}"
ValidatesOnDataErrors="True" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
And the ErrorTemplate:
<ControlTemplate x:Key="ValidationErrorTemplate">
<DockPanel >
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
<Grid Width="20" Height="20">
<Ellipse Width="20" Height="20" Fill="Tomato" HorizontalAlignment="Center" VerticalAlignment="Center" />
<TextBlock Foreground="White" FontWeight="Heavy" FontSize="8" HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center"
ToolTip="{Binding ElementName=ErrorAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}">X</TextBlock>
</Grid>
<TextBlock Foreground="Tomato" FontWeight="12" Margin="2,0,0,0" FontSize="20"
Text="{Binding ElementName=ErrorAdorner, Path=AdornedElement.(Validation.Errors)[0].ErrorContent}" />
</StackPanel>
<AdornedElementPlaceholder x:Name="ErrorAdorner" />
</DockPanel>
</ControlTemplate>
The code works fine when I'm typing. But when the TextBox is loaded, the validation occurs too. And I don't want it to happen when it gains focus, only when it looses it or I change the text (like the one published here).
How can I avoid the validation error to be considered on first TextBox load?
NOTE: Even if I set the UpdateSourceTrigger to LostFocus, it is still making the validations.
To acheive you goal you need to:
First, remove ValidatesOnDataErrors="True" on your Binding. As said in docs:
Setting this property provides an alternative to using the
DataErrorValidationRule element explicitly
And we're gonna use it explicitly. Then use DataErrorValidationRule instead of ExceptionValidationRule for correctly working with IDataErrorInfo and data errors.
And last, we need to use some properties that this rule gives us:
<Binding.ValidationRules>
<DataErrorValidationRule ValidatesOnTargetUpdated="False" />
</Binding.ValidationRules>
ValidatesOnTargetUpdated on false will not trigger validation when target itself changes (i.e. on load). You can also play with ValidationStep property for additional control.
Edit:
Ok, I see that you need to skip validation on load and you need to validate on lost focus even if the value was not changed. Well, validation rules does not support that, because if the value was not updated, then no changed events will be called and no validation will occur, regardless of UpdateSourceTrigger setting.
The easy way out is to emulate this functionality by adding LostFocus handler to TextBox itself:
private void ValidatedTextBox_LostFocus(object sender, RoutedEventArgs e)
{
var txt = (TextBox)sender;
txt.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
If you need this for several TextBoxes, you can move the code to some static class.
The same results can be achieved using Explicit update source trigger, wich can be a little bit more cleaner.
I dont have any example atm, because I moved to what you have. But You will need to create a class which will Inherit from ValidationRule which exist in system.windows.controls, and then override Validate method.
Then your xaml textbox would look something like this instead
<TextBox.Text>
<Binding Path="your binding here" UpdateSourceTrigger="LostFocus" >
<Binding.ValidationRules>
<validationClass:yourRule/> define this at the top of xaml page
</Binding.ValidationRules>
</Binding>
You should be able to find examples on msdn, and here about validation rules

Validate two TextBox.Text values to be distinct

Grab two TextBox and say that you need to validate that their content are distinct strings.
Example :
Correct result : prefix1, prefix2
Incorrect result : prefix1, prefix1
To do that task I thought about using a MultiBinding but two problems arises then :
Where should it be placed ? currently it is on a dummy TextBox
Even using that dummy TextBox, the ValidationRule is never called
Not sure whether this approach is correct, how would you do that ?
<MultiBinding Converter="{StaticResource MyConverter}">
<Binding Path="GradientPrefix"
Source="{StaticResource Indices}"
UpdateSourceTrigger="PropertyChanged" />
<Binding Path="ColorPrefix"
Source="{StaticResource Indices}"
UpdateSourceTrigger="PropertyChanged" />
<MultiBinding.ValidationRules>
<gpl2Xaml:DistinctStringValidationRule />
</MultiBinding.ValidationRules>
</MultiBinding>
Here's the solution using a BindingGroup !
Error at BindingGroup level :
Error at BindingGroup and field levels :
No errors :
Here's the code :
<Window>
<Window.Resources>
<gpl2Xaml:Indices x:Key="Indices"
ColorIndex="1"
ColorPrefix="MyColor"
GradientIndex="1"
GradientPrefix="MyColor" />
</Window.Resources>
<Grid DataContext="{StaticResource Indices}"
Style="{StaticResource gridInError}"
Validation.ErrorTemplate="{StaticResource validationTemplate}">
<Grid.BindingGroup>
<BindingGroup>
<BindingGroup.ValidationRules>
<gpl2Xaml:DistinctValidationRule />
</BindingGroup.ValidationRules>
</BindingGroup>
</Grid.BindingGroup>
<TextBox x:Name="TextBoxGradientPrefix"
Style="{StaticResource textBoxInError}"
TextChanged="TextBoxGradientPrefix_OnTextChanged"
Validation.ErrorTemplate="{StaticResource validationTemplate}">
<Binding Path="GradientPrefix"
Source="{StaticResource Indices}"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<gpl2Xaml:StringValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox>
<TextBox x:Name="TextBoxColorPrefix"
Style="{StaticResource textBoxInError}"
TextChanged="TextBoxColorPrefix_OnTextChanged"
Validation.ErrorTemplate="{StaticResource validationTemplate}">
<Binding Path="ColorPrefix"
Source="{StaticResource Indices}"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<gpl2Xaml:StringValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox>
</Grid>
</Window>
Extra code to trigger validation every time :
private void TextBoxGradientPrefix_OnTextChanged(object sender, TextChangedEventArgs e)
{
grid.BindingGroup.CommitEdit();
}
private void TextBoxColorPrefix_OnTextChanged(object sender, TextChangedEventArgs e)
{
grid.BindingGroup.CommitEdit();
}
And the validation rule :
public class DistinctValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
var bindingGroup = value as BindingGroup;
if (bindingGroup == null) return new ValidationResult(false, "Not a BindingGroup");
var o = bindingGroup.Items[0] as Indices;
if (o == null) return new ValidationResult(false, "Not an Indices");
if (o.ColorPrefix == o.GradientPrefix)
return new ValidationResult(false, "Color prefix and Gradient prefix must be distinct.");
return new ValidationResult(true, null);
}
}

Resources