none
Binding an Height von UserControl RRS feed

  • Frage

  • Ich versuche erstmals die Height eines UserControls an diejenige eines anderen zu binden, und habe dazu folgendes Storyboard:

    <Storyboard x:Key="DownsizeControl" Completed="DownsizeStoryboard_Completed" >
          	<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(FrameworkElement.Height)" Storyboard.TargetName="ThisUser_Control">
          		<EasingDoubleKeyFrame KeyTime="0:0:1" Value="{Binding Path=mUserControl.Height}"}"/>
          	</DoubleAnimationUsingKeyFrames>
        </Storyboard>

    Nun funktioniert das irgendwie nicht. Die neue Height wird immer 0. Wenn ich statt des Bindings Value=100 eingebe, funktioniert es.

    mUserControl wird im Code-behind bei der Deklaration initialisiert.

    Was mache ich falsch?

    Mittwoch, 16. März 2011 13:50

Antworten

  •  <EasingDoubleKeyFrame KeyTime="0:0:1" Value="{Binding Path=mUserControl.Height}"}"/> </DoubleAnimationUsingKeyFrames> </Storyboard>

    mUserControl wird im Code-behind bei der Deklaration initialisiert.

    Was mache ich falsch?

    mUserControl müsste eine Property sein und natürlich im DataContext.
    So wie Du es beschreibst, ist mUserControl aber ein Feld:

    UserControl mUserControl = new MyUserControl();

    An Felder kann WPF nicht binden.
    Du solltest es zur Property machen etwa:

    private UserControl mUserControl;
    public UserControl MyUserControl
    {
        get {if (null == mUserControl)
            mUserControl = new MyUserControl();
         return mUserControl ;
    }}


    Und dann an MyUserControl.ActualHeight binden.
    ActualHeight hat immer einen Wert und zwar den tatsächlichen, Height kann auch NaN
    sein, wenn die Höhe nicht direkt zugewiesen wird sondern über Dependencies ermittelt wird:

    <EasingDoubleKeyFrame KeyTime="0:0:1" Value="{Binding Path=MyUserControl.ActualHeight}"}"/>


    Christoph
    • Als Antwort markiert Code4132 Dienstag, 22. März 2011 15:29
    • Tag als Antwort aufgehoben Code4132 Dienstag, 22. März 2011 15:30
    • Als Antwort markiert Code4132 Mittwoch, 23. März 2011 08:58
    Donnerstag, 17. März 2011 01:48
  • Die Höhe wird direkt zugewiesen im XAML-Code des Controls:

    <UserControl (...)  Width="492" Height="145" (...)>

     

    OK dann sollte auch MyUserControl.Height prinzipiell gehen.
    Wird MyUserControl eigentlich irgendwie angezeigt und eingebunden -
    als "Child" eines Grids oder Panels oder irgendwo als Content eines anderen Controls -
    oder existiert das sozusagen nur "frei schwebend" im Code?

    Wenn MyUserControl nur mit new konstruiert aber nicht als Kindelement eingefügt wird,
    funktioniert das Binding nur mit MyUserControl.Height und nicht wie üblicherwiese
    mit MyUserControl.ActualHeight. ActualHeight ist dann immer 0 weil MyUserControl gar nicht gerendert
    und gemeasuret wird. Height wäre aber dennoch 145 weil es eben so im XAMl steht (woran man sieht, dass
    Height eine Vorgabe ist und nicht die tasächliche Höhe)

    Auch habe ich ein Property implementiert, einfach leicht anders:

     

    UserControl mUserControl = new MyUserControl();

    public UserControl MyUserControl
    {
        get { return mUserControl ;}

        set { mUserControl = value; }

    }

    Die andere Frage ist der DataContext, ist der irgendwo definiert?
    Im Constructor oder irgendwo sollte etwas wie

       this.DataContext = this;

    stehen bzw sinngleiches in XAML.
    Ohne DataContext kann die Property 'MyUserControl' nicht gebunden werden.
    Das wäre eine Erklärung, warum "es nicht funktioniert".

    Christoph
    • Als Antwort markiert Code4132 Mittwoch, 23. März 2011 08:58
    Dienstag, 22. März 2011 22:20
  • Also den DataContext muss man prinzipiell schon immer setzen, wenn man
    mit Bindings arbeitet. Er muss aber nicht immer auf "this" gesetzt werden.

    Beim DataContext geht es eigentlich nur darum, wo die Bindings gesucht werden.
    Wenn Du in XAML schreibst

    <TextBox Text="{Binding Path=MyTextProperty}" />

    sucht WPF eine Eigenschaft namens "MyTextProperty" im aktuellen DataContext.
    Das ist sowas wie der Namespace für das Binding.
    Wenn der DataContext nicht gesetzt ist, oder keine Eigenschaft mit dem gesuchten Namen
    gefunden wird, klappt das Binding nicht.
    Ohne ein explizites Setzen des DataContext ist er null (hat also keinen verwendbaren Defaultwert).
    Da in deinem Fall die Eigenschaft "MyUserControl" in der Codebehind-Klasse definiert ist
    muss der DataContext auf "this" gesetzt werden.
    Prinzipiell kann er aber auf jedes beliebige .NET-Objekt gesetzt werden, z.B. auch auf
    den Rückgabewert einer LINQ-Query oder ein ViewModel-Objekt oder einfach eine beliebige Variable.

    Zu beachten ist, dass der DataContext von den übergeordneten an die untegreodeneten Elemente "vererebt"
    wird,  wenn Du also z.B. als Wurzel-Element ein Window hast, reicht es den DataContext
    für das Window zu setzen, er ist dann überall gültig.

    Eine Besonderheit sind in dem Zusammenhang "ItemControls" also ListBox, ListView, ComboBox
    und DataGrid und ein paar andere, hier wird die Datenquelle meist via der ItemsSource-Property
    gesetzt, die Bindings im ItemTemplate bzw CellTemplate beziehen sich dann auf das einzelne Element
    der ItemsSource

    Hoffe das hilft etwas weiter,
    Christoph






    • Als Antwort markiert Code4132 Mittwoch, 23. März 2011 08:58
    Mittwoch, 23. März 2011 08:44

