Datenbindungsgrundlagen und Datenkontexte falsch verstehen - eine lange Geschichte

Ich habe die Datenbindung in mehreren einfachen Situationen mit ziemlich gutem Erfolg verwendet. Normalerweise verwende ich nur INotifyPropertyChanged, um meinem Codebehind zu ermöglichen, die GUI-Werte auf dem Bildschirm zu ändern, anstatt Abhängigkeitseigenschaften für alles zu implementieren.

Ich spiele mit einem LED-Steuerelement, um mehr über die Datenbindung in Benutzersteuerelementen zu erfahren, und musste Abhängigkeitseigenschaften verwenden, da VS2008 mir sagte, dass ich dies tun musste. Meine Anwendung ist unkompliziert - ich habe ein Fenster, in dem mehrere LED-Steuerelemente mit einer Nummer darüber und optional einer nebeneinander angezeigt werden. Die LEDs sollten mit einer Standardfarbe definierbar sein und den Status ändern.

Ich habe zunächst eine LED-Steuerung geschrieben, die anscheinend einwandfrei funktioniert. Zuerst habe ich mit folgendem Code begonnen:

LED.xaml

<UserControl x:Class="LEDControl.LED"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="Auto" Width="Auto">

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>
        <!-- LED portion -->
        <Ellipse Grid.Column="0" Margin="3" Height="{Binding LEDSize}" Width="{Binding LEDSize}" Fill="{Binding LEDColor}" StrokeThickness="2" Stroke="DarkGray" />
        <Ellipse Grid.Column="0" Margin="3" Height="{Binding LEDSize}" Width="{Binding LEDSize}">
            <Ellipse.Fill>
                <RadialGradientBrush GradientOrigin="0.5,1.0">
                    <RadialGradientBrush.RelativeTransform>
                        <TransformGroup>
                            <ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="1.5" ScaleY="1.5"/>
                            <TranslateTransform X="0.02" Y="0.3"/>
                        </TransformGroup>
                    </RadialGradientBrush.RelativeTransform>
                    <GradientStop Offset="1" Color="#00000000"/>
                    <GradientStop Offset="0.4" Color="#FFFFFFFF"/>
                </RadialGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
        <!-- label -->
        <TextBlock Grid.Column="1" Margin="3" VerticalAlignment="Center" Text="{Binding LEDLabel}" />
    </Grid>
</UserControl>

Dies zeichnet eine LED ganz gut. Ich habe dann LEDSize, LEDLabel und LEDColor an die Ellipse-Eigenschaften gebunden, indem ich @ gesetzt habthis.DataContext = this wie ich es immer mache

LED.xaml.cs

/// <summary>
/// Interaction logic for LED.xaml
/// </summary>
public partial class LED : UserControl, INotifyPropertyChanged
{
    private Brush state_color_;
    public Brush LEDColor
    {
        get { return state_color_; }
        set { 
            state_color_ = value;
            OnPropertyChanged( "LEDColor");
        }
    }

    private int led_size_;
    public int LEDSize
    {
        get { return led_size_; }
        set {
            led_size_ = value;
            OnPropertyChanged( "LEDSize");
        }
    }

    private string led_label_;
    public string LEDLabel
    {
        get { return led_label_; }
        set {
            led_label_ = value;
            OnPropertyChanged( "LEDLabel");
        }
    }

    public LED()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged( string property_name)
    {
        if( PropertyChanged != null)
            PropertyChanged( this, new PropertyChangedEventArgs( property_name));
    }

    #endregion
}

An diesem Punkt kann ich die Eigenschaftswerte ändern und feststellen, dass sich Größe, Farbe und Bezeichnung der LED ändern. Groß

Ich möchte, dass das LED-Steuerelement in anderen Widgets, die ich im Laufe der Zeit schreibe, wiederverwendbar ist, und der nächste Schritt für mich bestand darin, ein anderes UserControl (in einer separaten Assembly) mit dem Namen @ zu erstelleIOView. IOView ist an dieser Stelle ziemlich einfach:

IOView.xaml

