C# WPF数据网格(MVVM)的滚动视图

C# WPF数据网格(MVVM)的滚动视图,c#,wpf,mvvm,datagrid,scroll,C#,Wpf,Mvvm,Datagrid,Scroll,我使用的是MVVM模式,我已经用XAML为DataGrid的SelectedItem创建了一个绑定。我通过编程设置SelectedItem,但是当我这样做时,DataGrid不会滚动到所选内容。有什么方法可以在不完全打破MVVM模式的情况下实现这一点 我找到了以下解决方案,但当我尝试实现行为类时出现了一个错误,即使我已经安装了Blend SDK:这应该可以工作。这样做的目的是将这个附加属性附加到DataGrid。在附加它的xaml中,您将把它绑定到ViewModel上的属性。每当您希望以编程方式

我使用的是MVVM模式,我已经用XAML为DataGrid的SelectedItem创建了一个绑定。我通过编程设置SelectedItem,但是当我这样做时,DataGrid不会滚动到所选内容。有什么方法可以在不完全打破MVVM模式的情况下实现这一点


我找到了以下解决方案,但当我尝试实现
行为
类时出现了一个错误,即使我已经安装了Blend SDK:

这应该可以工作。这样做的目的是将这个附加属性附加到
DataGrid
。在附加它的xaml中,您将把它绑定到
ViewModel
上的属性。每当您希望以编程方式为
SelectedItem
赋值时,您还可以为此属性设置一个值,附加属性将绑定到该属性

我已经将附加的属性类型设置为
SelectedItem
类型的任何类型,但老实说,类型是什么并不重要,只要您将其设置为与以前不同的类型。此附加属性仅用作以MVVM友好方式在视图控件(在本例中为
DataGrid
)上执行某些代码的方法

也就是说,这是附属财产的代码:

namespace MyAttachedProperties
{
    public class SelectingItemAttachedProperty
    {
        public static readonly DependencyProperty SelectingItemProperty = DependencyProperty.RegisterAttached(
            "SelectingItem",
            typeof(MySelectionType),
            typeof(SelectingItemAttachedProperty),
            new PropertyMetadata(default(MySelectionType), OnSelectingItemChanged));

        public static MySelectionType GetSelectingItem(DependencyObject target)
        {
            return (MySelectionType)target.GetValue(SelectingItemProperty);
        }

        public static void SetSelectingItem(DependencyObject target, MySelectionType value)
        {
            target.SetValue(SelectingItemProperty, value);
        }

        static void OnSelectingItemChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var grid = sender as DataGrid;
            if (grid == null || grid.SelectedItem == null)
                return;

            // Works with .Net 4.5
            grid.Dispatcher.InvokeAsync(() => 
            {
                grid.UpdateLayout();
                grid.ScrollIntoView(grid.SelectedItem, null);
            });

            // Works with .Net 4.0
            grid.Dispatcher.BeginInvoke((Action)(() =>
            {
                grid.UpdateLayout();
                grid.ScrollIntoView(grid.SelectedItem, null);
            }));
        }
    }
}
下面是xaml片段:

<Window ...
        xmlns:attachedProperties="clr-namespace:MyAttachedProperties">
    ...
        <DataGrid 
            attachedProperties:SelectingItemAttachedProperty.SelectingItem="{Binding MyViewModel.SelectingItem}">
            ...
        </DataGrid>
    </Grid>

...
...

我是MVVM新手。我理解MVVM的思想,并尝试正确地实现所有内容。 我遇到了与上面类似的问题,最终在XAML中有一行代码,在代码隐藏中有一行代码。其余的代码在VM中。 我在XAML中做了以下工作

<ListBox DockPanel.Dock="Top"
    Name="Selection1List" 
    ItemsSource="{Binding SelectedList1ItemsSource}" 
    SelectedItem="{Binding SelectedList1Item}"
    SelectedIndex="{Binding SelectedList1SelectedIndex}"
    SelectionChanged="Selection1List_SelectionChanged">
这个很好用

我知道有些人甚至不希望在窗口后面的代码中包含一行代码。但我认为这一行只是为了看风景。它与数据或数据逻辑无关。所以我认为这并没有违反MVVM原则——而且实现起来更容易


欢迎发表任何意见。

Edgar的解决方案运行良好,但在我的应用程序中,我还必须检查SelectionChangedEventArgs的原始来源

private void OperatorQualificationsTable_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if ((OperatorQualificationsTable.SelectedItem != null) && (e.OriginalSource?.Equals(OperatorQualificationsTable) ?? false))
    {
        OperatorQualificationsTable.ScrollIntoView(OperatorQualificationsTable.SelectedItem);
    }
}
我的datagrid包含以下ComboBoxColumn

