WPF数据网格(MVVM)的ScrollIntoView
本文关键字:ScrollIntoView MVVM 数据 数据网 网格 WPF | 更新日期: 2023-09-27 18:26:05
我使用MVVM模式,并且在XAML中为DataGrid的SelectedItem创建了绑定。我用程序设置SelectedItem,但当我这样做时,DataGrid不会滚动到所选内容。有没有什么方法可以在不完全打破MVVM模式的情况下实现这一点?
我找到了以下解决方案,但在尝试实现Behavior
类时遇到了一个错误,尽管我已经安装了Blend SDK:http://www.codeproject.com/Tips/125583/ScrollIntoView-for-a-DataGrid-when-using-MVVM
这应该可以工作。我们的想法是,您有一个附加的属性,将其附加到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中有1行,在代码后面有1行。其余的代码在虚拟机中。我在XAML 中做了以下操作
<ListBox DockPanel.Dock="Top"
Name="Selection1List"
ItemsSource="{Binding SelectedList1ItemsSource}"
SelectedItem="{Binding SelectedList1Item}"
SelectedIndex="{Binding SelectedList1SelectedIndex}"
SelectionChanged="Selection1List_SelectionChanged">
这在后面的代码中:
private void Selection1List_SelectionChanged(object sender, SelectionChangedEventArgs e) {
Selection1List.ScrollIntoView(Selection1List.SelectedItem);
}
这很好用。
我知道有些人甚至不想在窗口后面的代码中有一行代码。但我认为这1行只是为了观点。它与数据或数据的逻辑无关。所以我认为这并没有违反MVVM原则,而且更容易实现。
欢迎任何意见。
@Edgar的解决方案运行良好,但在我的应用程序中,我还必须检查SelectionChangedEventArgs的OriginalSource。
private void OperatorQualificationsTable_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if ((OperatorQualificationsTable.SelectedItem != null) && (e.OriginalSource?.Equals(OperatorQualificationsTable) ?? false))
{
OperatorQualificationsTable.ScrollIntoView(OperatorQualificationsTable.SelectedItem);
}
}
我的数据网格包含以下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"/>
每次向上或向下滚动时,都会为组合框调用selction changed事件,并且无法再将所选项目移出视图。
这是我让ScrollIntoView
工作的解决方案。我在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 class ScrollingDataGrid : DataGrid
{
protected override void OnSelectionChanged(SelectionChangedEventArgs e)
{
var grid = e.Source as DataGrid;
if(grid.SelectedItem != null)
{
grid.UpdateLayout();
grid.ScrollIntoView(grid.SelectedItem, null);
}
base.OnSelectionChanged(e);
}
}