2010-07-24 8 views
9

Ich schreibe eine echte NumericUpDown/Spinner Kontrolle als Übung, um benutzerdefinierte Steuerelement Authoring zu lernen. Ich habe das meiste Verhalten, nach dem ich suche, einschließlich angemessenem Zwang. Einer meiner Tests hat jedoch einen Fehler aufgedeckt.Warum sieht meine Datenbindung den tatsächlichen Wert anstelle des erzwungenen Werts?

Meine Kontrolle hat 3 Abhängigkeitseigenschaften: Value, MaximumValue und MinimumValue. Ich verwende Zwang, um sicherzustellen, dass Value zwischen dem Minimum und dem Maximum einschließlich bleibt. Zum Beispiel:

Mein Test ist nur um sicherzustellen, dass die Datenbindung funktioniert, wie ich es erwarte. Ich habe eine Standard wpf Windows-Anwendung und warf in der folgenden XAML:

<Window x:Class="WpfApplication.MainWindow" x:Name="This" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:nud="clr-namespace:WpfCustomControlLibrary;assembly=WpfCustomControlLibrary" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition /> 
      <RowDefinition /> 
     </Grid.RowDefinitions> 
     <nud:NumericUpDown Value="{Binding ElementName=This, Path=NumberValue}"/> 
     <TextBox Grid.Row="1" Text="{Binding ElementName=This, Path=NumberValue, Mode=OneWay}" /> 
    </Grid> 
</Window> 

mit sehr einfachen Code-Behind:

public partial class MainWindow : Window 
{ 
    public int NumberValue 
    { 
     get { return (int)GetValue(NumberValueProperty); } 
     set { SetCurrentValue(NumberValueProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for NumberValue. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty NumberValueProperty = 
     DependencyProperty.Register("NumberValue", typeof(int), typeof(MainWindow), new UIPropertyMetadata(0));  

    public MainWindow() 
    { 
     InitializeComponent(); 
    } 
} 

(Ich bin das XAML für die Kontrolle der Darstellung weggelassen)

Jetzt Wenn ich das ausführe, sehe ich den Wert aus dem NumericUpDown entsprechend in das Textfeld, aber wenn ich einen Wert außerhalb des Bereichs eintippe, wird der Wert außerhalb des Bereichs im Testtextfeld angezeigt, während NumericUpDown den korrekten Wert anzeigt.

Wie sollen die erzwungenen Werte wirken? Es ist gut, dass es in der UI erzwungen wird, aber ich erwartete, dass der erzwungene Wert auch durch die Datenbindung läuft.

+0

Scheint, dass die Bindung Val nicht unterstützt. Hast du den Diff-Modus in TextBox ausprobiert? –

Antwort

9

Wow, das ist überraschend. Wenn Sie einen Wert für eine Abhängigkeitseigenschaft festlegen, werden die Bindungsausdrücke aktualisiert, bevor die Wertabhängigkeit ausgeführt wird.

Wenn Sie sich DependencyObject.SetValueCommon in Reflector ansehen, können Sie den Aufruf von Expression.SetValue auf halbem Weg durch die Methode sehen. Der Aufruf von UpdateEffectiveValue, der Ihr CoerceValueCallback aufruft, befindet sich am Ende, nachdem die Bindung bereits aktualisiert wurde.

Sie können dies auch auf Framework-Klassen sehen. Von einer neuen WPF-Anwendung, fügen Sie den folgenden XAML:

<StackPanel> 
    <Slider Name="Slider" Minimum="10" Maximum="20" Value="{Binding Value, 
     RelativeSource={RelativeSource AncestorType=Window}}"/> 
    <Button Click="SetInvalid_Click">Set Invalid</Button> 
</StackPanel> 

und den folgenden Code:

private void SetInvalid_Click(object sender, RoutedEventArgs e) 
{ 
    var before = this.Value; 
    var sliderBefore = Slider.Value; 
    Slider.Value = -1; 
    var after = this.Value; 
    var sliderAfter = Slider.Value; 
    MessageBox.Show(string.Format("Value changed from {0} to {1}; " + 
     "Slider changed from {2} to {3}", 
     before, after, sliderBefore, sliderAfter)); 
} 

public int Value { get; set; } 

Wenn Sie den Slider und klicken Sie dann auf die Schaltfläche ziehen, wird eine Meldung wie „Wert erhalten geändert von 11 zu -1; Slider wurde von 11 auf 10 "geändert.

+1

Interessant, so ist das erwartete Verhalten. Ich nehme an, mein nächster Schritt ist, herauszufinden, wie man eine bindbare 'ActualValue'-Eigenschaft liefert, die angibt, was tatsächlich in NumericUpDown angezeigt wird. Ist dir ein besserer Weg bekannt, als den Zwang in einem Property-geänderten Handler neu zu bewerten und dort 'ActualValue' zu setzen? –

+0

Ich denke, du verdienst mehr als eine Erwiderung für die Antwort. –

-1

Sie zwingen v, die ein int und als solcher ein Werttyp ist. Es wird daher auf dem Stapel gespeichert. Es ist in keiner Weise mit baseValue verbunden. Wenn Sie also v ändern, wird baseValue nicht geändert.

Die gleiche Logik gilt für baseValue. Es wird als Wert übergeben (nicht als Referenz), also ändert es nicht den tatsächlichen Parameter.

v zurückgegeben und wird eindeutig verwendet, um die Benutzeroberfläche zu aktualisieren.

Sie können untersuchen, ob der Datentyp der Eigenschaft in einen Referenztyp geändert werden soll. Dann wird es als Referenz weitergegeben und alle vorgenommenen Änderungen werden zur Quelle zurückreflektiert. Angenommen, der Datenbindungsprozess erstellt keine Kopie.

+0

Wird es nicht eingerahmt, wenn es als Objekt von der Zwangsmethode zurückgegeben wird? –

1

Eine neue Antwort für eine alte Frage: :-)

In der Registrierung der ValueProperty eine FrameworkPropertyMetadata Instanz verwendet wird. Setzen Sie die Eigenschaft UpdateSourceTrigger dieser Instanz auf Explicit. Dies kann in einer Konstruktorüberladung erfolgen.

public static readonly DependencyProperty ValueProperty = 
    DependencyProperty.Register("Value", typeof(int), typeof(NumericUpDown), 
    new FrameworkPropertyMetadata(
     0, 
     FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, 
     HandleValueChanged, 
     HandleCoerceValue, 
     false 
     UpdateSourceTrigger.Explicit)); 

Nun ist die Bindung Quelle des ValueProperty nicht automatisch auf PropertyChanged aktualisiert werden. Führen Sie die Aktualisierung manuell in Ihrer HandleValueChanged Methode aus (siehe Code oben). Diese Methode wird nur bei "echten" Änderungen der Eigenschaft aufgerufen, nachdem die Coerce-Methode aufgerufen wurde.

Sie können es auf diese Weise tun:

static void HandleValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args) 
{ 
    NumericUpDown nud = obj as NumericUpDown; 
    if (nud == null) 
     return; 

    BindingExpression be = nud.GetBindingExpression(NumericUpDown.ValueProperty); 
    if(be != null) 
     be.UpdateSource(); 
} 

Auf diese Weise können Sie vermeiden, Ihre Bindungen mit nicht dazu gezwungen Werte Ihrer DependencyProperty zu aktualisieren.

+0

Gute Problemumgehung. :) –

Verwandte Themen