Home > Enterprise >  Attached property is not updating the value it is bind to (MVVM)
Attached property is not updating the value it is bind to (MVVM)

Time:10-31

I have a ScrollViewer filled with green rectangles and I need to report its scroll value (or percentage) on a TextBlock everytime it changes. I also need to be able to change the scroll value by changing the property it is bind to.

I have created an Attached property ScrollViewerBehavior which gives me the value I need, and I bind it to a property of my viewmodel (ScrollValue) however the value is not updated as the setter is never reached. The viewmodel is correctly linked with the view.

Here is my code:
View

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="6*" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>

    <ScrollViewer Grid.Row="0" Background="Gray" local:ScrollViewerBehavior.ScrollState="{Binding ScrollValue, Mode=TwoWay}" >
        <StackPanel>
            <Rectangle Width="50" Height="50" Fill="Green" Margin="5" />
            <Rectangle Width="50" Height="50" Fill="Green" Margin="5" />
            <Rectangle Width="50" Height="50" Fill="Green" Margin="5" />
            <Rectangle Width="50" Height="50" Fill="Green" Margin="5" />
            <Rectangle Width="50" Height="50" Fill="Green" Margin="5" />
        </StackPanel>
    </ScrollViewer>

    <TextBlock Grid.Row="1" Text="{Binding ScrollValue}" />
</Grid>


ViewModel

public class MainViewModel : BaseViewModel
{
    private double _scrollValue = 0;
    public double ScrollValue
    {
        get { return _scrollValue; }
        set
        {
            // This code is never reached 
            _scrollValue = value;
            OnPropertyChanged("ScrollValue");
        }
    }
}


ScrollViewerBehavior

    public static double GetScrollState(DependencyObject obj)
    {
        return (double)obj.GetValue(ScrollStateProperty);
    }

    public static void SetScrollState(DependencyObject obj, double value)
    {
        obj.SetValue(ScrollStateProperty, value);
    }

    public static readonly DependencyProperty ScrollStateProperty =
        DependencyProperty.RegisterAttached("ScrollState", typeof(double), typeof(ScrollViewerBehavior), new PropertyMetadata(0.5, (o, e) =>
        {
            var scrollViewer = o as ScrollViewer;
            
            if (scrollViewer == null)
            {
                return;
            }
            else
            {
                double newVO = (double)e.NewValue * scrollViewer.ScrollableHeight;
                scrollViewer.ScrollToVerticalOffset(newVO);
                SetScrollState(o, newVO);
            }
        }));

I can't see what I missed

CodePudding user response:

I do not see anywhere in the code where you are changing the value of ScrollValue. Since your attached property is bound to this, it only updates when ScrollValue in your view model is updated. Unless I am misunderstanding your explanation, you want to update ScrollValue everytime the scroll bar value changes. To do that you can hook into the ScrollChanged event:

(I only updated your code just show what was needed)

public static readonly DependencyProperty ScrollStateProperty =
    DependencyProperty.RegisterAttached("ScrollState", typeof(double), typeof(ScrollViewerBehavior), new PropertyMetadata(0.5, (o, e) =>
        {
            if (o is ScrollViewer scrollViewer)
            {
                scrollViewer.ScrollChanged  = OnScrollChanged;
            }
        }));

    private static void OnScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        if (sender is ScrollViewer scrollViewer)
        {
            SetScrollState(scrollViewer, scrollViewer.VerticalOffset);
        }
    }
  • Related