I am coding the following piece of code and it is looking like it is functional. However once I get a validation error it stays even if the validation error has been corrected. I am using validation at row level as well as at cell level on my DataGridTextColumn using the EditingElementStyle parameter but this can't be used in a DataGridTemplateColumn. Is there an equivalent that I can use as I am not sure how to proceed. Below is a sample of my code showing one of my DataGridTextColumns and my DataGridTemplateColumn.
<dg:DataGridTemplateColumn Header="Instrument" MinWidth="140">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate >
<TextBlock Text="{Binding Path=PRODUCTNO, Mode=TwoWay}"/>
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
<dg:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox IsEditable="True" ItemsSource="{StaticResource TestList}">
<ComboBox.Text>
<Binding Path="PRODUCTNO" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<ValidationRules:IntegerValidationRule />
</Binding.ValidationRules>
</Binding>
</ComboBox.Text>
</ComboBox>
</DataTemplate>
</dg:DataGridTemplateColumn.CellEditingTemplate>
</dg:DataGridTemplateColumn>
<dg:DataGridTextColumn Header="BATCH No." Width="100" EditingElementStyle="{StaticResource CellEditStyle}">
<dg:DataGridTextColumn.Binding>
<Binding Mode="TwoWay" Path="BATCHNO">
<Binding.ValidationRules>
<ValidationRules:StringValidationRule/>
</Binding.ValidationRules>
</Binding>
</dg:DataGridTextColumn.Binding>
</dg:DataGridTextColumn>
Maybe try out what this person did. The code snippets on the page don't seem to work, but what he suggests is to rather use a customised DataGridTextColumn instead of DataGridTemplateColumn. I'm going to try that and I'll update my answer when (or if) it works.
Related
How to Show Two textBox control content in third one using concatenating in WPF only in xaml binding no viewmodel code & code behind code.
Something like this should do the trick.
<TextBlock>
<Run Text="{Binding Path=Text, ElementName=txt1}"/>
<Run Text="{Binding Path=Text, ElementName=txt2}"/>
</TextBlock>
Where the ElementName property is the Name of your TextBoxes.
If you do not want a space between the runs, then put them on the same line like this.
<TextBlock>
<Run Text="{Binding Path=Text, ElementName=txt1}"/><Run Text="{Binding Path=Text, ElementName=txt2}"/>
</TextBlock>
EDIT:
Sorry just re-read the question, and believe you want the output to be in another TextBox. TextBoxes do not support the Run object so you have to approach this with a MultiBinding.
<TextBox>
<TextBox.Text>
<MultiBinding StringFormat="{}{0}{1}">
<Binding Path="Text" ElementName="txt1" />
<Binding Path="Text" ElementName="txt2" />
</MultiBinding>
</TextBox.Text>
</TextBox>
Hopefully this helps!
I am trying to validate some input data in WPF, and am using the DataResource proxy as described here:
http://www.wpfmentor.com/2009/01/how-to-add-binding-to-property-on.html
So, here's my XAML (simplified):
<mvvm:BaseDataView
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:md="clr-namespace:Mfc.Mvvm.Master"
>
<mvvm:BaseDataView.Resources>
<ResourceDictionary>
<md:DataResource x:Key="mmSS" BindingTarget="{Binding ElementName=tbMmss,Path=Text}"/>
<md:DataResource x:Key="mmTS" BindingTarget="{Binding ElementName=tbMmts,Path=Text}"/>
</ResourceDictionary>
</mvvm:BaseDataView.Resources>
...
<TextBox x:Name="tbMmts" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="2" Grid.Column="1" Width="100" Margin="3,3,0,0">
<TextBox.Text>
<Binding Path="MTS" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<md:DoubleRangeValidationRule MinValue="10.0" MaxValue="5000.0"/>
<md:LessThanValidationRule>
<md:LessThanValidationRule.LessThanChecker>
<md:ProxyForComparisonString
CompareTo="{md:DataResourceBinding DataResource={StaticResource mmSS}}">
</md:ProxyForComparisonString>
</md:LessThanValidationRule.LessThanChecker>
</md:LessThanValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
...
<TextBox x:Name="tbMmss" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="3" Grid.Column="1" Width="100" Margin="3,3,0,0">
<TextBox.Text>
<Binding Path="STS" UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<md:DoubleRangeValidationRule MinValue="10.0" MaxValue="5000.0"/>
<md:GreaterThanValidationRule>
<md:GreaterThanValidationRule.GreaterThanChecker>
<md:ProxyForComparisonString
CompareTo="{md:DataResourceBinding DataResource={StaticResource mmTS}}">
</md:ProxyForComparisonString>
</md:GreaterThanValidationRule.GreaterThanChecker>
</md:GreaterThanValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
Validation works perfectly for the second textbox, but it crashes with a NullReferenceException for the first Textbox. The DataResource does not seem to get initialized, and Debugging says
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=tbMmss'. BindingExpression:Path=Text; DataItem=null; target element is 'DataResource'
Any ideas? Thank you!
You have got a completely wrong approach for this. Do not use Freezables. They are bad. They are not ment to be stuck in a resource and violated to deal like bridges!!! Please use attached properties or INotifyDataError info to solve this clean and properly.
I got WPF validation running (added ValidationRules to the binding) and with the template I can create nice adorners. There are many posting out there.
But I cannot find a way to display the error message outside of the adorned control in a fixed place like a TextBlock in a corner of the window e.g.
How could I achieve this? Can I bind all of my validation error messages to my DataContext (ViewModel here)?
Update: Thanks to an answer I got it partly working. The validation messages are now displayed in another label. As all the textboxes with their validation rules are created on the fly by code, the binding to do so is done this way:
Binding bindSite = new Binding();
bindSite.Source = this.validationErrorDisplayLabel;
BindingOperations.SetBinding(textBox, Validation.ValidationAdornerSiteProperty, bindSite);
But the validation messages are only forwarded to the adornersite for the last textbox for which this code was executed.
I reproduced the problem in this small example.
XAML:
<Grid>
<TextBox
Validation.ValidationAdornerSite="{Binding ElementName=ErrorDisplay}"
HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
<TextBox.Text>
<Binding>
<Binding.Path>Box1</Binding.Path>
<Binding.ValidationRules>
<local:RuleA />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox
Validation.ValidationAdornerSite="{Binding ElementName=ErrorDisplay}"
HorizontalAlignment="Left" Height="23" Margin="10,38,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
<TextBox.Text>
<Binding>
<Binding.Path>Box2</Binding.Path>
<Binding.ValidationRules>
<local:RuleA />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBlock
x:Name="ErrorDisplay"
Background="AntiqueWhite"
Foreground="Red"
Text="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.ValidationAdornerSiteFor).(Validation.Errors)[0].ErrorContent}"
HorizontalAlignment="Left" Margin="230,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" RenderTransformOrigin="2.218,-4.577" Width="177" Height="51"/>
</Grid>
The class RuleA produces a validation error when the value equals the string "A". The errors in the 2nd textbox are displayed in the TextBlock, the errors of the first one not (instead it uses the default template and gets a red border).
How can it work for both? The textblock does not need to sum up all errors but display the very first error.
You can use a BindingGroup in combination with the Validation.ValidationAdornerSite and Validation.ValidationAdornerSiteFor properties.
This blog post shows you an example of how to do this.
<StackPanel x:Name="FormRoot"
Validation.ValidationAdornerSite="{Binding ElementName=ErrorDisplay}">
<FrameworkElement.BindingGroup>
<BindingGroup Name="FormBindingGroup" />
</FrameworkElement.BindingGroup>
<TextBox>
<TextBox.Text>
<Binding BindingGroupName="FormBindingGroup"
UpdateSourceTrigger="LostFocus"
Path="Box1">
<Binding.ValidationRules>
<l:RuleA />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox>
<TextBox.Text>
<Binding BindingGroupName="FormBindingGroup"
UpdateSourceTrigger="LostFocus"
Path="Box2">
<Binding.ValidationRules>
<l:RuleA />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<ItemsControl x:Name="ErrorDisplay"
Background="AntiqueWhite"
Foreground="Red"
ItemsSource="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.ValidationAdornerSiteFor).(Validation.Errors)}"
DisplayMemberPath="ErrorContent" />
</StackPanel>
To commit the values as the user types, change the UpdateSourceTrigger values to PropertyChanged. Note that it isn't strictly necessary to use ValidationAdornerSite here; you could simply point the ErrorDisplay binding directly to the owner of the BindingGroup:
ItemsSource="{Binding ElementName=FormRoot, Path=(Validation.Errors)}"
Thanks to http://www.scottlogic.com/blog/2008/11/28/using-bindinggroups-for-greater-control-over-input-validation.html I was able to solve this with a BindingGroup and without ValidationAdornerSite.
<Window x:Class="BindingAndValidation.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:BindingAndValidation"
Title="Binding and Validation" Height="110" Width="425"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
>
<Grid x:Name="RootElement">
<Grid.Resources>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}" />
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Grid.BindingGroup>
<BindingGroup Name="LocalBindingGroup">
<BindingGroup.ValidationRules>
<local:RuleGroup />
</BindingGroup.ValidationRules>
</BindingGroup>
</Grid.BindingGroup>
<TextBox
HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120" LostFocus="TextBox_LostFocus">
<TextBox.Text>
<Binding>
<Binding.Path>Box1</Binding.Path>
<Binding.BindingGroupName>LocalBindingGroup</Binding.BindingGroupName>
<Binding.ValidationRules>
<local:RuleA />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<TextBox
HorizontalAlignment="Left" Height="23" Margin="10,38,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="120">
<TextBox.Text>
<Binding>
<Binding.Path>Box2</Binding.Path>
<Binding.BindingGroupName>LocalBindingGroup</Binding.BindingGroupName>
<Binding.ValidationRules>
<local:RuleA />
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<ItemsControl ItemsSource="{Binding Path=(Validation.Errors), ElementName=RootElement}" MinWidth="100" MinHeight="16" Margin="230,10,0,0" Background="AntiqueWhite" Foreground="Red">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label Foreground="Red" Content="{Binding Path=ErrorContent}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Button" HorizontalAlignment="Left" Margin="150,0,0,0" VerticalAlignment="Top" Width="75" Click="Button_Click" />
</Grid>
</Window>
The validation only occurs when you call CommitEdit. If you want to have it immediatly like I wished here you can use LostFocus
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
this.RootElement.BindingGroup.CommitEdit();
}
Of course for a greater project an attached property might help.
I have a CheckBox and a WrapPanel in a Grid. The WrapPanel has two TextBox's inside of it. When the CheckBox is checked the whole WrapPanel is disabled. The TextBox's are bound to Properties.Settings.Default properties. The TextBox's also use ValidationRule's to validate the input.
What I would like to do: If one or both of the TextBox's have a validation error, I would like the act of checking the CheckBox to return the TextBox's text to the last good value from the Settings.Default property and thus clear the error.
I don't really care about sticking to some strict MVVM model anymore (this window is small and doesn't even have a ViewModel. What's the simplest way of achieving what I want?
I think I'm on the right path by adding an event handler to the Checked property of the CheckBox, but it is throwing NullReference as soon as I open the window, I don't yet have a reference to minBox and maxBox when the event handler is attached, I think.
private void AllPages_Checked(object sender, RoutedEventArgs e)
{
minBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
maxBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
}
;
<CheckBox Name="AllPages" Margin ="10,0,0,0" Grid.ColumnSpan="3" Grid.Row="1" Content="All Pages"
IsChecked="{Binding Source={StaticResource Settings}, Path=Default.AllPages, Mode=TwoWay}"/>
<WrapPanel Margin="10" Grid.Row="2"
IsEnabled="{Binding ElementName=AllPages, Path=IsChecked, Converter={StaticResource boolConvert}, Mode=OneWay}">
<TextBox Name="minBox" MaxWidth="30" MinWidth="30" MaxLength="3">
<TextBox.Text>
<Binding Source="{StaticResource Settings}" Path="Default.MinPage" Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:MinValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
<Label Margin="0,0,0,0" Grid.ColumnSpan="3" Content="to"/>
<TextBox Name="maxBox" MaxWidth="30" MinWidth="30" MaxLength="3">
<TextBox.Text>
<Binding Source="{StaticResource Settings}" Path="Default.MaxPage" Mode="TwoWay"
UpdateSourceTrigger="PropertyChanged">
<Binding.ValidationRules>
<local:MaxValidationRule/>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
</WrapPanel>
Problem solved:
private void AllPages_Checked(object sender, RoutedEventArgs e)
{
if (minBox != null) minBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
if (maxBox != null) maxBox.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
}
So I have this code and I can't use the bindings!
<ListView.View>
<GridView>
<GridViewColumn Header="File name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<Image Width="16" Height="16" VerticalAlignment="Center" HorizontalAlignment="Left">
<Image.Source>
<MultiBinding Converter="{StaticResource fic}">
<Binding Path="FileName" />
</MultiBinding >
</Image.Source>
</Image>
<TextBlock Margin="16,0,0,0">
<Binding Path="FileName" />
</TextBlock>
</DockPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
Long boring exception removed
<TextBlock Margin="16,0,0,0">
<TextBlock.Text>
<Binding Path="FileName" />
</TextBlock.Text>
</TextBlock>
... Seems to work!
Things inside <TextBlock> tags are items contained inside the text block, which can be a whole lot of spans and other text fragments.
If you need to bind the text in the text block, you need to bind to the Text property, as you've done in your question. It is a DependencyProperty that supports this binding.
The items inside the text block does not support direct binding, nor does it support directly putting binding objects in there. However, you may put in another control with a DependencyProperty that is a binding though.
You cannot bind the text of a textblock through its children.