XamlParseException datagrid - wpf

I get a XamlParseException and I don't know how to solve it. I want to be able to control the input.
xaml
<DataGridTemplateColumn Header="Naam" CellTemplate="{StaticResource ContactNameTemplate}" CellEditingTemplate="{StaticResource EditingContactNameTemplate}"/>
dictionary
<DataTemplate x:Key="ContactNameTemplate" >
<TextBox HorizontalAlignment="Center" Validation.ErrorTemplate="{StaticResource validationTemplate}" Style="{StaticResource textBoxInError}">
<TextBox.Text>
<Binding Path="Name" Source="{Binding Name}" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<c:Text Min="21" Max="130"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
<DataTemplate x:Key="EditingContactNameTemplate">
<TextBox HorizontalAlignment="Center" Validation.ErrorTemplate="{StaticResource validationTemplate}" Style="{StaticResource textBoxInError}">
<TextBox.Text>
<Binding Path="Name" Source="{Binding Name}" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<c:Text Min="21" Max="130"/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</DataTemplate>
edit
my error
A first chance exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll
Additional information: A Binding can not be set in the Source property of type Binding. A binding can only be set in a DependencyProperty of a DependencyObject.
<ControlTemplate x:Key="validationTemplate">
<DockPanel>
<TextBlock Foreground="Red" FontSize="20">!</TextBlock>
<AdornedElementPlaceholder/>
</DockPanel>
</ControlTemplate>
<Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self},Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
public class Text : ValidationRule
{
private int _min;
private int _max;
public Text()
{
}
public int Min
{
get { return _min; }
set { _min = value; }
}
public int Max
{
get { return _max; }
set { _max = value; }
}
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
ValidationResult result;
string text;
if (((string)value).Length < Min || ((string)value).Length > Max)
{
text = value.ToString();
result = new ValidationResult(true, null);
}
else
{
text = "";
result = new ValidationResult(false, "De naam moet tss " + Min + " en " + Max);
}
return result;
}
}

Related

EmptyTextValidationRule not behave as requested

Requirements: We have TextBox and when it is empty and loses focus, textbox will mark itself as "need to be filled" and raise popup.
Implementation: So far i became with following solution:
EmptyTextValidationRule actually check if textbox is empty and raise ValidationResult.
TextBox's style in XAML which responsible to to UI job (i.e. mark red color around textbox and raise popup.
Important NOTE: i cannot afford usage of code behind since this should be generic for ant textbox.
The problem is that my code is not working when textbox is empty and looses its focus.
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<Grid DockPanel.Dock="Right" Width="16" Height="16" VerticalAlignment="Center" Margin="3 0 0 0">
<Ellipse Width="16" Height="16" Fill="Red" />
<Ellipse Width="3" Height="8" VerticalAlignment="Top" HorizontalAlignment="Center" Margin="0 2 0 0" Fill="White" />
<Ellipse Width="2" Height="2" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="0 0 0 2" Fill="White" />
</Grid>
<Border BorderBrush="Red" BorderThickness="2" CornerRadius="2">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<TextBox Height="20" Width="100" />
<TextBox Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left" MinWidth="150" MaxWidth="180" Margin="5,0">
<TextBox.Text>
<Binding Path="IncidentName" Mode="TwoWay" NotifyOnSourceUpdated="True" UpdateSourceTrigger="LostFocus">
<Binding.ValidationRules>
<validators:EmptyTextValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</Grid>
EmptyTextValidationRule:
public class EmptyTextValidationRule : ValidationRule
{
private string m_errorText;
private const string TEXT_REQUERIED = "Text filed should not be empty";
public EmptyTextValidationRule()
{
}
public string ErrorText
{
get { return m_errorText; }
set
{
m_errorText = value;
}
}
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
string enterendText = value.ToString();
if (string.IsNullOrEmpty(enterendText))
{
if (string.IsNullOrEmpty(ErrorText))
return new ValidationResult(false, TEXT_REQUERIED);
else
return new ValidationResult(false, ErrorText);
}
return new ValidationResult(true, null);
}
}
This is not an attempt to answer your question, instead just pointing out a mistake in your code:
public override ValidationResult Validate(object value, System.Globalization.CultureInfo cultureInfo)
{
string enterendText = value.ToString(); //<< You'll get error here if value is null
if (string.IsNullOrEmpty(enterendText)) //<< No point checking here... too late!
{
if (string.IsNullOrEmpty(ErrorText))
return new ValidationResult(false, TEXT_REQUERIED);
else
return new ValidationResult(false, ErrorText);
}
return new ValidationResult(true, null);
}
Using this code, if value was ever null, then you'd get an error in the first if statement condition... you should do something like this instead:
string enterendText = string.Empty;
if (value == null || (enterendText = value.ToString()).Trim() == string.Empty)
{
...
}

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);
}
}

