WPF Datagrid: MVVM-удобный способ привязать выбранные ячейки к моей модели представления - PullRequest
6 голосов
/ 17 января 2011

Я использую сетку данных WPF и у меня есть SelectionUnit = "Cell" и SelectionMode = "Extended". Я также стараюсь придерживаться принципов MVVM, насколько я могу.

Мне нужна моя ViewModel, чтобы отслеживать текущие выбранные ячейки.

Жизнь была бы легкой, если бы я мог просто привязать ее свойство SelectedCells к моей ViewModel. Как ни странно, SelectedCells вызывается только один раз - когда мы впервые выбираем любую ячейку в сетке.

MS объясняет это здесь: http://social.msdn.microsoft.com/Forums/en/wpf/thread/737117f4-6d20-4232-88cf-e52cc44d4431

Может кто-нибудь придумать подход MVVM, способный его обойти?

Спасибо!

Ответы [ 4 ]

11 голосов
/ 23 января 2011

Я понял, что мой последний ответ был для SelectedItems вместо SelectedCells, поэтому я написал полный класс прикрепленных свойств для привязки данных для нескольких SelectedCells, который работает следующим образом:

 <controls:DataGrid ItemsSource="{StaticResource list}"
                      SelectionMode="Extended"
                      behaviors:DataGridSelectedCellsBehavior.SelectedCells="{Binding Path=SelectedGridCellCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>        

У меня есть рабочий исходный коди демонстрационный проект этого здесь .

Код поведения присоединенного свойства:

public class DataGridSelectedCellsBehavior
{
    // Source : https://archive.codeplex.com/?p=datagridthemesfromsl
    // Credit to : T. Webster, https://stackoverflow.com/users/266457/t-webster

    public static IList<DataGridCellInfo> GetSelectedCells(DependencyObject obj)
    {
        return (IList<DataGridCellInfo>)obj.GetValue(SelectedCellsProperty);
    }
    public static void SetSelectedCells(DependencyObject obj, IList<DataGridCellInfo> value)
    {
        obj.SetValue(SelectedCellsProperty, value);
    }

    public static readonly DependencyProperty SelectedCellsProperty = DependencyProperty.RegisterAttached("SelectedCells", typeof(IList<DataGridCellInfo>), typeof(DataGridSelectedCellsBehavior), new UIPropertyMetadata(null, OnSelectedCellsChanged));

    static SelectedCellsChangedEventHandler GetSelectionChangedHandler(DependencyObject obj)
    {
        return (SelectedCellsChangedEventHandler)obj.GetValue(SelectionChangedHandlerProperty);
    }
    static void SetSelectionChangedHandler(DependencyObject obj, SelectedCellsChangedEventHandler value)
    {
        obj.SetValue(SelectionChangedHandlerProperty, value);
    }

    static readonly DependencyProperty SelectionChangedHandlerProperty = DependencyProperty.RegisterAttached("SelectedCellsChangedEventHandler", typeof(SelectedCellsChangedEventHandler), typeof(DataGridSelectedCellsBehavior), new UIPropertyMetadata(null));

    //d is MultiSelector (d as ListBox not supported)
    static void OnSelectedCellsChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
    {
        if (GetSelectionChangedHandler(d) != null)
            return;

        if (d is DataGrid)//DataGrid
        {
            DataGrid datagrid = d as DataGrid;
            SelectedCellsChangedEventHandler selectionchanged = null;
            foreach (var selected in GetSelectedCells(d) as IList<DataGridCellInfo>)
                datagrid.SelectedCells.Add(selected);

            selectionchanged = (sender, e) =>
            {
                SetSelectedCells(d, datagrid.SelectedCells);
            };
            SetSelectionChangedHandler(d, selectionchanged);
            datagrid.SelectedCellsChanged += GetSelectionChangedHandler(d);
        }
        //else if (d is ListBox)
        //{
        //    ListBox listbox = d as ListBox;
        //    SelectionChangedEventHandler selectionchanged = null;

        //    selectionchanged = (sender, e) =>
        //    {
        //        SetSelectedCells(d, listbox.SelectedCells);
        //    };
        //    SetSelectionChangedHandler(d, selectionchanged);
        //    listbox.SelectionChanged += GetSelectionChangedHandler(d);
        //}
    }

}

Просмотреть код модели:

