ScrollIntoView for WPF DataGrid (MVVM)

前端 未结 4 1140
花落未央
花落未央 2020-12-03 07:44

I\'m using the MVVM pattern, and I\'ve created a binding in XAML for the SelectedItem of a DataGrid. I programatically set the SelectedItem, however when I do so the DataGri

相关标签:
4条回答
  • 2020-12-03 07:59

    This should work. The idea is you have this attached property that you will attach to the DataGrid. In the xaml where you attach it, you'll bind it to a property on your ViewModel. Whenever you want to programmatically assign a value to the SelectedItem, you also set a value to this property, which the attached property is bound to.

    I've made the attached property type to be whatever the SelectedItem type is, but honestly it doesn't matter what the type is as long as you set it to something different than what it was before. This attached property is just being used as a means to execute some code on the view control (in this case, a DataGrid) in an MVVM friendly fashion.

    So, that said, here's the code for the attached property:

    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);
                }));
            }
        }
    }
    

    And here's the xaml snippet:

    <Window ...
            xmlns:attachedProperties="clr-namespace:MyAttachedProperties">
        ...
            <DataGrid 
                attachedProperties:SelectingItemAttachedProperty.SelectingItem="{Binding MyViewModel.SelectingItem}">
                ...
            </DataGrid>
        </Grid>
    
    0 讨论(0)
  • 2020-12-03 08:00

    I am new to MVVM. I understand the idea of MVVM and try to implement everything correctly. I had a similar problem to above and I ended up with 1 line in XAML and 1 line in code behind. The rest of the code is in the VM. I did the following in XAML

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

    And this in the code behind:

    private void Selection1List_SelectionChanged(object sender, SelectionChangedEventArgs e) {
        Selection1List.ScrollIntoView(Selection1List.SelectedItem);
    }
    

    and this works fine.

    I know some people don't want even one line of code in the code behind the window. But I think this 1 line is just for the view. It has nothing to do with the data or with the logic of the data. So I would think this is no violation of the MVVM principle - and so much easier to implement.

    Any comments are welcome.

    0 讨论(0)
  • 2020-12-03 08:03

    The solution of @Edgar works fine, but in my application I had to check the OriginalSource of the SelectionChangedEventArgs as well.

    private void OperatorQualificationsTable_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        if ((OperatorQualificationsTable.SelectedItem != null) && (e.OriginalSource?.Equals(OperatorQualificationsTable) ?? false))
        {
            OperatorQualificationsTable.ScrollIntoView(OperatorQualificationsTable.SelectedItem);
        }
    }
    

    My datagrid contains following 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"/>
    

    Everytime when I scrolled up or down the selction changed event was called for the Combobox and it was no longer possible to move the selected item out of the view.

    0 讨论(0)
  • 2020-12-03 08:22

    This is my solution to get ScrollIntoView working. I perform the operation in the LayoutUpdated() event

    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);
    }
    
    0 讨论(0)
提交回复
热议问题