WPF Bind many to one relationship returns empty results

I have a Booking which has a list of TourGuides and I'm trying to create a Booking and list of TourGuides at the same time, the Booking binding is working, but the TourGuideOnTour is always coming back as null.
heres my models:
public class Booking : INotifyPropertyChanged {
public IList<TourGuide> TourGuidesOnTour {
get {
if (_tourGuidesOnTour == null)
return new List<TourGuide>();
return _tourGuidesOnTour;
}
set {
_tourGuidesOnTour = value;
OnPropertyChanged("TourGuidesOnTour");
}
}
}
public class TourGuide : INotifyPropertyChanged {
string _tourGuideFirstName;
public string TourGuideFirstName {
get { return _tourGuideFirstName; }
set {
_tourGuideFirstName = value;
OnPropertyChanged("TourGuideFirstName");
}
}
}
And here are my bindings:
<Grid Name="myGrid" DataContext="{Binding RelativeSource={RelativeSource Self}}">
<StackPanel Margin="10">
<StackPanel.Resources>
<Style TargetType="{x:Type StackPanel}">
<Setter Property="Margin" Value="0,10,0,0"/>
</Style>
</StackPanel.Resources>
<GroupBox Header="Tour Information">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Tour" Width="130"/>
<TextBox Grid.Row="1" Grid.Column="1" Width="185">
<TextBox.Text>
<Binding Path="TourName" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ExceptionValidationRule />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</StackPanel>
</GroupBox>
<GroupBox Header="Tour Guide Information">
<DataGrid ItemsSource="{Binding Path=TourGuidesOnTour, Mode=TwoWay}">
</DataGrid>
</GroupBox>
<Button Name="Save" Content="Save" Click="Save_Click" Width="170" Height="40" />
</StackPanel>
</Grid>
I set my Datacontext:
public Booking ReturnValue;// = null;
public CreateBooking() {
InitializeComponent();
ReturnValue = new Booking();
myGrid.DataContext = ReturnValue;
}
and ReturnValue.TourGuidesOnTour is equal to null :(
Can anyone tell me why?
I don't see anywhere in your posted code where you are initialising the TourGuidesOnTour, i.e;
ReturnValue.TourGuidesOnTour = new List<TourGuide>();
If you don't, your getter will always return a new list of TourGuide, but never initialise the internal variable.
So, you could either Initialise the TourGuidesOnTour or perhaps if you modify your getter to the following;
get
{
if (_tourGuidesOnTour == null)
_tourGuidesOnTour= new List<TourGuide>();
return _tourGuidesOnTour;
}

How to Bind on a Property by String as Propertyname

Hi i want to Bind to an "unknown" (i only get a string) Property in Xaml
at first i wrote an IValueConverter but you can't bind to ConverterParameter
so i rewrite it as IMultiValueConverter but now i'm unable to figure out how to use the <Binding /> with out Path
or my i'm wrong?
if you write <TextBlock Text="{Binding}" /> you will get the object Person
and with {Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=View.Columns[0].Header}} i'm able to access the Header Text of the first row
now i'm only need to combine both and a will get the Property right?
my test Xaml code:
<UserControl x:Class="Frameworktest.View.auswahl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Frameworktest">
<UserControl.Resources>
<local:toPropertyConverter x:Key="PropertyConverter"/>
</UserControl.Resources>
<StackPanel>
<!--...-->
<Border BorderThickness="5" HorizontalAlignment="Left" VerticalAlignment="Top"
BorderBrush="Green" CornerRadius="5">
<ListView Name="listView1" IsSynchronizedWithCurrentItem="False"
ItemsSource="{Binding Items, UpdateSourceTrigger=PropertyChanged}" <!--ObservableCollection<Person>-->
SelectedItem="{Binding selectedItem, UpdateSourceTrigger=PropertyChanged}">
<ListView.View>
<GridView>
<GridViewColumn Header="Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="1">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource PropertyConverter}">
<Binding /><!-- How do i {Binding} here?-->
<Binding Source="{Binding RelativeSource={Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}, Path=View.Columns[0].Header}}" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Firstname" >
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="1" Text="{Binding Path=Name}" Width="100"/><!--works-->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Age">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Margin="1" Text="{Binding Age}" Width="50"/><!--works-->
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Border>
</StackPanel>
</UserControl>
the Converter:
public class toPropertyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0].GetType().GetProperty((string)values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The Model
public class Person : MBase, IContains
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value;
RaisePropertyChanged(() => Reg(() => Name));
}
}
private string _firstname;
public string Firstname
{
get { return _firstname; }
set
{
_firstname = value;
RaisePropertyChanged(() => Reg(() => Firstname));
}
}
private int _age;
public int Age
{
get { return _age; }
set
{
_age = value;
RaisePropertyChanged(() => Reg(() => Age));
}
}
public bool Contains(string text)
{
string pers = string.Format("{0} {1}", Firstname, Name);
return pers.Contains(text);
}
}
Update my current Multibindung
<MultiBinding Converter="{StaticResource PropertyConverter}">
<Binding Path="."/>
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type ListView}}" Path="View.Columns[0].Header}}" /> <!-- doesn't contain the word "Name" like i suspected -->
</MultiBinding>
LAST Update
it is a dead end in my case you can't Bind from the GridViewColumn.CellTemplate to the specific Column Header Value
{Binding} implicitely means : {Binding Path=.}.
So you could use
<Binding Path="."/>