<dgx:EnhancedDataGridComboBoxColumn 
    DisplayMemberPath="DescriptionNL"
    Header="{x:Static nl:Strings.Label_Qualification}"
    ItemsSource="{Binding Path=QualificationKeysView, Source={StaticResource ViewModel}}"
    SelectedValueBinding="{Binding ActivityQualification.QualificationKey}"
    SelectedValuePath="QualificationKey"/>


每次我向上或向下滚动时,都会为组合框调用Selection changed事件,并且无法再将所选项目移出视图。

这是我的解决方案,可以让
滚动视图
正常工作。我在
LayoutUpdated()
事件中执行该操作

public void ManipulateData()
{
    // Add a new record or what else is needed;
    myItemsSourceCollection.Add(...); 

    // Not needed when the ItemsSource is a ObervableCollectin 
    // with correct Binding (ItemsSource="{ Binding myItemsSourceElement }")
    myDataGrid.Items.Refresh();

    // Goto last Item or where ever
    myDataGrid.SelectedIndex = this.myDataGrid.Items.Count - 1;
}

// The LayoutUpdated event for the DataGrid
private void myDataGrid_LayoutUpdated(object sender, EventArgs e)
{
    if (myDataGrid.SelectedItem == null)
        return;
    //<----------

    // may become improved to check first if the `ScrollIntoView()` is really needed

    // To prevent hanging here the ItemsSource must be 
    // a) an ObervableCollection with a correct working binding or
    // b) myDataGrid.Items.Refresh(); must be called after changing
    // the data
    myDataGrid.ScrollIntoView(myDataGrid.SelectedItem, null);
}
public void manufacturedata()
{
//添加新记录或其他所需内容;
myItemsSourceCollection.Add(…);
//当ItemsSource是ObervableCollectin时不需要
//具有正确的绑定(ItemsSource=“{Binding myItemsSourceElement}”)
myDataGrid.Items.Refresh();
//转到最后一项或任何地方
myDataGrid.SelectedIndex=this.myDataGrid.Items.Count-1;
}
//DataGrid的LayoutUpdated事件
私有void myDataGrid_布局已更新(对象发送方,事件参数e)
{
if(myDataGrid.SelectedItem==null)
返回;

//尝试实现
行为时会出现什么错误?@Gjeltema
找不到类型或命名空间名称“Behavior”(是否缺少using指令或程序集引用?)
您的项目中是否有对System.Windows.Interactivity.dll的引用?@Gjeltema我看不到。添加一个dll是否意味着我必须在我的程序中附带另一个dll?如果您想利用
行为(您正在使用的此功能以及其他功能都需要它),然后是的,您将需要添加此dll。此dll应该存在于Blend SDK中,并且应该在您安装它时注册。+1非常感谢您的回答。但是我收到以下错误:
System.Windows.Threading.Dispatcher'不包含“InvokeAsync”的定义,并且没有扩展方法“InvokeAsync”accep未能找到类型为“System.Windows.Threading.Dispatcher”的第一个参数(是否缺少using指令或程序集引用?)
。有什么想法吗?我已经更改了
我的选择类型
@Andy抱歉,我仍在.Net 4.5中工作。我将更新我的答案以反映.Net 4.0。哦,好吧,哈哈,没关系!它现在工作得很好-少了一个DLL!希望我能再次投票给你的答案!@Andy很高兴能提供帮助!我使用模板扩展了它(ScrollIntoViewAttachedProperty),然后通过声明“一行类”(公共类SelectedLogEntryAttachedProperty:ScrollIntoViewAttachedProperty{})为不同类型创建具体的依赖属性因此,它可以在XAML中引用。有人知道如何在XAML中引用泛型类型以避免创建此“单行类”吗?回答得很好。我建议在使用
ScrollIntoView
之前对SelectedItem进行额外的null检查,因为从ItemsSource中删除项时,代码会抛出NullReferenceException没有任何选定的项目。我建议如下使用发件人:
if(发件人是DataGrid DataGrid&&DataGrid.SelectedItem!=null)DataGrid.ScrollIntoView(DataGrid.SelectedItem);
public void ManipulateData()
{
    // Add a new record or what else is needed;
    myItemsSourceCollection.Add(...); 

    // Not needed when the ItemsSource is a ObervableCollectin 
    // with correct Binding (ItemsSource="{ Binding myItemsSourceElement }")
    myDataGrid.Items.Refresh();

    // Goto last Item or where ever
    myDataGrid.SelectedIndex = this.myDataGrid.Items.Count - 1;
}

// The LayoutUpdated event for the DataGrid
private void myDataGrid_LayoutUpdated(object sender, EventArgs e)
{
    if (myDataGrid.SelectedItem == null)
        return;
    //<----------

    // may become improved to check first if the `ScrollIntoView()` is really needed

    // To prevent hanging here the ItemsSource must be 
    // a) an ObervableCollection with a correct working binding or
    // b) myDataGrid.Items.Refresh(); must be called after changing
    // the data
    myDataGrid.ScrollIntoView(myDataGrid.SelectedItem, null);
}