WPF Datagrid:MVVM友好的方式将选定单元格绑定到我的ViewModel

WPF Datagrid:MVVM友好的方式将选定单元格绑定到我的ViewModel,wpf,mvvm,datagrid,Wpf,Mvvm,Datagrid,我使用的是WPF数据网格,有SelectionUnit=“Cell”和SelectionMode=“Extended”。我还尽可能地坚持MVVM原则 我需要我的ViewModel来跟踪当前选定的单元格 如果我能将它的SelectedCells属性绑定到我的ViewModel,生活就会变得简单。奇怪的是,SelectedCells只在我们第一次选择网格中的任何单元格时被提升一次 MS在这里解释道: 有人能想出一个MVVM友好的方法来绕过它吗 谢谢 在完美的MVVM绑定和完整的事件处理程序代码之间的

我使用的是WPF数据网格,有SelectionUnit=“Cell”和SelectionMode=“Extended”。我还尽可能地坚持MVVM原则

我需要我的ViewModel来跟踪当前选定的单元格

如果我能将它的SelectedCells属性绑定到我的ViewModel,生活就会变得简单。奇怪的是,SelectedCells只在我们第一次选择网格中的任何单元格时被提升一次

MS在这里解释道:

有人能想出一个MVVM友好的方法来绕过它吗


谢谢

在完美的MVVM绑定和完整的事件处理程序代码之间的某个地方,存在交互事件触发器的灰色区域(请参见Blend SDK):
如果将eventtrigger放入datagrid,并设置为“SelectionChanged”,并将eventargs传递给命令(使用EventToCommand actiontrigger),则可以从eventargs中获取所选项目…

或者使用MS线程中所述的多重绑定:)

您可能对图书库示例应用程序感兴趣。它显示了如何将DataGrid.SelectedItems与ViewModel同步。这可能与SelectedCells非常相似。

您需要不断绑定SelectedCells数据,还是在用户点击OK/Accept按钮时才需要?例如,如果您只在用户所在的任何进程结束时需要它,您可以将SelectedCells绑定到按钮的CommandParameter属性。SelectedCells是一个IList,您知道的足够多,只需对选择的实际对象类型执行强制转换即可。另一个选项是messier,您可以使用附加属性,将事件处理排除在视图之外。此附加属性将处理ListBox,或者在您的情况下处理DataGrid(MultiSelector)

公共类附加
{
公共静态IList GetSelectedItems(DependencyObject obj)
{
返回(IList)对象获取值(SelectedItemsProperty);
}
公共静态void SetSelectedItems(DependencyObject对象,IList值)
{
对象设置值(SelectedItemsProperty,value);
}
/// 
///附加此属性以公开用于数据绑定的MultiSelector的只读SelectedItems属性。
/// 
公共静态只读从属属性SelectedItemsProperty=
DependencyProperty.RegisterAttached(“SelectedItems”、typeof(IList)、typeof(Attach)、new UIPropertyMetadata(new List()作为IList,OnSelectedItemsChanged));
静态选择ChangedHandler GetSelectionChangedHandler(DependencyObject obj)
{
返回(SelectionChangedHandler)对象获取值(SelectionChangedHandlerProperty);
}
静态无效设置SelectionChangedHandler(DependencyObject对象,SelectionChangedEventHandler值)
{
对象设置值(选择ChangedHandlerProperty,值);
}
静态只读从属属性SelectionChangedHandlerProperty=
DependencyProperty.RegisterAttached(“SelectionChangedHandler”、typeof(SelectionChangedEndHandler)、typeof(Attach)、new-UIPropertyMetadata(null));
//d是多选择器(不支持d作为列表框)
SelectedItemsChanged上的静态无效(DependencyObject d、DependencyPropertyChangedEventArgs args)
{
如果(GetSelectionChangedHandler(d)!=null)
返回;
如果(d是多选择器)//DataGrid
{
MultiSelector MultiSelector=d作为MultiSelector;
SelectionChangedEventHandler selectionchanged=null;
foreach(在GetSelectedItems(d)中选择的var作为IList)
multiselector.SelectedItems.Add(选中);
selectionchanged=(发件人,e)=>
{
设置SelectedItems(d,多选择器。SelectedItems);
};
设置SelectionChangedHandler(d,selectionchanged);
multiselector.SelectionChanged+=GetSelectionChangedHandler(d);
}
else if(d是列表框)
{
ListBox ListBox=d作为ListBox;
SelectionChangedEventHandler selectionchanged=null;
selectionchanged=(发件人,e)=>
{
设置SelectedItems(d,列表框。SelectedItems);
};
设置SelectionChangedHandler(d,selectionchanged);
listbox.SelectionChanged+=GetSelectionChangedHandler(d);
}}}
XAML中的用法:

<DataGrid ItemsSource="{Binding Path=SourceList}"
              myControls:Attach.SelectedItems="{Binding Path=myMvvmSelectedItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" 
              SelectionMode="Extended" />

我意识到我的最后一个答案是针对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);
        //}
    }

}
公共类DataGridSelectedCellsBehavior
{
//资料来源:https://archive.codeplex.com/?p=datagridthemesfromsl
//归功于:T.韦伯斯特,https://stackoverflow.com/users/266457/t-webster
公共静态IList GetSelectedCells(DependencyObject obj)
{
返回(IList)对象获取值(SelectedCellsProperty);
}
公共静态void SetSelectedCells(DependencyObject对象,IList值)
{
对象设置值(SelectedCellsProperty,value);
}
public static readonly DependencyProperty SelectedCellsProperty=DependencyProperty.RegisterAttached(“SelectedCells”、typeof(IList)、typeof(DataGridSelectedCellsBehavior)、new UIPropertyMetadata(null,OnSelectedCellsAnged));
静态SelectedCellsChangedEventHandler GetSelectionChangedHandler(DependencyObject obj)
{
返回(SelectedCellsChangedEventHandler)对象获取值(SelectionChangedHandlerProperty);
}
静态无效设置SelectionChangedHandler(DependencyObject对象,SelectedCellsChangedHandler值)
{
对象设置值(选择ChangedHandlerProperty,值);
}
静态只读DependencyProperty SelectionChangedHandlerProperty=DependencyProperty.RegisterAttached(“SelectedCellsChangedEventHandler”,类型为
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;
}