Stop ValidationRule if ComboBox is Collapsed

I have two ComboBox's - cbo_client_pay_method & cbo_terms
One of the cbo_client_pay_method items (On Account) requires cbo_terms (30 days etc...) to be Visible else its Collapsed, I have this functionality already set up in cbo_payment_type_SelectionChanged event.
I have implemented a validationRule that test if the cbo's are not null && if selectedValue <0 (something is selected), this works ok.
This all works great unless the cbo's are collapsed, the validation still fires!
Can I halt the validationRule if the element is collapsed?
<StackPanel Name="sp_account" Orientation="Horizontal" VerticalAlignment="Center">
<Label Content="Payment" Style="{StaticResource formLabel}"/>
<Grid>
<ComboBox Name="cbo_client_pay_method" Style="{StaticResource reminder_cbo}" SelectionChanged="cbo_client_payMethod_SelectionChanged" Validation.ErrorTemplate="{StaticResource validationTemplate}">
<ComboBox.SelectedValue>
<Binding Path="client_payment_type_id" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<local:ValidCbo ErrorMessage="Select A Payment Type" />
</Binding.ValidationRules>
</Binding>
</ComboBox.SelectedValue>
</ComboBox>
<TextBlock Name="txtSelectPayMethod" Text="Please Select A Payment Method..." Style="{StaticResource cbo_overlay}" />
</Grid>
</StackPanel>
<StackPanel Name="sp_terms" Orientation="Horizontal" VerticalAlignment="Center">
<Label Content="Terms" Style="{StaticResource formLabel}"/>
<Grid>
<ComboBox Name="cbo_terms" Style="{StaticResource reminder_cbo}" SelectionChanged="cbo_terms_SelectionChanged" Validation.ErrorTemplate="{StaticResource validationTemplate}">
<ComboBox.SelectedValue>
<Binding Path="terms_id" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<local:ValidCbo ErrorMessage="Select Payment Terms" />
</Binding.ValidationRules>
</Binding>
</ComboBox.SelectedValue>
</ComboBox>
<TextBlock Name="txtSelectTerms" Text="Please Select Payment Terms..." Style="{StaticResource cbo_overlay}" />
</Grid>
</StackPanel>
public class ValidCbo : ValidationRule
{
private string _errorMessage;
public string ErrorMessage
{
get { return _errorMessage; }
set { _errorMessage = value; }
}
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
//if (this.ErrorMessage.Contains("Master") |)
if (value == null )
{
// value = null
return new ValidationResult(false, this.ErrorMessage);
}
else
{
// Not null
int selectedValue = (int)value;
if (selectedValue < 0)
{
return new ValidationResult(false, this.ErrorMessage);
}
else
{
return ValidationResult.ValidResult;
}
}
}
}
You could apply a style which only binds the value if the ComboBox is visible:
<ComboBox.Style>
<Style TargetType="{x:Type ComboBox}">
<Style.Triggers>
<Trigger Property="Visibility" Value="Visible">
<Setter Property="SelectedValue">
<Setter.Value>
<Binding Path="terms_id" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<local:ValidCbo ErrorMessage="Select Payment Terms" />
</Binding.ValidationRules>
</Binding>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.Style>
You must not set the SelectedValue in the ComboBox itself or it will overwrite the style though.

Resources