Хобрук: Ваш путь к мастерству в программировании

Двухсторонняя привязка не обновляется

Я добавил поведение ScrollViewer, которое позволяет мне прокручивать вверх, когда для свойства установлено значение 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 Inspector, я вижу, что свойство ScrollViewer равно false, как и должно быть, но мое свойство ViewModel остается true.

26.07.2018

  • отлаживать, чтобы найти вниз? 26.07.2018
  • @ThierryV, я поставил точки останова, когда выполняется строка кода, которая должна вернуть значение false, в ViewModel ничего нет. Инспектор WPF говорит, что он обновлен, но я не уверен, что еще нужно отлаживать. 26.07.2018
  • Привет, по-видимому (я несколько раз спотыкался об этом), метод Setvalue свойства зависимости не запускает Notifypropertychanged, как ожидалось. Возможно, может помочь расширение привязки поведения с помощью NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True (хотя последнего не произойдет, поскольку вы не меняете значение связанного элемента управления...) 27.07.2018
  • ладно, перечитывание показывает, нужно таргет обновить :) 27.07.2018
  • @dba интересно знать, что это не запускает событие. Есть ли способ обойти это? 27.07.2018
  • Параметр NotifyOn.. не работает для вашего сценария? 27.07.2018
  • Нет, значение по-прежнему не распространяется на ViewModel. 27.07.2018
  • Нубский вопрос: где вы привязываете свою модель представления как контекст данных? не вижу этого в этих кусках кода 27.07.2018
  • Код позади xaml, я обновлю сообщение с ним. 27.07.2018

Ответы:


1

Я не силен в английском, но я написал этот пример. Посмотри на это

[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>
27.07.2018
  • Ваш пример не демонстрирует то, что я пытаюсь сделать. Проблема в том, что поведение устанавливает значение обратно в false, но ViewModel не получает это значение. 27.07.2018
  • Новые материалы

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

    Настольный ПК как «одно кольцо, чтобы править всеми» домашних компьютеров
    Вид после 9 месяцев использования С настольных компьютеров все началось, но в какой-то момент они стали «серверами», и мы все перешли на ноутбуки. В прошлом году я столкнулся с идеей настольных..

    Расширенные методы безопасности для VueJS: реализация аутентификации без пароля
    Руководство, которое поможет вам создавать безопасные приложения в долгосрочной перспективе Безопасность приложений часто упускается из виду в процессе разработки, потому что основная..

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..

    Обзор 20 основных и современных методов работы с массивами в JavaScript
    Вы знаете их всех? В этом коротком посте я покажу сводку методов, доступных в JavaScript для работы с массивами. Я надеюсь, что вы найдете это полезным! В конце поста вы найдете ссылку на..

    Да, но я чувствую необходимость указать, что это или не единственные два.
    Да, но я чувствую необходимость указать, что это или не единственные два. Обучение с подкреплением (в качестве примера) также является важным.