Alle Antworten

  • Teste mal die ActualHeight Eigenschaft.
    Mittwoch, 16. März 2011 15:14
    Beantworter
  • Leider bringt das nichts. Es kommt auch in beiden Fällen keine Fehlermeldung in der Ausgabe.
    Mittwoch, 16. März 2011 15:35
  •  <EasingDoubleKeyFrame KeyTime="0:0:1" Value="{Binding Path=mUserControl.Height}"}"/> </DoubleAnimationUsingKeyFrames> </Storyboard>

    mUserControl wird im Code-behind bei der Deklaration initialisiert.

    Was mache ich falsch?

    mUserControl müsste eine Property sein und natürlich im DataContext.
    So wie Du es beschreibst, ist mUserControl aber ein Feld:

    UserControl mUserControl = new MyUserControl();

    An Felder kann WPF nicht binden.
    Du solltest es zur Property machen etwa:

    private UserControl mUserControl;
    public UserControl MyUserControl
    {
        get {if (null == mUserControl)
            mUserControl = new MyUserControl();
         return mUserControl ;
    }}


    Und dann an MyUserControl.ActualHeight binden.
    ActualHeight hat immer einen Wert und zwar den tatsächlichen, Height kann auch NaN
    sein, wenn die Höhe nicht direkt zugewiesen wird sondern über Dependencies ermittelt wird:

    <EasingDoubleKeyFrame KeyTime="0:0:1" Value="{Binding Path=MyUserControl.ActualHeight}"}"/>


    Christoph
    • Als Antwort markiert Code4132 Dienstag, 22. März 2011 15:29
    • Tag als Antwort aufgehoben Code4132 Dienstag, 22. März 2011 15:30
    • Als Antwort markiert Code4132 Mittwoch, 23. März 2011 08:58
    Donnerstag, 17. März 2011 01:48
  • Die Höhe wird direkt zugewiesen im XAML-Code des Controls:

    <UserControl (...)  Width="492" Height="145" (...)>

     

    Auch habe ich ein Property implementiert, einfach leicht anders:

     

    UserControl mUserControl = new MyUserControl();

    public UserControl MyUserControl
    {
        get { return mUserControl ;}

        set { mUserControl = value; }

    }

    Dienstag, 22. März 2011 15:33
  • Die Höhe wird direkt zugewiesen im XAML-Code des Controls:

    <UserControl (...)  Width="492" Height="145" (...)>

     

    OK dann sollte auch MyUserControl.Height prinzipiell gehen.
    Wird MyUserControl eigentlich irgendwie angezeigt und eingebunden -
    als "Child" eines Grids oder Panels oder irgendwo als Content eines anderen Controls -
    oder existiert das sozusagen nur "frei schwebend" im Code?

    Wenn MyUserControl nur mit new konstruiert aber nicht als Kindelement eingefügt wird,
    funktioniert das Binding nur mit MyUserControl.Height und nicht wie üblicherwiese
    mit MyUserControl.ActualHeight. ActualHeight ist dann immer 0 weil MyUserControl gar nicht gerendert
    und gemeasuret wird. Height wäre aber dennoch 145 weil es eben so im XAMl steht (woran man sieht, dass
    Height eine Vorgabe ist und nicht die tasächliche Höhe)

    Auch habe ich ein Property implementiert, einfach leicht anders:

     

    UserControl mUserControl = new MyUserControl();

    public UserControl MyUserControl
    {
        get { return mUserControl ;}

        set { mUserControl = value; }

    }

    Die andere Frage ist der DataContext, ist der irgendwo definiert?
    Im Constructor oder irgendwo sollte etwas wie

       this.DataContext = this;

    stehen bzw sinngleiches in XAML.
    Ohne DataContext kann die Property 'MyUserControl' nicht gebunden werden.
    Das wäre eine Erklärung, warum "es nicht funktioniert".

    Christoph
    • Als Antwort markiert Code4132 Mittwoch, 23. März 2011 08:58
    Dienstag, 22. März 2011 22:20
  • Vielen Dank für die ausführliche Antwort!
    Tatsächlich ist der DataContext (noch) nicht gebunden.

    Ebenfalls eine Lösung des Problems, könnte die Einbindung des UserControls sein.

    Im Storyboard verkleinere ich zuerst das Control, in welchem das UserControl eingebunden wird, füge das UserControl aber erst nachher ein.


    Ich werde die diversen Punkte überprüfen.

    Mittwoch, 23. März 2011 07:54
  • Nun, ich glaube ich habe das Problem gefunden:

    Ich binde das UserControl nicht eigentlich in das übergeordnete UserControl ein, sondern füge es als Item in eine ListBox ein. Wahrscheinlich verursacht dies die Probleme (obwohl nun sämtliche oben genannte Punkte berücksichtigt sind).

     

    Eine Frage habe ich noch zum DataContext. Weshalb muss man den auf diese Weise (this.DataContext=this) setzen? Muss man das immer tun bei UserControls?

    Mittwoch, 23. März 2011 08:14
  • Also den DataContext muss man prinzipiell schon immer setzen, wenn man
    mit Bindings arbeitet. Er muss aber nicht immer auf "this" gesetzt werden.

    Beim DataContext geht es eigentlich nur darum, wo die Bindings gesucht werden.
    Wenn Du in XAML schreibst

    <TextBox Text="{Binding Path=MyTextProperty}" />

    sucht WPF eine Eigenschaft namens "MyTextProperty" im aktuellen DataContext.
    Das ist sowas wie der Namespace für das Binding.
    Wenn der DataContext nicht gesetzt ist, oder keine Eigenschaft mit dem gesuchten Namen
    gefunden wird, klappt das Binding nicht.
    Ohne ein explizites Setzen des DataContext ist er null (hat also keinen verwendbaren Defaultwert).
    Da in deinem Fall die Eigenschaft "MyUserControl" in der Codebehind-Klasse definiert ist
    muss der DataContext auf "this" gesetzt werden.
    Prinzipiell kann er aber auf jedes beliebige .NET-Objekt gesetzt werden, z.B. auch auf
    den Rückgabewert einer LINQ-Query oder ein ViewModel-Objekt oder einfach eine beliebige Variable.

    Zu beachten ist, dass der DataContext von den übergeordneten an die untegreodeneten Elemente "vererebt"
    wird,  wenn Du also z.B. als Wurzel-Element ein Window hast, reicht es den DataContext
    für das Window zu setzen, er ist dann überall gültig.

    Eine Besonderheit sind in dem Zusammenhang "ItemControls" also ListBox, ListView, ComboBox
    und DataGrid und ein paar andere, hier wird die Datenquelle meist via der ItemsSource-Property
    gesetzt, die Bindings im ItemTemplate bzw CellTemplate beziehen sich dann auf das einzelne Element
    der ItemsSource

    Hoffe das hilft etwas weiter,
    Christoph






    • Als Antwort markiert Code4132 Mittwoch, 23. März 2011 08:58
    Mittwoch, 23. März 2011 08:44