Ползунок.Значение изменяется при настройке Minimum, когда оно не должно

короче:
При определенных обстоятельствах установка слайдера.Минимум отрегулирует слайдер.Значение хотя текущее значение больше, чем новый минимум.

код: (должно быть повторимо)
файл MainWindow.язык XAML:

<Window x:Class="WpfApplication1.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <DockPanel>
        <Slider Name="MySlider" DockPanel.Dock="Top" AutoToolTipPlacement="BottomRight" />
        <Button Name="MyButton1" DockPanel.Dock="Top" Content="shrink borders"/>
        <Button Name="MyButton2" DockPanel.Dock="Top" VerticalAlignment="Top" Content="grow borders"/>
    </DockPanel>
</Window>

файл MainWindow.код XAML.cs:

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

namespace WpfApplication1
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            MySlider.ValueChanged += (sender, e) =>
            {
                System.Diagnostics.Debug.WriteLine("Value changed: " + e.NewValue);
            };

            System.ComponentModel.DependencyPropertyDescriptor.FromProperty(Slider.MinimumProperty, typeof(Slider)).AddValueChanged(MySlider, delegate
            {
                System.Diagnostics.Debug.WriteLine("Minimum changed: " + MySlider.Minimum);
            });

            System.ComponentModel.DependencyPropertyDescriptor.FromProperty(Slider.MaximumProperty, typeof(Slider)).AddValueChanged(MySlider, delegate
            {
                System.Diagnostics.Debug.WriteLine("Maximum changed: " + MySlider.Maximum);
            });

            MySlider.Value = 1;

            MySlider.Minimum = 0.5;
            MySlider.Maximum = 20;

            MyButton1.Click += (sender, e) =>
            {
                MySlider.Minimum = 1.6;
                MySlider.Maximum = 8;
            };

            MyButton2.Click += (sender, e) =>
            {
                MySlider.Minimum = 0.5;
                MySlider.Maximum = 20;
            };
        }
    }
}

воспроизведение:
1. Запуск в режиме отладки.
2. Переместите ползунок вправо.
3. Нажмите кнопку "shrink borders".
4. Нажмите кнопку" grow borders".

Ожидаемый Результат:

Value changed: 1
Minimum changed: 0,5
Maximum changed: 20
//Multiple times "Value changed"
Value changed: 20
Minimum changed: 1,6
Value changed: 8
Maximum changed: 8
Minimum changed: 0,5
Maximum changed: 20

слайдер остается на 8.

Фактический Выход:

Value changed: 1
Minimum changed: 0,5
Maximum changed: 20
//Multiple times "Value changed"
Value changed: 20
Minimum changed: 1,6
Value changed: 8
Maximum changed: 8
Value changed: 1
Minimum changed: 0,5
Maximum changed: 20

(обратите внимание на дополнительное "значение изменено: 1")
Слайдер переходит к 1.

Sidenote:
Бегунок Привязки.Значение OneWayToSource (или TwoWay) для свойства double, исправляет вопрос.

вопрос:
Почему это происходит?

теория:
Я уверен, что это как-то связано с Значение Принуждения. Кажется, происходит следующее:
1. Установка значения в 1 программно устанавливает "базовое значение" значения. Он находится между значениями по умолчанию для минимального и максимального, поэтому "эффективное значение" точно так же.
2. Установка минимума и максимума ничего не меняет, потому что Ценность все еще между ними.
3. Вручную потянув ползунок вправо, по-видимому, изменяет "эффективное значение", но по какой-то странной причине не"базовое значение".
4. Снова увеличивая границы, вызывает обратный вызов принуждения, который понимает, что (неправильное) "базовое значение" находится между новым минимумом и максимумом и изменяет "эффективное значение" обратно на него.

предполагая, что это действительно то, что происходит, приводит нас к следующему вопросу:
Почему вручную потянув ползунок (3.) не влияет на "базовое значение"?

1 ответов


