Kurz gesagt:
Unter bestimmten Umständen wird die Einstellung von Slider.Minimum Slider.Value anpassen, obwohl der aktuelle Wert größer als das neue Minimum ist.
Code: (sollte reproduzierbar sein)
MainWindow.xaml:
MainWindow.xaml.cs:
%Vor% Zu reproduzierende Schritte:
1. Im Debug-Modus ausführen.
2. Bewegen Sie den Schieberegler ganz nach rechts.
3. Drücken Sie die Schaltfläche "Schrumpfgrenzen".
4. Drücken Sie die Schaltfläche "Grenzen wachsen".
Erwartete Ausgabe:
%Vor%Slider bleibt bei 8.
Tatsächliche Ausgabe:
%Vor% (Beachten Sie den zusätzlichen "Wert geändert: 1")
Slider springt auf 1.
Hinweis:
Binding Slider.Value OneWayToSource (oder TwoWay) zu einer doppelten Eigenschaft, behebt das Problem.
Frage:
Warum passiert das?
Theorie:
Ich bin mir ziemlich sicher, dass es etwas mit Value Coercion zu tun hat . Es scheint folgendes zu passieren:
1. Wert auf 1 setzen programmatic setzt Wert "Grundwert". Es liegt zwischen den Standardwerten für Minimum und Maximum, also ist der "effektive Wert" genau gleich.
2. Die Einstellung von Minimum und Maximum ändert nichts, weil der Wert immer noch dazwischen liegt.
3. Manuelles Verschieben des Schiebereglers nach rechts ändert scheinbar den "effektiven Wert", aber aus irgendeinem seltsamen Grund nicht den "Basiswert".
4. Durch erneutes Erhöhen der Grenzen wird der Zwangsrückruf aufgerufen, der erkennt, dass der (falsche) "Basiswert" zwischen dem neuen Minimum und Maximum liegt und den "effektiven Wert" wieder auf diesen Wert zurückstellt.
Wenn wir annehmen, dass dies tatsächlich geschieht, führt uns das zu folgender Frage:
Warum zieht der Slider (3.) den "Basiswert" nicht manuell an?
Ich bin mir nicht sicher, was der Anwendungsfall ist, aber dieses Problem könnte wahrscheinlich vermieden werden, wenn Sie MVVM verwenden und alles gebunden ist. Wenn es das ist, was du tust, und das ist nur eine andere Möglichkeit, das Problem zu reproduzieren, einen guten Fund.
Sehen Sie sich den Slider
Quellcode an . Ich konnte das Problem nicht genau bestimmen, aber ich verstehe besser, dass ein interner Wert verwendet wird.
Fügen Sie 2 Schaltflächen hinzu und ändern Sie Minimum oder Maximum nur in den Ereignishandlern für alle Schaltflächen:
%Vor% Klicken Sie beim Start auf MyButton1
und MyButton2
:
Sie können sehen, intern speichert es den ursprünglichen Startwert und stellt es wieder her, wenn der Bereich es anzeigen kann, es setzt es zurück, was es war. Wenn Sie beim Start nur das Maximum ändern (ohne Schieberegler zu verschieben), wird die Value
-Eigenschaft nicht geändert, da Value
im aktuellen Bereich liegt:
Wenn Sie jedoch das Schiebereglermaximum auf 20 ändern, dann ändern Sie Maximum = 8
und Minimum = 1.6
, Minimum
ist jetzt außerhalb des Bereichs von (intern) Value
(1) und verwendet Maximum
Wert für es ist Value
. Wenn Sie es erneut anwachsen, legen Sie Minimum = 0.5
und Maximum = 20
fest, und da der interne Wert = 1 ist und zwischen 0,5 und 20 liegt, wird Value
wieder auf 1 gesetzt.
Ich habe eine Problemumgehung gefunden, und das setzt den internen Wert jedes Mal zurück, wenn Sie den Bereich ändern. Zum Zurücksetzen legen Sie einfach Slider.Value
erneut fest. Wenn Sie dies tun, nachdem Sie Maximum
und Minimum
geändert haben, wird der Wert beibehalten, falls der alte Wert nicht im Bereich des neuen Bereichs liegt. Also nehmen Sie Ihre ursprüngliche Implementierung:
Ausgabe (entspricht Ihren Erwartungen):
%Vor%Der Link zum Wertausgleich ist sehr hilfreich. Ich bin mir nicht sicher, ob deine Frage eine zweite Frage wäre, aber ich glaube, ich habe die Antwort gefunden.
Wenn Sie das Slider
(technisch das Thumb
mit dem Track
) verwenden, stellen Sie sicher, dass das Thumb
riding around an das Slider.Value
über Slider.UpdateValue
wird aufgerufen. Dieses Problem ist nun, dass SetCurrentValueInternal
nicht Open Source ist :( Was wir daraus schließen können ist, dass die Mystery-Funktion ValueProperty
nicht erzwingt, sondern nur den Grundwert ("gewünscht") setzt. Probieren Sie:
Wenn Sie so vorgehen, sehen Sie, dass Value
zwar von 20 auf 8 abfällt, die Sekunde, in der Sie ValueProperty tatsächlich erzwingen, wenn sie auf 8 abfällt, sich aber in 1.6
ändert. Genau das passiert, wenn Sie die Reichweite erhöhen. Nachdem Sie Max oder Min festgelegt haben, wird die Wertänderung angezeigt, da die Werteigenschaft erneut erzwungen wird. Versuchen Sie, Max und Min zu spiegeln:
Das lässt dich immer noch denken, wie kann es daran denken, wenn möglich wieder zu 1 zurückzukehren, gibt es eine effektive Basis und erste Werte? Ich bin mir nicht sicher.
Tatsächlich versuchen Sie das.
Nach Schritt 5 werden Sie feststellen, dass Value immer noch gleich 8 ist. So habe ich herausgefunden, dass es etwas mit dieser Mystery-Funktion zu tun hat.
Hinweis: Mein Gehirn ist wieder tot. Wenn das nicht klar ist, entschuldige ich mich, ich muss zurückkommen und es bearbeiten.
Zum einen rufen sowohl SetCurrentValueInteral
als auch SetValue
beide SetValueCommon
auf. Die Differenz ist SetCurrentValueInternal
wertet den aktuellen Wert mit dem Basiswert neu aus, da das Coerce-Flag auf "True" gesetzt ist. (behält den Wert von Min / Max bei), ref .
Durch meine Suche habe ich herausgefunden, dass SetCurrentValue
und SetValue
zwei völlig verschiedene Ergebnisse haben, und zwar: die Nötigung angeben ( IsInternal
scheint nicht verwendet zu werden, wenn die gebundene Eigenschaft vom Werttyp ist) .
Mein Beweis ist Dokumentation , die besagt :
"Die SetCurrentValue-Methode stellt eine weitere Möglichkeit zum Festlegen einer Eigenschaft dar, liegt jedoch nicht in der Rangfolge. Stattdessen können Sie mit SetCurrentValue den Wert einer Eigenschaft ändern, ohne die Quelle eines vorherigen Werts zu überschreiben. Sie können SetCurrentValue verwenden Wann immer Sie einen Wert festlegen möchten, ohne diesem Wert den Vorrang eines lokalen Wertes zu geben ... "
Damit ändert sich das Verschieben von Thumb
von Slider
nicht auf den Basiswert, weil es SetCurrentValueInternal
aufruft.
Wenn Sie Value
manuell ändern, ändert sich der Basiswert außerdem, weil er SetValue
aufruft. Die ValueProperty
-Abhängigkeitseigenschaft definiert, wie mithilfe des CoerceValueCallback
, wie im letzten Absatz hier Im Detail, was in Slider.Value
passiert,
Die Koerzition interagiert so mit dem Basiswert, dass die Zwangsbedingungen angewendet werden, da diese Bedingungen zu der Zeit existieren, aber der Basiswert bleibt erhalten. Wenn Zwangsbedingungen später aufgehoben werden, wird der Zwang den nächstmöglichen Wert zu diesem Basiswert zurückgeben, und möglicherweise wird der Zwangseinfluss auf eine Eigenschaft aufhören, sobald alle Beschränkungen aufgehoben sind.
Natürlich lese ich weiter Abhängigkeits-Callbacks und Validierung - Erweiterte Zwangs-und Callback-Szenarien und gefunden:
Zum Beispiel könnten Sie im Min / Max / Current-Szenario wählen, ob Minimum und Maximum benutzerdefinierbar sein sollen. Wenn dies der Fall ist, müssen Sie möglicherweise erzwingen, dass Maximum immer größer als Minimum ist und umgekehrt. Wenn dieser Zwang aktiv ist und Maximum auf Minimum reduziert wird, bleibt Current in einem nicht setzbaren Zustand, da er von beiden abhängig ist und auf den Bereich zwischen den Werten beschränkt ist, der null ist. Dann, wenn Maximum oder Minimum eingestellt sind, scheint Current einem der Werte zu folgen, weil der gewünschte Wert von Current immer noch gespeichert wird und versucht, den gewünschten Wert zu erreichen, wenn die Beschränkungen gelockert werden.
Zusammenfassend denke ich, dass dies in Slider
so gestaltet ist, weil es so codiert ist, dass Value
immer zwischen Minimum
und Maximum
liegen muss. Die Value
folgen der Eigenschaft Maximum
, und wenn Bedingungen aufgehoben werden (der aktuelle Wert liegt im Bereich des Basiswerts), wird die Eigenschaft value auf ihren ursprünglichen Wert zurückgesetzt.
Schließlich hat IMO WPF den Schieberegler so entworfen, weil WPF Hand in Hand mit der Datenbindung geht. Ich nehme an, dass sie unter der Annahme entworfen wurden, dass Entwickler die Datenbindung voll ausnutzen würden, und würden die Logik implementieren, um zu verhindern, dass ungültige Werte verwendet werden.