class DemoViewModel : INotifyPropertyChanged
{  
    private IList<DataGridCellInfo> selectedGridCellCollection = new List<DataGridCellInfo>();
    public IList<DataGridCellInfo> SelectedGridCellCollection
    {
        get { return selectedGridCellCollection; }
        set
        {
            selectedGridCellCollection = value;
            NotifyPropertyChanged();
        }
    }

    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public event PropertyChangedEventHandler PropertyChanged;
}
2 голосов
/ 22 января 2011

Вам нужно, чтобы SelectedCells постоянно связывались с данными, или только когда пользователь нажимает кнопку OK / Accept? Если вам это нужно только в конце любого процесса, в котором находится пользователь, вы можете, например, связать SelectedCells со свойством CommandParameter для Button. SelectedCells - это IList, и вы знаете достаточно, чтобы просто выполнить приведение к любому типу объекта, которым на самом деле является выделение. Другой вариант - более сложный, вы можете использовать прикрепленное свойство, не допуская обработки событий в ваших представлениях. Это присоединенное свойство будет обрабатывать ListBox или, в вашем случае, DataGrid (MultiSelector).

public class Attach 
{
    public static IList GetSelectedItems(DependencyObject obj)
    {
        return (IList)obj.GetValue(SelectedItemsProperty);
    }

    public static void SetSelectedItems(DependencyObject obj, IList value)
    {
        obj.SetValue(SelectedItemsProperty, value);
    }

    /// <summary>
    /// Attach this property to expose the read-only SelectedItems property of a MultiSelector for data binding.
    /// </summary>
    public static readonly DependencyProperty SelectedItemsProperty =
        DependencyProperty.RegisterAttached("SelectedItems", typeof(IList), typeof(Attach), new UIPropertyMetadata(new List<object>() as IList, OnSelectedItemsChanged));



    static SelectionChangedEventHandler GetSelectionChangedHandler(DependencyObject obj)
    {
        return (SelectionChangedEventHandler)obj.GetValue(SelectionChangedHandlerProperty);
    }
    static void SetSelectionChangedHandler(DependencyObject obj, SelectionChangedEventHandler value)
    {
        obj.SetValue(SelectionChangedHandlerProperty, value);
    }
    static readonly DependencyProperty SelectionChangedHandlerProperty =
        DependencyProperty.RegisterAttached("SelectionChangedHandler", typeof(SelectionChangedEventHandler), typeof(Attach), new UIPropertyMetadata(null));


    //d is MultiSelector (d as ListBox not supported)
    static void OnSelectedItemsChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)
    {
        if (GetSelectionChangedHandler(d) != null)
            return;

        if (d is MultiSelector)//DataGrid
        {
            MultiSelector multiselector = d as MultiSelector;
            SelectionChangedEventHandler selectionchanged = null;
            foreach (var selected in GetSelectedItems(d) as IList)
                multiselector.SelectedItems.Add(selected);

            selectionchanged = (sender, e) =>
            {
                SetSelectedItems(d, multiselector.SelectedItems);
            };
            SetSelectionChangedHandler(d, selectionchanged);
            multiselector.SelectionChanged += GetSelectionChangedHandler(d);
        }
        else if (d is ListBox)
        {
            ListBox listbox = d as ListBox;
            SelectionChangedEventHandler selectionchanged = null;

            selectionchanged = (sender, e) =>
            {
                SetSelectedItems(d, listbox.SelectedItems);
            };
            SetSelectionChangedHandler(d, selectionchanged);
            listbox.SelectionChanged += GetSelectionChangedHandler(d);
        }}}

Использование в XAML:

<DataGrid ItemsSource="{Binding Path=SourceList}"
              myControls:Attach.SelectedItems="{Binding Path=myMvvmSelectedItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
              SelectionMode="Extended" />
1 голос
/ 17 января 2011

Вас может заинтересовать BookLibrary пример приложения WPF Application Framework (WAF) .Он показывает, как синхронизировать DataGrid.SelectedItems с ViewModel.Это может быть очень похоже на SelectedCells.

0 голосов
/ 17 января 2011

Где-то между идеальными привязками MVVM и полными кодами обработчиков событий существует серая область интерактивности EventTriggers (см. Blend SDK):)
Если вы поместите событийный триггер в сетку данных, установите для него «SelectionChanged» и передадите событийные команды команде (используйте ActionTrigger EventToCommand), вы можете получить выбранные элементы из событийных надежд, надеюсь ...
Или используйте мультисвязывание, как сказано в потоке MS:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...