Hi,
I use the RadGridView with a VQCV and dynamic typed data in it. This works fine so far. My problem is, that I want to select the first row after the first set of data was loaded. I get the ItemsLoaded event and set the a SelectedItem property in my ViewModel. This property is bound to the RadGridView but it doesn't render the selected item correct (yellow selection). If I selected rows using the mouse everything works fine, only the programatic selection doesn't work.
I have debugged the issue for a while and wonder that I can see, that the RadGridView has the correct object referenced by the SelectedItem property, but visually the row is not selected.
public class MainWindowViewModel : INotifyPropertyChanged { private DynamicDataRow selectedItem; public MainWindowViewModel() { this.Data = new VirtualQueryableCollectionView { LoadSize = 10, VirtualItemCount = 1000 }; this.Data.ItemsLoading += this.ViewItemsLoading; this.Data.ItemsLoaded += this.ViewItemsLoaded; } public event PropertyChangedEventHandler PropertyChanged; public VirtualQueryableCollectionView Data { get; private set; } public DynamicDataRow SelectedItem { get { return this.selectedItem; } set { this.selectedItem = value; this.OnPropertyChanged(); } } protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { var handler = this.PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); } } private static void GenerateData(List<dynamic> data, int startIndex = 0) { var random = new Random(1000); for (int i = startIndex; i < startIndex + 10; i++) { var row = new DynamicDataRow(); row.Add("Time", DateTime.Now); row.Add("NameD", "Klaus" + i); row.Add("Age", Convert.ToInt16(random.Next(short.MaxValue))); row.Add("Null", (object)null); row.Add("Double", Convert.ToDouble(1.3434)); data.Add(row); } } private void ViewItemsLoaded(object sender, VirtualQueryableCollectionViewItemsLoadedEventArgs e) { if (e.StartIndex == 10) { foreach (DynamicDataRow item in e.Items) { this.SelectedItem = item; break; } } } private void ViewItemsLoading(object sender, VirtualQueryableCollectionViewItemsLoadingEventArgs e) { var source = sender as VirtualQueryableCollectionView; if (source == null) { return; } var data = new List<dynamic>(); GenerateData(data, e.StartIndex); source.Load(e.StartIndex, data); } }
/// <summary> /// A collection of dynamic data rows can be used to populate a RadGridView /// with content if the number of columns is variable and only known during /// runtime. /// </summary> /// <seealso cref="System.Dynamic.DynamicObject" /> public class DynamicDataRow : DynamicObject { private readonly IDictionary<string, object> data; /// <summary> /// Initializes a new instance of the <see cref="DynamicDataRow" /> class. /// </summary> public DynamicDataRow() { this.data = new Dictionary<string, object>(); } /// <summary> /// Adds a value to a specified column in the current row. /// </summary> /// <param name="columnName">The column name.</param> /// <param name="value">The value for the cell.</param> public void Add(string columnName, object value) { this.data[columnName] = value; } /// <summary> /// Call this method to get a list of all column names. The method /// is used by the RadGridView to generate the columns during runtime- /// </summary> /// <returns> /// A sequence that contains the column names. /// </returns> public override IEnumerable<string> GetDynamicMemberNames() { return this.data.Keys; } /// <summary> /// Provides the implementation for operations that get member values. Classes derived from /// the <see cref="T:System.Dynamic.DynamicObject" /> class can override this method to specify /// dynamic behavior for operations such as getting a value for a property. /// </summary> /// <param name="binder"> /// Provides information about the object that called the dynamic operation. /// The binder.Name property provides the name of the member on which the dynamic operation is /// performed. For example, for the Console.WriteLine(sampleObject.SampleProperty) statement, /// where sampleObject is an instance of the class derived from the /// <see cref="T:System.Dynamic.DynamicObject" /> class, binder.Name returns "SampleProperty". /// The binder.IgnoreCase property specifies whether the member name is case-sensitive. /// </param> /// <param name="result"> /// The result of the get operation. For example, if the method is called /// for a property, you can assign the property value to <paramref name="result" />. /// </param> /// <returns> /// True if the operation is successful; otherwise, false. If this method returns false, the /// run-time binder of the language determines the behavior. (In most cases, a run-time /// exception is thrown.) /// </returns> public override bool TryGetMember(GetMemberBinder binder, out object result) { result = this.data[binder.Name]; return true; } /// <summary> /// Provides the implementation for operations that set member values. Classes derived /// from the <see cref="T:System.Dynamic.DynamicObject" /> class can override this method /// to specify dynamic behavior for operations such as setting a value for a property. /// </summary> /// <param name="binder"> /// Provides information about the object that called the dynamic /// operation. The binder.Name property provides the name of the member to which the value /// is being assigned. For example, for the statement sampleObject.SampleProperty = "Test", /// where sampleObject is an instance of the class derived from the /// <see cref="T:System.Dynamic.DynamicObject" /> class, binder.Name returns "SampleProperty". /// The binder.IgnoreCase property specifies whether the member name is case-sensitive. /// </param> /// <param name="value"> /// The value to set to the member. For example, for /// sampleObject.SampleProperty = "Test", where sampleObject is an instance of the class /// derived from the <see cref="T:System.Dynamic.DynamicObject" /> class, the /// <paramref name="value" /> is "Test". /// </param> /// <returns> /// True if the operation is successful; otherwise, false. If this method returns false, /// the run-time binder of the language determines the behavior. (In most cases, a /// language-specific run-time exception is thrown.) /// </returns> public override bool TrySetMember(SetMemberBinder binder, object value) { this.data[binder.Name] = value; return true; } }<Window x:Class="WPF.RadGridViewVirtualizing.MainWindow" xmlns:controls="http://schemas.telerik.com/2008/xaml/presentation" Title="MainWindow" Height="350" Width="525"> <Grid> <controls:RadGridView CanUserReorderColumns="False" ShowGroupPanel="False" AutoGenerateColumns="True" Grid.Row="0" IsFilteringAllowed="False" IsReadOnly="True" GridLinesVisibility="Both" SelectionMode="Extended" SelectionUnit="FullRow" RowIndicatorVisibility="Collapsed" AlternationCount="2" AlternateRowBackground="#F2F2F2" ItemsSource="{Binding Data}" SelectedItem="{Binding SelectedItem}" CanUserFreezeColumns="False"/> </Grid></Window>I tried to attach a small project which reproduces the problem, but zip was not allowed :(
Thanks
Michael