【问题标题】:ScrollIntoView for WPF DataGrid (MVVM)WPF DataGrid (MVVM) 的 ScrollIntoView
【发布时间】:2013-08-03 20:57:55
【问题描述】:

我正在使用 MVVM 模式,并在 XAML 中为 DataGrid 的 SelectedItem 创建了一个绑定。我以编程方式设置 SelectedItem,但是当我这样做时,DataGrid 不会滚动到选择。有什么方法可以在不完全破坏 MVVM 模式的情况下实现这一目标?

我找到了以下解决方案,但是当我尝试实现 Behavior 类时出现错误,即使我已经安装了 Blend SDK:http://www.codeproject.com/Tips/125583/ScrollIntoView-for-a-DataGrid-when-using-MVVM

【问题讨论】:

  • 在尝试实现Behavior 时遇到什么错误?
  • @Gjeltema The type or namespace name 'Behavior' could not be found (are you missing a using directive or an assembly reference?)
  • 您的项目中是否引用了 System.Windows.Interactivity.dll?
  • @Gjeltema 我看不到一个。添加一个是否意味着我必须在我的程序中添加另一个 DLL?
  • 如果你想使用Behaviors(你正在开发的这个功能以及其他东西需要它),那么是的,你需要添加这个dll。此 dll 应该存在于 Blend SDK 中,并且应该在您安装它时已注册。

标签: c# wpf mvvm datagrid scroll


【解决方案1】:

这是我让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);
}

【讨论】:

    【解决方案2】:

    @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"/>
    

    每次我向上或向下滚动时,都会为组合框调用选择更改事件,并且不再可能将所选项目移出视图。

    【讨论】:

      【解决方案3】:

      我是 MVVM 的新手。我理解 MVVM 的想法并尝试正确地实现一切。 我遇到了与上面类似的问题,最后我在 XAML 中有 1 行,在后面的代码中有 1 行。其余代码在 VM 中。 我在 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 原则——而且更容易实现。

      欢迎任何cmets。

      【讨论】:

      • 优秀的答案。我建议在使用 ScrollIntoView 之前对 SelectedItem 进行额外的空检查,因为代码会在从 ItemsSource 中删除项目并且没有任何选定项目时抛出 NullReferenceException。
      • 我确实建议这样使用发件人:if (sender is DataGrid dataGrid &amp;&amp; dataGrid.SelectedItem != null) dataGrid.ScrollIntoView(dataGrid.SelectedItem);
      【解决方案4】:

      这应该可行。这个想法是你有这个附加属性,你将附加到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 sn-p:

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

      【讨论】:

      • +1 非常感谢您的回答。但是我收到以下错误:System.Windows.Threading.Dispatcher' does not contain a definition for 'InvokeAsync' and no extension method 'InvokeAsync' accepting a first argument of type 'System.Windows.Threading.Dispatcher' could be found (are you missing a using directive or an assembly reference?)。任何想法为什么?我已经改变了MySelectionType
      • @Andy 抱歉,我仍在使用 .Net 4.5。我将更新我的答案以反映 .Net 4.0。
      • 哦好吧哈哈,没关系!它现在运行良好 - 少了一个 DLL!希望我能再次投票给你的答案!
      • @Andy 很高兴能帮上忙!
      • 我使用模板(ScrollIntoViewAttachedProperty)扩展了它,然后通过声明一个“单行类”(公共类 SelectedLogEntryAttachedProperty:ScrollIntoViewAttachedProperty { })为不同类型创建具体的依赖属性可以在 XAML 中引用。有人知道如何在 XAML 中引用泛型类型以避免创建这个“单行类”吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-17
      • 1970-01-01
      • 1970-01-01
      • 2012-09-17
      • 2011-12-11
      • 1970-01-01
      • 2019-03-02
      相关资源
      最近更新 更多