0

I need to put my ObservableCollection<ValidationMessage> into my TextBlock. Here is my code. Right now it is showing the Item and the SubItems, but where the messages show it has System.Collections.ObjectModel.ObservableCollection'1[ValidationWPF.DataSources.‌​ValidationMessages].

I think this is because it cannot put an ObservableCollection into the TextBlock.

XAML:

<UserControl x:Class="ValidationWPF.ValidationUserControl"
             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:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
             xmlns:local="clr-namespace:ValidationWPF.DataSources"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <DataTemplate x:Key="Messages">
            <TextBlock Text="{Binding Message}"/>
        </DataTemplate>



    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">

        <telerik:RadTreeView x:Name="radTreeView" Margin="8">
            <telerik:RadTreeView.ItemTemplate>



                <HierarchicalDataTemplate ItemsSource="{Binding SubItems}">
                    <TextBlock Text="{Binding item}" />
                </HierarchicalDataTemplate>

            </telerik:RadTreeView.ItemTemplate>
        </telerik:RadTreeView>

    </Grid>
</UserControl>

ValidationMessage Class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ValidationWPF.DataSources
{
    public class ValidationMessage
    {
        public ValidationMessage(string Message)
        {
            this.Message = Message;
        }

        public string Message
        {
            get;
            set;
        }

    }
}

ValidationItem Class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;

namespace ValidationWPF.DataSources
{
   public class ValidationItem : ObservableCollection<ValidationItem>
    {
        public ValidationItem()
        {
            SubItems = new ObservableCollection<ValidationItem>();


        }


        public ObservableCollection<ValidationMessage> Message
        {
            get;
            set;
        }

        public string item
        {
            get;
            set;
        }

        public IList<ValidationItem> SubItems
        {
            get;
            set;
        }

        public static IList<ValidationItem> GetItems(string name)
        {
            var Validation = new ObservableCollection<ValidationItem>();


            var item = new ValidationItem();
            item.item = "Customer";


            var subItem = new ValidationItem();
            subItem.item = "Name";
            item.SubItems.Add(subItem);

            var Message = new ValidationItem();
            Message.item = new ObservableCollection<ValidationMessage>().ToString();
            subItem.SubItems.Add(Message);






            Validation.Add(item);

            return Validation;

        }
    }
}

Thank you for your help!!

Andy
  • 30,088
  • 6
  • 78
  • 89
JLott
  • 1,818
  • 3
  • 35
  • 56

3 Answers3

3

The problem is that the Text property of the TextBlock is a string, and you're giving it an ObservableCollection. The only way WPF knows to convert the two is by calling ObservableCollection.ToString(), which returns the full type name of the class.

The fix is to convert your ObservableCollection into a string by creating a class that implements System.Windows.Data.IValueConverter. This allows you to control the conversion.

You could implement it something like this:

using System.Globalization;
using System.Text;
using System.Windows.Data;

namespace ValidationWPF.DataSources
{
    class CollectionConverter : IValueConverter
    {
        object Convert(object value, Type targetType,object parameter,CultureInfo culture)
        {
            ObservableCollection<ValidationMessage> messages = (ObservableCollection<ValidationMessage>)value;

            var sb = new StringBuilder();
            foreach(var msg in messages)
            {
                sb.AppendLine(msg.Message);
            }

            return sb.ToString();
        }

        object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return null;
        }
    }
}

And you can use it like this in your XAML file:

<UserControl x:Class="ValidationWPF.ValidationUserControl"
             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:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
             xmlns:local="clr-namespace:ValidationWPF.DataSources"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <local:CollectionConverter x:Key="CollectionConverter" />

        <DataTemplate x:Key="Messages">
            <TextBlock Text="{Binding Message, Converter={StaticResource CollectionConverter}}"/>
        </DataTemplate>
    </UserControl.Resources>
    ...
</UserControl>

Now WPF will call CollectionConverter.Convert() whenever it needs to populate your TextBlock.

