locked
How to stop Scroll changed event from triggering on data binding (WPF) RRS feed

  • Question

  • <ScrollViewer VerticalScrollBarVisibility="Visible"  Presentation:ScrollViewerExtensions.AlwaysScrollToEnd="True" >
                    <ItemsControl ItemsSource="{Binding Source={StaticResource ConversationCollection}}" Grid.Row="0" Margin="0,20,0,0" HorizontalAlignment="Stretch" AlternationCount="2">
                        <ItemsControl.ItemTemplate>
                           
                        </ItemsControl.ItemTemplate>
                        <ItemsControl.GroupStyle>
                            <GroupStyle>
                                <GroupStyle.ContainerStyle>
                                    <Style TargetType="GroupItem">
                                        <Setter Property="Template">
                                            <Setter.Value>
                                                <ControlTemplate TargetType="GroupItem">
                                                    <Grid>
                                                        <Grid.RowDefinitions>
                                                            <RowDefinition/>
                                                            <RowDefinition/>
                                                        </Grid.RowDefinitions>
                                                        <TextBlock Grid.Row="0" HorizontalAlignment="Center"  FontWeight="Bold"  Foreground="{StaticResource Accent}" Text="{Binding Path=Name , StringFormat={}{0:D}}"  />
                                                        <ItemsPresenter Grid.Row="1"/>
                                                    </Grid>
                                                </ControlTemplate>
                                            </Setter.Value>
                                        </Setter>
                                    </Style>
                                </GroupStyle.ContainerStyle>
                            </GroupStyle>
                        </ItemsControl.GroupStyle>
                    </ItemsControl>
                    <Interactivity:Interaction.Triggers>
                        <Interactivity:EventTrigger EventName="ScrollChanged" >
                            <Presentation:InvokeDelegateCommandAction  Command="{Binding ChatScrollViewer_OnViewChange}" CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=InvokeParameter}" />
                        </Interactivity:EventTrigger>
                    </Interactivity:Interaction.Triggers>
                </ScrollViewer>
    
    
    
        private void ChatScrollViewer_OnViewChangeEvent(ScrollChangedEventArgs Event)
            {
                ScrollViewer scrollViewer = Event.OriginalSource as ScrollViewer;
                if (scrollViewer.VerticalOffset == 0)
                {
                   
                }
            }
    

    When data binding take place scroll changed event is triggered . How check that this is a trigger from data binding and do nothing and when triggered manually do something 
    Friday, April 8, 2016 10:51 AM

Answers

  • >>How check that this is a trigger from data binding and do nothing and when triggered manually do something

    You can't really tell. As far as the event handler is concerned, there is no difference whether it was invoked as a result of a data binding taking place or asa result of the user manually scrolling. It is the exact same event that gets raised.

    You will need to add some logic to your view model that determines whether to actually do something in the ChatScrollViewer_OnViewChangeEvent method.

    If you are only data binding once, you could simply use a counter variable and return the immediately except for the first time the command is invoked:

    int count = 0;
    private void ChatScrollViewer_OnViewChangeEvent(ScrollChangedEventArgs Event)
            {
                if(count++ > 0)
      return;
    
                ScrollViewer scrollViewer = Event.OriginalSource as ScrollViewer;
                if (scrollViewer.VerticalOffset == 0)
                {
                   
                }
            }
    

    If the binding is refreshed more than once, you should handle another event. There is indeed no event raised when the ItemsSource property of an ItemsControl gets updated but you could create a custom ItemsControl and override the OnItemsChanged method: http://stackoverflow.com/questions/10708806/how-to-raise-an-event-when-datagrid-itemssource-is-changed

    Or you could use a DependencyPropertyDescriptor and invoke the command from the code-behind of the view:

    public MainWindow()
            {
                InitializeComponent();
    
                var dpd = System.ComponentModel.DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl));
                if (dpd != null)
                {
                    dpd.AddValueChanged(itemsControl1, OnItemsSourceChanged);
                }
    }
    
    private void OnItemsSourceChanged(object sender, EventArgs e)
    {
    var vm = this.DataContext as ViewModel;
    vm.ChatScrollViewer_OnViewChange.Execute(scrollViewer1);
    }
    

    And no, this doesn't break the MVVM pattern since you are just invoking the command from code-behind of the same view instead of using a XAML trigger. MVVM is not about eliminating code from the view, it is about separation of concerns.

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    • Proposed as answer by DotNet Wang Friday, April 15, 2016 8:36 AM
    • Marked as answer by Xavier Xie-MSFT Thursday, April 21, 2016 12:32 PM
    Sunday, April 10, 2016 4:08 PM

