문제

I am using Caliburn micro(1.3)/MVVM and Silverlight. When I update the itemsource RadGridView, I lose the selected items. I found a blog about implementing a behavior to save the selected items when you are implementing MVVM. I can get the selected items, but I cannot set them back once the itemsource is refreshed. Can someoneshow me how to implement this using caliburn.micro and the RadGridVIew? I think the best way to go is to create a caliburn micro convention, but I can only find a reference for creating a convention for selectedItem, not selectedItems.

Can someone show me how to accomplish this? I tried the following, but it does not work.

  private static void SetRadGridSelecteditemsConventions()
    {
        ConventionManager
            .AddElementConvention<DataControl>(DataControl.ItemsSourceProperty, "SelectedItem", "SelectionChanged")
            .ApplyBinding = (viewModelType, path, property, element, convention) =>
                                {
                                    ConventionManager.SetBinding(viewModelType, path, property, element, convention, DataControl.ItemsSourceProperty);

                                    if (ConventionManager.HasBinding(element, DataControl.SelectedItemProperty))
                                        return true;

                                    var index = path.LastIndexOf('.');
                                    index = index == -1 ? 0 : index + 1;
                                    var baseName = path.Substring(index);
                                    foreach (var selectionPath in
                                        from potentialName in ConventionManager.DerivePotentialSelectionNames(baseName)
                                        where viewModelType.GetProperty(potentialName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance) != null
                                        select path.Replace(baseName, potentialName))
                                    {
                                        var binding = new Binding(selectionPath) { Mode = BindingMode.TwoWay };
                                        BindingOperations.SetBinding(element, DataControl.SelectedItemProperty, binding);
                                    }
                                    return true;
                                };
    }

Thanks, Stephane

도움이 되었습니까?

해결책

You should use a behavior for this since the SelectedItems property is readonly. Telerik has an example for this, only the example is not specific for caliburn.micro. If you add the following class to your project:

public class MultiSelectBehavior : Behavior<RadGridView>
{
    public INotifyCollectionChanged SelectedItems
    {
        get { return (INotifyCollectionChanged)GetValue(SelectedItemsProperty); }
        set { SetValue(SelectedItemsProperty, value); }
    }

    public static readonly DependencyProperty SelectedItemsProperty =
        DependencyProperty.Register("SelectedItems", typeof(INotifyCollectionChanged), typeof(MultiSelectBehavior), new PropertyMetadata(OnSelectedItemsPropertyChanged));


    private static void OnSelectedItemsPropertyChanged(DependencyObject target, DependencyPropertyChangedEventArgs args)
    {
        var collection = args.NewValue as INotifyCollectionChanged;
        if (collection != null)
        {
            collection.CollectionChanged += ((MultiSelectBehavior)target).ContextSelectedItems_CollectionChanged;
        }
    }

    protected override void OnAttached()
    {
        base.OnAttached();

        AssociatedObject.SelectedItems.CollectionChanged += GridSelectedItems_CollectionChanged;
    }

    void ContextSelectedItems_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        UnsubscribeFromEvents();

        Transfer(SelectedItems as IList, AssociatedObject.SelectedItems);

        SubscribeToEvents();
    }

    void GridSelectedItems_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    {
        UnsubscribeFromEvents();

        Transfer(AssociatedObject.SelectedItems, SelectedItems as IList);

        SubscribeToEvents();
    }

    private void SubscribeToEvents()
    {
        AssociatedObject.SelectedItems.CollectionChanged += GridSelectedItems_CollectionChanged;

        if (SelectedItems != null)
        {
            SelectedItems.CollectionChanged += ContextSelectedItems_CollectionChanged;
        }
    }

    private void UnsubscribeFromEvents()
    {
        AssociatedObject.SelectedItems.CollectionChanged -= GridSelectedItems_CollectionChanged;

        if (SelectedItems != null)
        {
            SelectedItems.CollectionChanged -= ContextSelectedItems_CollectionChanged;
        }
    }

    public static void Transfer(IList source, IList target)
    {
        if (source == null || target == null)
            return;

        target.Clear();

        foreach (var o in source)
        {
            target.Add(o);
        }
    }
}

This behavior takes care of the synchronization between collection RadGridView.SelectedItems and MultiSelectBehavior.SelectedItems.

Now we need to have an ObservableCollection in the ViewModel

    //Collection holding the selected items
    private ObservableCollection<object> selectedGridItems;
    public ObservableCollection<object> SelectedGridItems
    {
        get
        {
            if (selectedGridItems == null)
                selectedGridItems = new ObservableCollection<object>();

            return selectedGridItems;
        }
        set
        {
            if (selectedGridItems == value) return;
            selectedGridItems = value;
            NotifyOfPropertyChange(() => SelectedGridItems);
        }
    }

    //Deselect all selected items in the gridview
    public void ClearSelectedGridItems()
    {
        SelectedGridItems.Clear();
    }

Last thing is bind the behavior in the view

    <telerik:RadGridView x:Name="CustomLogs" AutoGenerateColumns="true" SelectionMode="Extended">
        <i:Interaction.Behaviors>
            <local:MultiSelectBehavior SelectedItems="{Binding SelectedGridItems}"/>                
        </i:Interaction.Behaviors>                
    </telerik:RadGridView>

Thats it, hope it helps you!

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top