10 Answers, 1 is accepted
We couldn't reproduce the problem. May I ask you to send us a sample project that will allow us to reproduce the exception.
Thank you for your time.
Regards,
Milan
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.
<Window x:Class="carrouselSample.Window1" |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
xmlns:carousel="clr-namespace:Telerik.Windows.Controls.Carousel;assembly=Telerik.Windows.Controls.Navigation" |
Title="Window1" Height="350" Width="535" |
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"> |
<Grid> |
<Grid.Resources> |
<carousel:ArithmeticValueConverter x:Key="ArithmeticValueConverter" /> |
<Style TargetType="{x:Type telerik:CarouselItem}"> |
<Setter Property="Template"> |
<Setter.Value> |
<ControlTemplate TargetType="{x:Type telerik:CarouselItem}"> |
<Grid ClipToBounds="False" Height="150" Width="200"> |
<Border RenderTransformOrigin="0.5, 1" ClipToBounds="False" |
Width="{Binding ElementName=CarouselItemInnerGrid, Path=ActualWidth}" |
Opacity="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ReflectionSettings.Opacity}" |
Visibility="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ReflectionSettings.Visibility}" |
BorderBrush="White" BorderThickness="0"> |
<Border.RenderTransform> |
<TransformGroup> |
<ScaleTransform ScaleX="{Binding RelativeSource={RelativeSource TemplatedParent}, |
Path=ReflectionSettings.WidthOffset, |
Converter={StaticResource ArithmeticValueConverter}, |
ConverterParameter=1}" ScaleY="{Binding RelativeSource={RelativeSource TemplatedParent}, |
Path=ReflectionSettings.HeightOffset, |
Converter={StaticResource ArithmeticValueConverter}, |
ConverterParameter=-1}" |
CenterY="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ReflectionSettings.OffsetY}" /> |
<TranslateTransform |
X="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ReflectionSettings.OffsetX}" /> |
<SkewTransform |
AngleX="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ReflectionSettings.Angle}" /> |
</TransformGroup> |
</Border.RenderTransform> |
<Border.OpacityMask> |
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1"> |
<LinearGradientBrush.GradientStops> |
<GradientStop |
Offset="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ReflectionSettings.HiddenPercentage}" |
Color="Transparent" /> |
<GradientStop Offset="1" Color="Black" /> |
</LinearGradientBrush.GradientStops> |
</LinearGradientBrush> |
</Border.OpacityMask> |
<Border.Background> |
<VisualBrush |
Visual="{Binding ElementName=CarouselItemInnerGrid}"> |
</VisualBrush> |
</Border.Background> |
</Border> |
<Grid x:Name="CarouselItemInnerGrid"> |
<Border x:Name="CarouselItemMainBorder" Opacity="0.5" |
BorderBrush="#FF91B3FF" BorderThickness="1,1,1,1" |
CornerRadius="5,5,5,5" SnapsToDevicePixels="True"> |
<Border.Background> |
<LinearGradientBrush EndPoint="108,472" |
StartPoint="108,23" MappingMode="Absolute"> |
<GradientStop Color="#FF2C3A68" Offset="0" /> |
<GradientStop Color="#FF000000" Offset="1" /> |
<GradientStop Color="#FF0F224C" Offset="0.045" /> |
<GradientStop Color="#FF000000" Offset="0.0451" /> |
</LinearGradientBrush> |
</Border.Background> |
</Border> |
<Border Opacity="1" BorderBrush="#3F000000" |
BorderThickness="1,1,1,1" CornerRadius="5,5,5,5" |
Margin="10,10,10,10" x:Name="CarouselItemInnerBorder" |
SnapsToDevicePixels="True"> |
<Border.Background> |
<LinearGradientBrush EndPoint="101,462" |
StartPoint="101,13" MappingMode="Absolute"> |
<GradientStop Color="#FF2C3A68" Offset="0" /> |
<GradientStop Color="#FF000000" Offset="1" /> |
<GradientStop Color="#FF0F224C" Offset="0.045" /> |
<GradientStop Color="#FF000000" Offset="0.0451" /> |
</LinearGradientBrush> |
</Border.Background> |
<ContentPresenter IsHitTestVisible="True" /> |
<Border.Triggers> |
</Border.Triggers> |
</Border> |
</Grid> |
</Grid> |
<ControlTemplate.Triggers> |
<MultiTrigger> |
<MultiTrigger.Conditions> |
<Condition Property="IsSelected" Value="False" /> |
<Condition Property="IsMouseOver" Value="True" /> |
</MultiTrigger.Conditions> |
<MultiTrigger.EnterActions> |
<BeginStoryboard> |
<Storyboard> |
<ColorAnimation |
Storyboard.TargetName="CarouselItemMainBorder" |
Storyboard.TargetProperty="Background.GradientStops[0].Color" |
To="#FF344B97" Duration="0:0:0.3" /> |
<ColorAnimation |
Storyboard.TargetName="CarouselItemMainBorder" |
Storyboard.TargetProperty="Background.GradientStops[2].Color" |
To="#FF233F7E" Duration="0:0:0.3" /> |
<ColorAnimation |
Storyboard.TargetName="CarouselItemInnerBorder" |
Storyboard.TargetProperty="Background.GradientStops[0].Color" |
To="#FF344B97" Duration="0:0:0.3" /> |
<ColorAnimation |
Storyboard.TargetName="CarouselItemInnerBorder" |
Storyboard.TargetProperty="Background.GradientStops[2].Color" |
To="#FF233F7E" Duration="0:0:0.3" /> |
</Storyboard> |
</BeginStoryboard> |
</MultiTrigger.EnterActions> |
<MultiTrigger.ExitActions> |
<BeginStoryboard> |
<Storyboard FillBehavior="Stop"> |
<ColorAnimation |
Storyboard.TargetName="CarouselItemMainBorder" |
Storyboard.TargetProperty="Background.GradientStops[0].Color" |
To="#FF2C3A68" Duration="0:0:0.3" /> |
<ColorAnimation |
Storyboard.TargetName="CarouselItemMainBorder" |
Storyboard.TargetProperty="Background.GradientStops[2].Color" |
To="#FF0F224C" Duration="0:0:0.3" /> |
<ColorAnimation |
Storyboard.TargetName="CarouselItemInnerBorder" |
Storyboard.TargetProperty="Background.GradientStops[0].Color" |
To="#FF2C3A68" Duration="0:0:0.3" /> |
<ColorAnimation |
Storyboard.TargetName="CarouselItemInnerBorder" |
Storyboard.TargetProperty="Background.GradientStops[2].Color" |
To="#FF0F224C" Duration="0:0:0.3" /> |
</Storyboard> |
</BeginStoryboard> |
</MultiTrigger.ExitActions> |
</MultiTrigger> |
<Trigger Property="IsSelected" Value="True"> |
<Setter TargetName="CarouselItemMainBorder" |
Property="Background"> |
<Setter.Value> |
<LinearGradientBrush EndPoint="108,472" |
StartPoint="108,23" MappingMode="Absolute"> |
<GradientStop Color="#FF344B97" Offset="0" /> |
<GradientStop Color="#FF000000" Offset="1" /> |
<GradientStop Color="#FF233F7E" Offset="0.045" /> |
<GradientStop Color="#FF000000" Offset="0.0451" /> |
</LinearGradientBrush> |
</Setter.Value> |
</Setter> |
<Setter TargetName="CarouselItemInnerBorder" |
Property="Background"> |
<Setter.Value> |
<LinearGradientBrush EndPoint="101,462" |
StartPoint="101,13" MappingMode="Absolute"> |
<GradientStop Color="#FF344B97" Offset="0" /> |
<GradientStop Color="#FF000000" Offset="1" /> |
<GradientStop Color="#FF233F7E" Offset="0.045" /> |
<GradientStop Color="#FF000000" Offset="0.0451" /> |
</LinearGradientBrush> |
</Setter.Value> |
</Setter> |
</Trigger> |
</ControlTemplate.Triggers> |
</ControlTemplate> |
</Setter.Value> |
</Setter> |
</Style> |
<Style TargetType="{x:Type telerik:CarouselDataRecordPresenter}"> |
<Setter Property="Template"> |
<Setter.Value> |
<ControlTemplate TargetType="{x:Type telerik:CarouselDataRecordPresenter}"> |
<Grid IsHitTestVisible="False" HorizontalAlignment="Stretch" |
VerticalAlignment="Stretch"> |
<Grid.Background> |
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> |
<GradientStop Color="White" Offset="0"/> |
<GradientStop Color="Silver" Offset="1"/> |
<GradientStop Color="WhiteSmoke" Offset="0.43"/> |
<GradientStop Color="WhiteSmoke" Offset="0.42"/> |
</LinearGradientBrush> |
</Grid.Background> |
<Grid.RowDefinitions> |
<RowDefinition Height="Auto" /> |
<RowDefinition Height="Auto" /> |
</Grid.RowDefinitions> |
<StackPanel Grid.Row="0" Orientation="Vertical" Margin="5,5,0,0" |
VerticalAlignment="Top"> |
<TextBlock Text="{Binding Path=FullName}" Height="Auto" |
Foreground="Black" FontSize="12" /> |
<TextBlock Text="{Binding Path=FirstName}" Height="Auto" |
Foreground="DarkBlue" FontSize="10" /> |
<TextBlock Text="{Binding Path=LastName}" Height="Auto" |
TextTrimming="WordEllipsis" |
Foreground="DarkGreen" FontSize="10" /> |
</StackPanel> |
</Grid> |
</ControlTemplate> |
</Setter.Value> |
</Setter> |
</Style> |
</Grid.Resources> |
<telerik:RadCarousel Margin="10,10,0,41" Name="radCarousel1" |
ItemsSource="{Binding UserList}"> |
</telerik:RadCarousel> |
</Grid> |
</Window> |
using System; |
using System.Collections.Generic; |
using System.Linq; |
using System.Text; |
using System.Windows; |
using System.Windows.Controls; |
using System.Windows.Data; |
using System.Windows.Documents; |
using System.Windows.Input; |
using System.Windows.Media; |
using System.Windows.Media.Imaging; |
using System.Windows.Navigation; |
using System.Windows.Shapes; |
using carrouselSample.Code; |
namespace carrouselSample |
{ |
/// <summary> |
/// Interaction logic for Window1.xaml |
/// </summary> |
public partial class Window1 : Window |
{ |
//this is just for demonstrating purposes |
WindowVM _VM; |
public Window1() |
{ |
InitializeComponent(); |
Loaded += new RoutedEventHandler(Window1_Loaded); |
//this is just for demonstrating purposes |
_VM = new WindowVM(); |
} |
void Window1_Loaded(object sender, RoutedEventArgs e) |
{ |
//this is just for demonstrating purposes |
this.DataContext = _VM; |
} |
} |
} |
using System; |
using System.ComponentModel; |
using System.Windows.Threading; |
namespace carrouselSample.Code |
{ |
public class WindowVM: INotifyPropertyChanged |
{ |
DispatcherTimer _AddUsersDispatcher; |
/// <summary> |
/// Add users async to simulate real world situation |
/// </summary> |
public WindowVM() |
{ |
_AddUsersDispatcher = new DispatcherTimer(); |
_AddUsersDispatcher.Interval = new TimeSpan(0, 0, 0, 0, 300); |
_AddUsersDispatcher.Tick += new EventHandler(_AddUsersDispatcher_Tick); |
_AddUsersDispatcher.Start(); |
_UserList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_UserList_CollectionChanged); |
} |
/// <summary> |
/// Add users |
/// </summary> |
void _AddUsersDispatcher_Tick(object sender, EventArgs e) |
{ |
if (_UserList.Count < 30) |
{ |
User u = new User() { FirstName = string.Format("John{0}", _UserList.Count), LastName = string.Format("{0}Smith", _UserList.Count), Other = DateTime.Now.ToString() }; |
_UserList.Add(u); |
} |
else |
{ |
//stop the timer |
_AddUsersDispatcher.Stop(); |
} |
} |
/// <summary> |
/// The user list |
/// </summary> |
private ObservableSortableCollection<User> _UserList = new ObservableSortableCollection<User>(); |
public ObservableSortableCollection<User> UserList |
{ |
get |
{ |
return _UserList; |
} |
} |
void _UserList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) |
{ |
UserList.SortBy(c => c.FullName, ListSortDirection.Descending); |
} |
#region INotifyPropertyChanged Members |
public event PropertyChangedEventHandler PropertyChanged; |
public void OnPropertyChanged(string property) |
{ |
if (PropertyChanged != null) |
{ |
PropertyChanged(this, new PropertyChangedEventArgs(property)); |
} |
} |
#endregion |
} |
} |
using System; |
using System.Collections.Generic; |
using System.Linq; |
using System.Text; |
namespace carrouselSample.Code |
{ |
public class User |
{ |
public string FirstName |
{ |
get; |
set; |
} |
public string LastName |
{ |
get; |
set; |
} |
public string Other |
{ |
get; |
set; |
} |
public string FullName |
{ |
get |
{ |
return string.Format("{0}, {1}",LastName,FirstName); |
} |
} |
} |
} |
using System; |
using System.Collections.Generic; |
using System.Linq; |
using System.Text; |
using System.Collections.ObjectModel; |
namespace carrouselSample.Code |
{ |
// Summary: |
// Specifies the direction of a sort operation. |
public enum ListSortDirection |
{ |
Ascending = 0, |
Descending = 1, |
} |
/// <summary> |
/// Allows to create an observable + sortable collection |
/// </summary> |
/// <typeparam name="T">Type of colection</typeparam> |
public class ObservableSortableCollection<T> : ObservableCollection<T> |
{ |
List<T> _OriginalList; |
/// <summary> |
/// constructor |
/// </summary> |
public ObservableSortableCollection() |
: base() |
{ |
} |
/// <summary> |
/// Sort the observable collection |
/// </summary> |
/// <param name="keySelector">function used to sort</param> |
/// <param name="direction">sort direction</param> |
public void SortBy<TKey>(Func<T, TKey> keySelector, ListSortDirection direction) |
{ |
switch (direction) |
{ |
case ListSortDirection.Ascending: |
SortCollection(Items.OrderBy(keySelector).ToList()); |
break; |
default: |
SortCollection(Items.OrderByDescending(keySelector).ToList()); |
break; |
} |
} |
/// <summary> |
/// Sorts the collection |
/// </summary> |
/// <param name="sortedItems">sorted list</param> |
private void SortCollection(IList<T> sortedItems) |
{ |
//first clear the list, then add the items ordered |
Items.Clear(); |
foreach (var item in sortedItems) |
{ |
Items.Add(item); |
} |
} |
} |
} |
Thank you for sharing your code.
It turned out that the exception is caused by an incorrect behavior of the ObservableSortableCollection and WindowVM. Whever the UserList is changed it raises a CollectionChanged event which notifies all listeners like RadCarousel. Unfortunately before this event reaches RadCarousel the items of the ObservableSortableCollection are already sorted (with changed places) and as a result RadCarousel receives CollectionChanged event with wrong indices.
Probably the best approach is to use a ListCollectionView which has built-in sorting functionality. Moreover by using ListCollectionView you will not have to implement custom collections. The switch to ListCollectionView requires just a few changes.:
public
class
WindowVM : INotifyPropertyChanged
{
DispatcherTimer _AddUsersDispatcher;
ObservableCollection<User> data =
new
ObservableCollection<User>();
/// <summary>
/// The user list
/// </summary>
private
ListCollectionView _UserList;
public
ListCollectionView UserList
{
get
{
return
_UserList;
}
private
set
{
this
._UserList = value;
}
}
/// <summary>
/// Add users async to simulate real world situation
/// </summary>
public
WindowVM()
{
// create sorted ListCollectionView with empty data
this
.UserList =
new
ListCollectionView(
this
.data);
this
.UserList.SortDescriptions.Add(
new
SortDescription(
"FullName"
, System.ComponentModel.ListSortDirection.Descending));
_AddUsersDispatcher =
new
DispatcherTimer();
_AddUsersDispatcher.Interval =
new
TimeSpan(0, 0, 0, 0, 300);
_AddUsersDispatcher.Tick +=
new
EventHandler(_AddUsersDispatcher_Tick);
_AddUsersDispatcher.Start();
}
/// <summary>
/// Add users
/// </summary>
void
_AddUsersDispatcher_Tick(
object
sender, EventArgs e)
{
if
(
this
.data.Count < 30)
{
User u =
new
User() { FirstName =
string
.Format(
"John{0}"
, _UserList.Count), LastName =
string
.Format(
"{0}Smith"
, _Us
this
.data.Add(u);
}
else
{
//stop the timer
_AddUsersDispatcher.Stop();
}
}
Hope this helps.
All the best,
Milan
the Telerik team
Do you want to have your say when we set our development plans? Do you want to know when a feature you care about is added or when a bug fixed? Explore the Telerik Public Issue Tracking system and vote to affect the priority of the items.