я не уверен, что такое вариант использования, но этой проблемы можно было бы избежать, если бы вы использовали MVVM, и все было связано. Если это то, что вы делаете, и это просто еще один способ воспроизведения проблемы, хорошая находка.

посмотри Slider исходный код. Я не мог точно определить проблему, но у меня есть лучшее понимание того, что используется внутреннее значение.

добавить 2 кнопки, и изменить только минимум или максимум в обработчики событий для кнопки:

MyButton1.Click += (sender, e) =>
{
    MySlider.Minimum = 1.6;
};

MyButton2.Click += (sender, e) =>
{
    MySlider.Minimum = 0.5;
};

MyButton3.Click += (sender, e) =>
{
    MySlider.Maximum = 20;
};

MyButton4.Click += (sender, e) =>
{
    MySlider.Maximum = 8;
};

при запуске, нажмите кнопку MyButton1 и MyButton2:

Value changed: 1.6
Minimum changed: 1.6
Value changed: 1
Minimum changed: 0.5
Value changed: 1.6
Minimum changed: 1.6
Value changed: 1
Minimum changed: 0.5

вы можете видеть, внутренне, он сохраняет исходное начальное значение и восстанавливает его, когда диапазон способен показать его, он возвращает его к тому, что он был. Если вы измените только максимум при запуске (без перемещения ползунка),Value свойство не изменяется, потому что Value находится в текущем диапазоне:

Maximum changed: 8
Maximum changed: 20
Maximum changed: 8
Maximum changed: 20

однако, когда вы изменяете ползунок максимум до 20, затем измените Maximum = 8 и Minimum = 1.6, the Minimum теперь вне диапазона от (внутреннего)Value (1) и использует Maximum значение для него Value. Когда вы вырастите его снова, вы установите Minimum = 0.5 и Maximum = 20, и так как внутреннее значение = 1 и находится между 0.5 и 20, он устанавливает Value вернуться к 1.

я нашел один обходной путь, и это сброс внутреннего значения каждый раз, когда вы меняете диапазон. Чтобы сбросить, вы просто установите Slider.Value снова. Если вы сделаете это после изменения Maximum и Minimum это сохраняет значение в случае, если старое значение не находится в диапазоне нового ассортимента. Итак, принимая вашу первоначальную реализацию:

MyButton1.Click += (sender, e) =>
{
    MySlider.Minimum = 1.6;
    MySlider.Maximum = 8;
    MySlider.Value = MySlider.Value;
};

MyButton2.Click += (sender, e) =>
{
    MySlider.Minimum = 0.5;
    MySlider.Maximum = 20;
    MySlider.Value = MySlider.Value;
};

вывод (соответствует ожидаемому):

Value changed: 1
Minimum changed: 0.5
Maximum changed: 20
//Multiple times "Value changed"
Value changed: 20
Minimum changed: 1.6
Value changed: 8
Maximum changed: 8
Minimum changed: 0.5
Maximum changed: 20

редактировать

ссылка, которую вы опубликовали о принуждении стоимости, очень полезна. Я не уверен, что ваш вопрос будет считаться вторым вопросом, но я считаю, что нашел ответ.