<UserControl x:Class="IOWidget.IOView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:led="clr-namespace:LEDControl;assembly=LEDControl"
    Height="Auto" Width="Auto">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" HorizontalAlignment="Center" Text="{Binding Path=Index}" />
        <led:LED Grid.Row="1" HorizontalContentAlignment="Center" HorizontalAlignment="Center" LEDSize="30" LEDColor="Green" LEDLabel="Test" />
    </Grid>
</UserControl>

Bitte beachten Sie, dass ich die LED-Eigenschaften in XAML zur Entwurfszeit ändern kann und alles wie erwartet funktioniert:

Ich habe dann blind versucht, LEDColor an meine IOView zu binden, und VS2008 hat mir freundlicherweise gesagt, "Für die Eigenschaft 'LEDColor' des Typs 'LED' kann keine 'Bindung' festgelegt werden. Eine 'Bindung' kann nur für eine DependencyProperty eines DependencyObject festgelegt werden." Hoppla! Ich hatte es nicht einmal bemerkt, da ich vorher noch keine eigenen GUI-Steuerelemente erstellt hatte. Seit derLEDColor ist bereits datengebunden an die Ellipse. Ich habe eine DependencyProperty namens Color hinzugefügt.

LED.xaml.cs

    public static DependencyProperty ColorProperty = DependencyProperty.Register( "Color", typeof(Brush), typeof(LED));
    public Brush Color
    {
        get { return (Brush)GetValue(ColorProperty); }
        set { 
            SetValue( ColorProperty, value);
            LEDColor = value;
        }
    }

Hinweis, dass ich die Eigenschaft @ gesetzt haLEDColor im Setter, da die Ellipse so weiß, welche Farbe sie haben soll.

Der nächste kleine Schritt bestand darin, die Farbe der LED in meinem IOView durch Binden an IOView.InputColor festzulegen:

IOView.xaml.cs:

/// <summary>
/// Interaction logic for IOView.xaml
/// </summary>
public partial class IOView : UserControl, INotifyPropertyChanged
{
    private Int32 index_;
    public Int32 Index
    {
        get { return index_; }
        set {
            index_ = value;
            OnPropertyChanged( "Index");
        }
    }

    private Brush color_;
    public Brush InputColor
    {
        get { return color_; }
        set {
            color_ = value;
            OnPropertyChanged( "InputColor");
        }
    }

    private Boolean state_;
    public Boolean State
    {
        get { return state_; }
        set {
            state_ = value;
            OnPropertyChanged( "State");
        }
    }

    public IOView()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged( string property_name)
    {
        if( PropertyChanged != null)
            PropertyChanged( this, new PropertyChangedEventArgs( property_name));
    }

    #endregion
}

und in IOView.xaml habe ich die LED folgendermaßen geändert:

<led:LED Grid.Row="1" HorizontalContentAlignment="Center" HorizontalAlignment="Center" LEDSize="30" Color="{Binding InputColor}" />

Aber es funktioniert aufgrund des folgenden Fehlers im Ausgabefenster nicht:

BindingExpression path error: Die Eigenschaft 'InputColor' wurde für 'object' '' LED '(Name =' ')' nicht gefunden. BindingExpression: Path = InputColor; DataItem = 'LED' (Name = ''); Zielelement ist 'LED' (Name = ''); Zieleigenschaft ist 'Color' (Typ 'Brush')

Hmm ... also aus irgendeinem Grund ist meine Datenbindung durcheinander. Ich kann die LED mit Datenbindung für sich arbeiten lassen, aber wenn ich sie in ein anderes Steuerelement einhülle und den Datenkontext einstelle, funktioniert sie nicht mehr. Ich bin mir nicht sicher, was ich an dieser Stelle versuchen soll.

Ich würde gerne eine möglichst ausführliche Antwort bekommen. Ich weiß, dass ich gerade eine CheckBox hätte überarbeiten können, um die gleichen Ergebnisse zu erzielen, aber dies ist ein Experiment für mich und ich versuche zu verstehen, wie man Daten an die Nachkommen der Steuerelemente bindet.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage