Unable to get external controls (Telerik) working

Topics: Issues / bugs
Jul 10, 2012 at 9:14 PM

Hello,

I have followed the documentation, searched online and I still can't get this to work. I get an error: Cannot attach type \"UserControlBehavior\" to type \"CrgGridControl\". Instances of type \"UserControlBehavior\" can only be attached to objects of type \"UserControl\. I have marked the line where the error occurs.

I can not find a working example of an external control anywhere and not much help from google search either.

Any input would be very helpful. Thank you.

The control:

public class CrgGridControl : RadGridViewIViewModelContainer
    {
        //--------------------------------------------------------------------------------
        // [fields]
        //--------------------------------------------------------------------------------
 
        private readonly UserControlBehavior _mvvmBehavior;
 
 
        //--------------------------------------------------------------------------------
        // [constructors]
        //--------------------------------------------------------------------------------
 
        public CrgGridControl()
        {
            _mvvmBehavior = new UserControlBehavior();
            _mvvmBehavior.ViewModelType = typeof(CrgGridViewModel);
 
// THIS IS WHERE I GET THE ERROR             System.Windows.Interactivity.Interaction.GetBehaviors(this).Add(_mvvmBehavior);             _mvvmBehavior.ViewModelChanged += (sender, e) => ViewModelChanged.SafeInvoke(this, e);             _mvvmBehavior.ViewModelPropertyChanged += (sender, e) => ViewModelPropertyChanged.SafeInvoke(this, e);             SetBinding(RadGridView.ItemsSourceProperty, new Binding("TranscriptItems"));         }         //--------------------------------------------------------------------------------         // [properties]         //--------------------------------------------------------------------------------                  public IViewModel ViewModel         {             get { return _mvvmBehavior.ViewModel; }         }         //--------------------------------------------------------------------------------         // [event-handlers]         //--------------------------------------------------------------------------------         public new event EventHandler<PropertyChangedEventArgs> PropertyChanged;         public event EventHandler<EventArgs> ViewModelChanged;         public event EventHandler<PropertyChangedEventArgs> ViewModelPropertyChanged;         //--------------------------------------------------------------------------------         // [methods]         //--------------------------------------------------------------------------------         protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)         {             base.OnPropertyChanged(e);             PropertyChanged.SafeInvoke(thisnew PropertyChangedEventArgs(e.Property.Name));         }
 }


The ViewModel:

 public class CrgGridViewModel : ViewModelBase
    {
        //--------------------------------------------------------------------------------
        // [properties]
        //--------------------------------------------------------------------------------
 
        public override string Title { get { return "Transcript ViewModel title"; } }
 
        public ObservableCollection<TranscriptItem> TranscriptItems
        {
            get { return GetValue<ObservableCollection<TranscriptItem>>(TranscriptItemsProperty); }
            set { SetValue(TranscriptItemsProperty, value); }
        }
        public static readonly PropertyData TranscriptItemsProperty = RegisterProperty("TranscriptItems"typeof(ObservableCollection<TranscriptItem>));
 
        public Command<string> ChangeRatingCommand { getprivate set; }
        public Command FilterOperatorsLoadingCommand { getprivate set; }
 
 
        //--------------------------------------------------------------------------------
        // [constructors]
        //--------------------------------------------------------------------------------
 
        public CrgGridViewModel() : base()
        {
            ChangeRatingCommand = new Command<string>(OnChangeRatingCommandExecute);
            FilterOperatorsLoadingCommand = new Command(OnFilterOperatorsLoadingCommandExecute);
 
            TranscriptItems = new ObservableCollection<TranscriptItem>()
            {
                new TranscriptItem() { Id = 1, Role = RoleType.Moderator, Name = "Sarah", MessageText = "Hello Hola Adriana", IsDeleted = false },
                new TranscriptItem() { Id = 2, Role = RoleType.Guest, Name = "Adriana", MessageText = "Hola Sarah",  IsDeleted = true},
                new TranscriptItem() { Id = 3, Role = RoleType.Moderator, Name = "Sarah", MessageText = "Hola What do you think about this flavor? What do you think about this flavor?What do you think about this flavor?What do you think about this flavor?What do you think about this flavor?What do you think about this flavor?", IsDeleted = false },
                new TranscriptItem() { Id = 4, Role = RoleType.Guest, Name = "Adriana", MessageText = "It's ok. It smells like jasmine.", IsDeleted = false }
            };
        }
 
 
        //--------------------------------------------------------------------------------
        // [methods]
        //--------------------------------------------------------------------------------
 
        private void OnChangeRatingCommandExecute(string rating)
        {
 
        }
 
        private void OnFilterOperatorsLoadingCommandExecute()
        {
 
        }
    }

Inside a view I am using the control like this:
<LocalControls:CrgGridControl CanUserFreezeColumns="False"
                             RowIndicatorVisibility="Collapsed"
                             IsReadOnly="False"                             
                             MinHeight="386" MaxHeight="500" Width="700"
                             FilteringMode="FilterRow"
                             AutoGenerateColumns="True"
                             AutoExpandGroups="True"
                             telerikGridViewHeaderMenu:GridViewHeaderMenu.IsEnabled="true">


Coordinator
Jul 11, 2012 at 10:08 AM

The UserControlLogic is currently forced to have a UserControl as base class. You might want to wrap the grid in a user control, then you can get it to work. We will investigate whether we can change it to FrameworkElement.

Jul 11, 2012 at 1:48 PM

Thank you for your quick reply.

I have the grid working using the other approach.

Not sure, if I should open another discussion or if you can guide me in the right direction here. Taking the approach you have suggested, how do I accomplish the following:

1. instead of using event handlers in the code behind of the xaml file, handle that in the view model. I will show in code what I am attempting to do.

2. what is the correct way of doing this if my attempt is not correct? I am sure there is a way to do it, having in mind we are using an mvvm pattern. I don't want any code in the xaml.cs file. I want the viewModel to handle it all.

Thank you again. Catel is awesome.

Working version

TranscriptView.xaml

<catel:UserControl x:Class="WPFCatelTelerik.Views.TranscriptView"
                   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
		   xmlns:catel="http://catel.codeplex.com"
                   xmlns:telerikGridViewHeaderMenu="clr-namespace:Telerik.Windows.Controls.GridView.HeaderMenu" 
                   xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                   xmlns:local="clr-namespace:WPFCatelTelerik.Converters"
                   xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
                   xmlns:ViewModels="clr-namespace:WPFCatelTelerik.ViewModels">
 
    <!-- Resources -->
    <catel:UserControl.Resources>
        <catel:BooleanToCollapsingVisibilityConverter x:Key="BooleanToCollapsingVisibilityConverter" />
    </catel:UserControl.Resources>

<telerik:RadGridView x:Name="radGridView" CanUserFreezeColumns="False"
                             RowIndicatorVisibility="Collapsed"
                             ItemsSource="{Binding TranscriptItems}"
                             IsReadOnly="False"                             
                             MinHeight="386" MaxHeight="500" Width="700"
                             FilteringMode="FilterRow"
                             AutoGenerateColumns="True"
                             AutoExpandGroups="True"
                             telerikGridViewHeaderMenu:GridViewHeaderMenu.IsEnabled="true"
                             FilterOperatorsLoading="OnFilterOperatorsLoading"/> 
</catel:UserControl>

TranscriptView.xaml.cs
public partial class TranscriptView : UserControl
    {
        public TranscriptView()
        {
            InitializeComponent();            
        }
 
 
        // default FilterRow
        private void OnFilterOperatorsLoading(object sender, FilterOperatorsLoadingEventArgs e)
        {
            Type columnType = ((GridViewBoundColumnBase)e.Column).DataType;
 
            if (columnType == typeof(string))
            {
                e.DefaultOperator1 = Telerik.Windows.Data.FilterOperator.Contains;
                e.DefaultOperator2 = Telerik.Windows.Data.FilterOperator.Contains;
            }
            else
            {
                e.DefaultOperator1 = Telerik.Windows.Data.FilterOperator.IsEqualTo;
                e.DefaultOperator2 = Telerik.Windows.Data.FilterOperator.IsEqualTo;
            }
        }
}


What I want:
<telerik:RadGridView x:Name="radGridView" CanUserFreezeColumns="False"
                             RowIndicatorVisibility="Collapsed"
                             ItemsSource="{Binding TranscriptItems}"
                             IsReadOnly="False"                             
                             MinHeight="386" MaxHeight="500" Width="700"
                             FilteringMode="FilterRow"
                             AutoGenerateColumns="True"
                             AutoExpandGroups="True"
                             telerikGridViewHeaderMenu:GridViewHeaderMenu.IsEnabled="true"/> 

<!-- not sure if this is the correct approach. If it is how to pass commandParameters and how to intercept it in the view model?
If it isn't how do I do this? -->             <i:Interaction.Triggers>                 <i:EventTrigger EventName="FilterOperatorsLoading">                     <i:InvokeCommandAction Command="{Binding FilterOperatorsLoadingCommand}" />                     <catel:EventToCommand Command="{Binding FilterOperatorsLoadingCommand}" DisableAssociatedObjectOnCannotExecute="False" />                 </i:EventTrigger>             </i:Interaction.Triggers>
</telerik:RadGridView>

TranscriptViewModel.cs

public class TranscriptViewModel : ViewModelBase
    {
        //--------------------------------------------------------------------------------
        // [properties]
        //--------------------------------------------------------------------------------
 
        public override string Title { get { return "Transcript ViewModel title"; } }
 
        public ObservableCollection<TranscriptItem> TranscriptItems
        {
            get { return GetValue<ObservableCollection<TranscriptItem>>(TranscriptItemsProperty); }
            set { SetValue(TranscriptItemsProperty, value); }
        }
        public static readonly PropertyData TranscriptItemsProperty = RegisterProperty("TranscriptItems"typeof(ObservableCollection<TranscriptItem>));
 
        public Command<string> ChangeRatingCommand { getprivate set; }
        public Command FilterOperatorsLoadingCommand { getprivate set; }
 
 
        //--------------------------------------------------------------------------------
        // [constructors]
        //--------------------------------------------------------------------------------
 
        public TranscriptViewModel() : base()
        {
            ChangeRatingCommand = new Command<string>(OnChangeRatingCommandExecute);
            FilterOperatorsLoadingCommand = new Command(OnFilterOperatorsLoadingCommandExecute);
 
            TranscriptItems = new ObservableCollection<TranscriptItem>()
            {
                new TranscriptItem() { Id = 1, Role = RoleType.Moderator, Name = "Sarah", MessageText = "Hello Hola Adriana", IsDeleted = false },
                new TranscriptItem() { Id = 2, Role = RoleType.Guest, Name = "Adriana", MessageText = "Hola Sarah",  IsDeleted = true},
                new TranscriptItem() { Id = 3, Role = RoleType.Moderator, Name = "Sarah", MessageText = "Hola What do you think about this flavor? What do you think about this flavor?What do you think about this flavor?What do you think about this flavor?What do you think about this flavor?What do you think about this flavor?", IsDeleted = false },
                new TranscriptItem() { Id = 4, Role = RoleType.Guest, Name = "Adriana", MessageText = "It's ok. It smells like jasmine.", IsDeleted = false }
            };
        }
 
 
        //--------------------------------------------------------------------------------
        // [methods]
        //--------------------------------------------------------------------------------
 
        private void OnChangeRatingCommandExecute(string rating)
        {
        }
 
        private void OnFilterOperatorsLoadingCommandExecute()
        {
            // ? 1. I can't get this to fire. 
// 2. Missing parameters. How to intercept them here?
// It should have FilterOperatorsLoadingEventArgs e in the signature. But when I try it it does not work.

         }     }

Coordinator
Jul 11, 2012 at 2:16 PM

1) Change the type of the event handlers in xaml to EventToCommand which ships with Catel. See this documentation.

2) You can specify the parameter type of a command. For example, declare your command as Command<FilterOperatorsLoadingEventArgs> and you can use the parameter.

Jul 11, 2012 at 2:40 PM

It works. Thanks.

For others that might have the same issues:

use

xmlns:commands="clr-namespace:Catel.Windows.Interactivity;assembly=Catel.MVVM"

<i:Interaction.Triggers>
                <i:EventTrigger EventName="SelectionChanged">
                    <commands:EventToCommand Command="{Binding SelectionChangedCommand}" DisableAssociatedObjectOnCannotExecute="False" PassEventArgsToCommand="True" />
                </i:EventTrigger>
            </i:Interaction.Triggers>

in the view model:

public Command<SelectionChangeEventArgs> SelectionChangedCommand { getprivate set; }