none
Why the use of ReadOnlyCollection in this Convertion method RRS feed

  • Question

  • Hi there:

    I'm studying Data Validation in Bindings, and I ran accross this Validation method:

    public class ErrorsToMessageConverter : IValueConverter {
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
            var sb = new StringBuilder();
            var errors = value as ReadOnlyCollection<ValidationError>;
            if (errors != null) {
                foreach (var e in errors.Where(e => e.ErrorContent != null)) {
                    sb.AppendLine(e.ErrorContent.ToString());
                }
            }
    
            return sb.ToString();
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
            throw new NotImplementedException();
        }
    }


    This is the xaml that uses it:

    <TextBlock Margin="2" Foreground="Red" FontWeight="Bold" 
            Text="{Binding ElementName=AddressBox, 
                            Path=(Validation.Errors),
                            Converter={StaticResource ErrorsToMessageConverter}}" />


    It's clear that we are obtaining a collection of errors (Validation.Errors), but why do we use
    ReadOnlyCollection<ValidationError> instead of just Collection or Array?, I tried changing it, and the app ceased to work!

    Thanks in advance,

    Rafael


    Believe you can do it, and you will!!

    Wednesday, November 20, 2013 9:12 PM

Answers

  • The type of the Validation.Errors attached property is System.Collections.ObjectModel.ReadOnlyObservableCollection<ValidationError>. A ValidationError object is added to the Validation.Errors collection of the bound element by the binding engine at runtime if the validation fails, but the application cannot modify the content of this collection.

    You could cast the value as an ICollection<ValidationError> though, as the ReadOnlyObservableCollection<T> class inherits from the ReadOnlyCollection<T> class which implements the ICollection<T> and several other interfaces: http://msdn.microsoft.com/en-us/library/ms132474(v=vs.110).aspx

    For more information about the data validation procedure in WPF, please refer to my blog post: http://blog.magnusmontin.net/2013/08/26/data-validation-in-wpf/

    • Marked as answer by RalphDev Thursday, November 21, 2013 1:55 AM
    Wednesday, November 20, 2013 10:14 PM
  • You can't be more explicit than specifying the name of the property of the source object to bind to, see the "Single Indexer on the Immediate Object as Data Context" section at the following MSDN link for more information about the PropertyPath XAML Syntax for arrays or collections: http://msdn.microsoft.com/en-us/library/ms742451(v=vs.110).aspx

    • Marked as answer by RalphDev Sunday, November 24, 2013 3:27 AM
    Saturday, November 23, 2013 9:22 AM

All replies

  • readonlycollection is generally used when someone doesn't want you to modify the underlying collection that it wraps.  in this case I guess it is used to stop any errors that are not thrown by the data object that is being validated from being added into the collection. its easy enough to modify if you really need to by creating new collection from the readonly one and doing what you want there.

    andy

    Wednesday, November 20, 2013 9:49 PM
  • The type of the Validation.Errors attached property is System.Collections.ObjectModel.ReadOnlyObservableCollection<ValidationError>. A ValidationError object is added to the Validation.Errors collection of the bound element by the binding engine at runtime if the validation fails, but the application cannot modify the content of this collection.

    You could cast the value as an ICollection<ValidationError> though, as the ReadOnlyObservableCollection<T> class inherits from the ReadOnlyCollection<T> class which implements the ICollection<T> and several other interfaces: http://msdn.microsoft.com/en-us/library/ms132474(v=vs.110).aspx

    For more information about the data validation procedure in WPF, please refer to my blog post: http://blog.magnusmontin.net/2013/08/26/data-validation-in-wpf/

    • Marked as answer by RalphDev Thursday, November 21, 2013 1:55 AM
    Wednesday, November 20, 2013 10:14 PM
  • Hi Magnus:

    I was reading your blog on Data Validation, and it's very interesting.

    But I have a question.

    In the ErrorTemplate section you coded:

    <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>

    It works, I tried in my own wpf app. But what I don't understand is the use of an indexer without a collection identifier <TextBlock Text="{Binding [0].ErrorContent}" ..., what collection is [0] referring to?, I tried (Validation.Errors)[0]... but it throws an exception since the converter receives null.

    Could you please enlighten me on this?

    Rafael


    Believe you can do it, and you will!!

    Thursday, November 21, 2013 12:21 PM
  • The DataContext of the TextBlock element in the Validation.ErrorTemplate is a ReadOnlyObservableCollection of ValidationError objects and the indexer [0] simply refers to the first item in this collection.

    When you write (Validation.Errors) at the beginning of the path, you are referring to an attached property of the DataContext. You would for example use this syntax when binding to an attached property of the TextBox in a style of the TextBox:

            <TextBox x:Name="txt" Text="{Binding Age, ValidatesOnDataErrors=True, 
                 UpdateSourceTrigger=PropertyChanged}">
                 <TextBox.Style>
                     <Style TargetType="TextBox">
                     <Setter Property="ToolTip" 
                             Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                             Path=(Validation.Errors)[0].ErrorContent}"/>
                     </Style>
                 </TextBox.Style>
             </TextBox>

    In this case, the TextBox itself doesn't have any collection of ValidationError objects, but it has an attached property (Validation.Errors) containing the ValidationError objects.

    The ReadOnlyObservableCollection that is acting as the DataContext of a Validation.ErrorTemplate does not have an attached property called Validation.Errors - it's just an ordinary Collection of objects -  but the TextBox does.


    Thursday, November 21, 2013 1:02 PM
  • Hi Magnus:

    The code you sent me works too. But I'd like to know why the previous one also worked.

    The benefit of the other is that the error gets visible inmediately, and not in a ToolTip, I want to use both.

    Instead of <TextBlock Text="{Binding [0].ErrorContent}" Foreground="Red"/>, why can't I write <TextBlock Text="{Binding (Validation.Errors)[0].ErrorContent}" Foreground="Red"/>, I get a null exception if I do, since the input parameter 'value' in the converter is null.

    It even throws an error, but works still:

    It's all a mystery to me!

    Rafael


    Believe you can do it, and you will!!






    • Edited by RalphDev Friday, November 22, 2013 12:07 PM
    Friday, November 22, 2013 11:57 AM
  • Because the DataContext of the ControlTemplate (the Validation.ErrorTemplate), i.e. the ReadOnlyObservableCollection<ValidationError>, doesn't have any attached property named Validation.Errors while the TextBoes does.

    When you specify a RelativeSource of Self for the binding, you are using the TextBox control itself as the DataContext and this one has an attached property named Validation.Errors.

    ReadOnlyObservableCollection<T> does not have a Validation.Errors attached property.
    TextBox does have a Validation.Errors attached property.


    Friday, November 22, 2013 12:10 PM
  • Hi:

    Ok, but for the Validation.ErrorTemplate case, what if I want to replace "{Binding [0].ErrorContent}" for something more explicit, "{Binding (Validation.Errors)[0].ErrorContent}" doesn't work, what does?

    By the way, I'm using your blog on Validation in WPF and it's excelent.


    Believe you can do it, and you will!!



    • Edited by RalphDev Friday, November 22, 2013 6:11 PM
    Friday, November 22, 2013 6:04 PM
  • You can't be more explicit than specifying the name of the property of the source object to bind to, see the "Single Indexer on the Immediate Object as Data Context" section at the following MSDN link for more information about the PropertyPath XAML Syntax for arrays or collections: http://msdn.microsoft.com/en-us/library/ms742451(v=vs.110).aspx

    • Marked as answer by RalphDev Sunday, November 24, 2013 3:27 AM
    Saturday, November 23, 2013 9:22 AM
  • Thank you again!

    Believe you can do it, and you will!!

    Sunday, November 24, 2013 3:27 AM