TwoWay绑定未更新

时间:2018-07-26 20:29:47

标签: c# wpf mvvm

我添加了ScrollViewer行为,当基于Scroll the scrollviewer to top through viewmodel将属性设置为true时,我可以滚动到顶部。我发现这第一次很好,但是随后的尝试不会触发,因为TwoWay绑定没有将我的属性设置回false

这是我的简单项目,显示了我的问题:

MainWindow.xaml

<Window x:Class="WpfApp1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp1"
        mc:Ignorable="d"
        Title="MainWindow" Height="100" Width="200">
    <ScrollViewer local:ScrollViewerBehavior.ScrollToTop="{Binding Reset, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="200"/>
            </Grid.RowDefinitions>
            <Button VerticalAlignment="Bottom" Content="Top" Command="{Binding Scroll}"/>
        </Grid>
    </ScrollViewer>
</Window>

MainWindow.xaml.cs

using System.Windows;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private ViewModel _ViewModel;

        public MainWindow()
        {
            InitializeComponent();

            DataContext = _ViewModel = new ViewModel();
        }
    }
}

ViewModel.cs

using System;
using System.ComponentModel;
using System.Windows.Input;

namespace WpfApp1
{
    public class RelayCommand : ICommand
    {
        private readonly Action<object> action;

        public RelayCommand(Action<object> action)
        {
            this.action = action;
        }

        public event EventHandler CanExecuteChanged;

        public bool CanExecute(object parameter)
        {
            return true;
        }

        public void Execute(object parameter)
        {
            action(parameter);
        }
    }

    public class ViewModel : INotifyPropertyChanged
    {
        private bool _reset = false;

        public ViewModel()
        {
            Scroll = new RelayCommand(o =>
            {
                Reset = true;
            });
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public bool Reset
        {
            get { return _reset; }
            set
            {
                bool changed = value != _reset;
                _reset = value;

                if (changed)
                {
                    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Reset)));
                }
            }
        }

        public RelayCommand Scroll { get; set; }
    }
}

ScrollViewerBehavior.cs

using System.Windows;
using System.Windows.Controls;

namespace WpfApp1
{
    public static class ScrollViewerBehavior
    {
        public static readonly DependencyProperty ScrollToTopProperty = DependencyProperty.RegisterAttached("ScrollToTop", typeof(bool), typeof(ScrollViewerBehavior), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, (o, e) =>
        {
            if (o is ScrollViewer sc)
            {
                if ((bool)e.NewValue)
                {
                    sc.ScrollToTop();
                    SetScrollToTop((FrameworkElement)o, false); // this should set the property back to false
                }
            }
        }));

        public static bool GetScrollToTop(FrameworkElement o)
        {
            return (bool)o.GetValue(ScrollToTopProperty);
        }

        public static void SetScrollToTop(FrameworkElement o, bool value)
        {
            o.SetValue(ScrollToTopProperty, value);
        }
    }
}

我知道,如果我对属性进行更改后的支票,它会起作用;但是,这对于我的情况而言并不理想。当我通过WPF检查器查看元素时,我看到ScrollViewer上的属性应为false,但我的ViewModel属性仍然为true

1 个答案:

答案 0 :(得分:0)

我的英语不好,但是我写了这个例子。看这个

[MainWindow.xaml.cs]

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    AnyClass c = new AnyClass();

    private void h1_Click(object sender, RoutedEventArgs e)
    {
        Test = true;
    }
    private void h2_Click(object sender, RoutedEventArgs e)
    {
        Test = false;
    }

    public bool Test
    {
        get { return (bool)GetValue(TestProperty); }
        set
        {
            SetValue(TestProperty, value);
            c.Val = value;
        }
    }
    public static readonly DependencyProperty TestProperty =
        DependencyProperty.Register("Test", typeof(bool), typeof(MainWindow), new PropertyMetadata(false));


}

[AnyClass.cs]
class AnyClass
{
    private bool val = false;
    public bool Val
    {
        get
        {
            return val;
        }
        set
        {
            val = value;
        }
    }
}

[mainWindow.xaml]
<Button Click="h1_Click" Content="true">
            <Button.Style>
                <Style TargetType="Button">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ElementName=hUserControl, Path=Test}" Value="True">
                            <Setter Property="Visibility" Value="Collapsed"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding ElementName=hUserControl, Path=Test}" Value="False">
                            <Setter Property="Visibility" Value="Visible"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
        </Button>
        <Button Click="h2_Click" Content="false">
            <Button.Style>
                <Style TargetType="Button">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding ElementName=hUserControl, Path=Test}" Value="True">
                            <Setter Property="Visibility" Value="Visible"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding ElementName=hUserControl, Path=Test}" Value="False">
                            <Setter Property="Visibility" Value="Collapsed"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
        </Button>