при использовании Slider (технически Thumb С Track), что Thumb скольжение вокруг связано с Slider.Valueчерез Slider.UpdateValue вызывается. Этот вопрос сейчас, что SetCurrentValueInternal не является открытым исходным кодом: (мы можем заключить, что функция тайны не принуждает ValueProperty, вместо этого он только устанавливает базовое ("желаемое") значение. Попробуйте:

private void MyButton1_Click(object sender, RoutedEventArgs e)
{
    MySlider.Minimum = 1.6;
    MySlider.Maximum = 8;
    MySlider.CoerceValue(Slider.ValueProperty); //Set breakpoint, watch MySlider.Value before and after breakpoint.
}

делая выше вы увидите, что даже если Value будет идти от 20 до 8, второй вы фактически принуждаете ValueProperty, когда он падает до 8, значение затем изменится на 1.6. На самом деле, это то, что происходит, когда вы увеличиваете диапазон. После установки Max или Min вы увидите изменение значения, так как оно снова принуждает свойство value. Попробуйте перевернуть Max и Min:

private void MyButton2_Click(object sender, RoutedEventArgs e)
{
    MySlider.Maximum = 20; //Will change Value to 1.6 after executing
    MySlider.Minimum = 0.5; //Will change Value to 1 after executing
}

тем не менее, это заставляет вас думать, как он может помнить, чтобы вернуться к 1, когда это возможно, есть эффективные, базовые и начальные значения? Я не конечно.

на самом деле попробовать это.

  1. запустить приложение
  2. переместить большой палец полностью вправо (значение = 20)
  3. сокращение границ (значение = 8)
  4. возьмите большой палец и переместите его влево, затем полностью вправо (значение = 8)
  5. тогда растут границы

после шага 5 вы увидите, что значение по-прежнему равно 8. Вот как я узнал, что это как-то связано с этой тайной. функция.

Примечание: мой мозг снова мертв, поэтому, если это не ясно, Я извиняюсь, мне придется вернуться и отредактировать его.

Изменить 2

например, как SetCurrentValueInteral и SetValue как позвонить SetValueCommon. Разница в SetCurrentValueInternal повторно оценивает текущее значение с базовым значением, так как флаг принуждения установлен в true. (сохраняет значение в пределах Min / Max),ref.

через мои раскопки, я нашел, что SetCurrentValue и SetValue имеют два совершенно разных результата, а именно: указание принуждения (IsInternal кажется неиспользуемым в случае, когда свойство bound имеет тип value).

доказательство документация о:

"метод SetCurrentValue-это еще один способ задать свойство, но не в порядке приоритета. Вместо этого SetCurrentValue позволяет изменять значение свойства без перезаписи источника предыдущего значения. Вы можете использовать SetCurrentValue в любое время, когда вы хотите установить значение, не давая этому значению приоритет локального значения..."

С этим, как говорится, двигая Thumb на Slider не влияет на базовое значение, потому что он называет SetCurrentValueInternal.

изменение Value вручную изменяет базовое значение, потому что оно вызывает SetValue. The ValueProperty свойство зависимостей определяет, как принудительно использовать CoerceValueCallback как указано в последнем параграф здесь. Более подробно о том, что происходит в Slider.Value,

принуждение взаимодействует с базовым значением таким образом, что ограничения на принуждение применяются, поскольку эти ограничения существуют в то время, но базовое значение все еще сохраняется. Поэтому, если ограничения в принуждении позже будут сняты, принуждение вернет максимально возможное значение к этому базовому значению, и потенциально влияние принуждения на свойство прекратится, как только все ограничения сняты.

конечно, я читаю дальше,обратные вызовы свойств зависимостей и проверка-расширенные сценарии принуждения и обратного вызова и

например, в сценарии Min/Max / Current вы можете выбрать минимальную и максимальную настройки пользователя. Если это так, вам может потребоваться принудить, что максимум всегда больше минимума и наоборот. Но если это принуждение активно, и максимум принуждает Минимум, он оставляет ток в неразрешимом состоянии, потому что он зависит от обоих и ограничен диапазоном между значениями, который равен нулю. Затем, если скорректировать максимум или минимум, ток будет "следовать" за одним из значений, потому что желаемое значение тока все еще сохраняется и пытается достичь желаемого значения по мере ослабления ограничений.

в заключение, я думаю, что это как-разработанные в Slider потому что он закодирован таким образом, что Value должно быть всегда между Minimum и Maximum. The Value будет следовать Maximum свойство, и при снятии ограничений (текущее значение находится в диапазоне базового значения) свойство value вернется к исходному значению.

наконец, IMO WPF разработал слайдер таким образом, потому что WPF идет рука об руку с привязкой данных. Я предполагаю, что они разработаны в предположении, что разработчики в полной мере воспользуются связыванием данных и реализуют логику запретить использование недопустимых значений.