Andy
  • 30,088
  • 6
  • 78
  • 89
  • Nice! I need my messages to show in my HierarchicalDataTemplate though... It should be Customers, Name, Cannot Be Null in the order of Parent, Child, GrandParent. – JLott May 24 '12 at 20:12
  • @JLott That depends on the type of item. You could probably do something similar and create a value converter for that as well. – Andy May 24 '12 at 20:55
  • The rest of the items are just type string. I only needed to convert that one. Now I am having trouble getting it to bind to the DataTemplate as shown in my code above. – JLott May 24 '12 at 21:22
  • @JLott I'm not familiar with Telerik's controls, but using the standard controls I'd expect to bind to an ItemsSource property on the main control, and then bind to members of items of that collection in the item template. Maybe you need to set an ItemsSource binding on the main control? Also, check the output window while debugging to see any binding errors. – Andy May 25 '12 at 04:25
2

You are right. The TextBlock tries to view the property value as a String and an ObservableCollection.ToString will return just what you saw.

What you could do is to add a new property that combines all the messages of the ObservableCollection into a single string. Something like this:

public string MessagesCombined  
{
   get { return string.Join(Environment.NewLine, Message.Select(m => m.Message)); } 
}

This will combine all the Messages in your ObservableCollection into a single string, with each item separated by a newline. (You may have to modify my code somewhat, I am writing this without access to a compiler...).

Rune Grimstad
  • 35,612
  • 10
  • 61
  • 76
  • This looks like it could work. How could I then implement that into my hierarchicaldatatemplate to make it show like Customers Name Message (This should go like parent, child, grandchild) – JLott May 24 '12 at 19:41
2

I ended up doing it a different and cleaner way.

XAML:

<UserControl x:Class="ValidationWPF.ValidationUserControl"
             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:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
             xmlns:local="clr-namespace:ValidationWPF.DataSources"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <UserControl.Resources>
        <!--<local:CollectionConverter x:Key="CollectionConverter"/>
            <DataTemplate x:Key="Messages">
            <TextBlock Text="{Binding Message}"/>
        </DataTemplate>--> 




    </UserControl.Resources>
    <Grid x:Name="LayoutRoot" Background="White">

        <telerik:RadTreeView x:Name="radTreeView" Margin="8" ItemsSource="{Binding Errors}">
            <telerik:RadTreeView.ItemTemplate>
                 <HierarchicalDataTemplate ItemsSource="{Binding SubItems}" >
                    <Grid>
                        <Grid.RowDefinitions>
                            <RowDefinition/>
                            <RowDefinition/>
                        </Grid.RowDefinitions>
                        <TextBlock Text="{Binding Description}"/>

                        <ListBox Grid.Row="1" ItemsSource="{Binding Messages}">
                            <ListBox.ItemTemplate>
                                <DataTemplate>
                                    <TextBlock Text="{Binding Message}"/>
                                </DataTemplate>
                            </ListBox.ItemTemplate>
                        </ListBox>
                    </Grid>

                </HierarchicalDataTemplate>

            </telerik:RadTreeView.ItemTemplate>
        </telerik:RadTreeView>

    </Grid>
</UserControl>

VALIDATIONMESSAGE CLASS:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    namespace ValidationWPF.DataSources
    {
        public class ValidationMessage
        {
            public ValidationMessage(string name, string Message)
            {
                this.Message = Message;
                this.PropertyName = name;
            }

            public string Message
            {
                get;
                set;
            }

            public string PropertyName { get; set; }

        }
    }

VALIDATIONVIEWMODEL CLASS:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Collections.ObjectModel;

    namespace ValidationWPF.DataSources
    {
        public class ValidationViewModel
        {
            public ValidationViewModel()
            {
                this.Errors = new ObservableCollection<ValidationItem>();

                ValidationItem item = new ValidationItem();
                item.Description = "Customer";

                ValidationMessage msg = new ValidationMessage("FirstName", "First name is required");
                item.Messages.Add(msg);


                this.Errors.Add(item);

                ValidationItem item2 = new ValidationItem();
                item2.Description = "Order";

                msg = new ValidationMessage("Quantity", "Quantity must be greater than zero");
                item2.Messages.Add(msg);


                item.SubItems.Add(item2);

            }

            public ObservableCollection<ValidationItem> Errors { get; set; }
        }
    }

VALIDATIONUSERCONTROL CLASS:

  public partial class ValidationUserControl : UserControl
    {
        public ValidationUserControl()
        {
            InitializeComponent();
            this.DataContext = new ValidationViewModel();

        }

    }
JLott
  • 1,818
  • 3
  • 35
  • 56