All replies

  • You could use a flag so you know the ui has completed loaded.

    private bool isLoading = true;
    
    public YourViewModel()
    {
      // Constructor
     Application.Current.Dispatcher.InvokeAsync(new Action(() => 
      {  
        isLoading = false;
      }), DispatcherPriority.ContextIdle);
    
    } 
    
       private void ChatScrollViewer_OnViewChangeEvent(ScrollChangedEventArgs Event)
            {
                if(isLoading)
                    return;
    
                ScrollViewer scrollViewer = Event.OriginalSource as ScrollViewer;
                if (scrollViewer.VerticalOffset == 0)
                {
                   
                }
            }
     

    Note that this is something of a hack relying on Dispatcher and I don't really like such code in a constructor of a viewmodel which will be unit tested.

    You could perhaps bind isfocused or some such to a property in the viewmodel and use that instead but I'd have to know more about your view and viewmodel to advise on that.


    Hope that helps.

    Technet articles: WPF: Layout Lab; All my Technet Articles

    • Proposed as answer by DotNet Wang Friday, April 15, 2016 8:36 AM
    Friday, April 8, 2016 11:18 AM
  • >>How check that this is a trigger from data binding and do nothing and when triggered manually do something

    You can't really tell. As far as the event handler is concerned, there is no difference whether it was invoked as a result of a data binding taking place or asa result of the user manually scrolling. It is the exact same event that gets raised.

    You will need to add some logic to your view model that determines whether to actually do something in the ChatScrollViewer_OnViewChangeEvent method.

    If you are only data binding once, you could simply use a counter variable and return the immediately except for the first time the command is invoked:

    int count = 0;
    private void ChatScrollViewer_OnViewChangeEvent(ScrollChangedEventArgs Event)
            {
                if(count++ > 0)
      return;
    
                ScrollViewer scrollViewer = Event.OriginalSource as ScrollViewer;
                if (scrollViewer.VerticalOffset == 0)
                {
                   
                }
            }
    

    If the binding is refreshed more than once, you should handle another event. There is indeed no event raised when the ItemsSource property of an ItemsControl gets updated but you could create a custom ItemsControl and override the OnItemsChanged method: http://stackoverflow.com/questions/10708806/how-to-raise-an-event-when-datagrid-itemssource-is-changed

    Or you could use a DependencyPropertyDescriptor and invoke the command from the code-behind of the view:

    public MainWindow()
            {
                InitializeComponent();
    
                var dpd = System.ComponentModel.DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl));
                if (dpd != null)
                {
                    dpd.AddValueChanged(itemsControl1, OnItemsSourceChanged);
                }
    }
    
    private void OnItemsSourceChanged(object sender, EventArgs e)
    {
    var vm = this.DataContext as ViewModel;
    vm.ChatScrollViewer_OnViewChange.Execute(scrollViewer1);
    }
    

    And no, this doesn't break the MVVM pattern since you are just invoking the command from code-behind of the same view instead of using a XAML trigger. MVVM is not about eliminating code from the view, it is about separation of concerns.

    Hope that helps.

    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    • Proposed as answer by DotNet Wang Friday, April 15, 2016 8:36 AM
    • Marked as answer by Xavier Xie-MSFT Thursday, April 21, 2016 12:32 PM
    Sunday, April 10, 2016 4:08 PM