Ne0
Ne0

Reputation: 2786

Windows Store App - XAML - Bind two values for Height Property

I want to bind the height of a control the sum of two other heights, so the UI looks nice various screen sizes.

<GridView
    AutomationProperties.AutomationId="ItemDetails"
    ItemsSource="{Binding data}"
    IsSwipeEnabled="False"
    SelectionMode="None" Height="{Binding Height, (ElementName=item - ElementName=itemTitle)}" >
    <GridView.ItemTemplate>
        <DataTemplate>
            <dll:TaskItemControl/>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

The above XAML is invalid, but it demonstrates what I want to do. I have two elements, item and itemTitle. item is a ScrollView that gets set to the height of the screen and I want the GridView to be the same height as the ScrollView minus the height of the itemTitle.

Is there a way to do this in XAML?

Note: The reasons for doing this are beyond the scope of this question. So please don't comment about restricting the height of a control within a ScrollView.

Upvotes: 0

Views: 247

Answers (1)

Justin XL
Justin XL

Reputation: 39006

This can be easily done in code behind by subscribing the SizeChanged events of the two elements and update the Height of the GridView whenever the handlers are called.

But you want a pure XAML solution, and this is where Behaviors come into play.

Use a Behavior.

First you need to add Blend SDK reference to your project. enter image description here

Then you need to create a new class that implements IBehavior. This class needs three dependency properties just to reference the GridView, the item and the itemTitle. So you can subscribe to their SizeChanged events and calulate the Height accordingly.

I choose to attach this behavior to a top level Panel (most likely your LayoutRoot Grid) because I want to ensure that all the elements under it are rendered properly inside its Loaded event handler.

The full Behavior class would look something like this -

public class HeightBehavior : DependencyObject, IBehavior
{
    public GridView GridView
    {
        get { return (GridView)GetValue(GridViewProperty); }
        set { SetValue(GridViewProperty, value); }
    }

public static readonly DependencyProperty GridViewProperty =
    DependencyProperty.Register("GridView", typeof(GridView), typeof(HeightBehavior), new PropertyMetadata(null));

public FrameworkElement FirstItem
{
    get { return (FrameworkElement)GetValue(FirstItemProperty); }
    set { SetValue(FirstItemProperty, value); }
}

public static readonly DependencyProperty FirstItemProperty =
    DependencyProperty.Register("FirstItem", typeof(FrameworkElement), typeof(HeightBehavior), new PropertyMetadata(null));

public FrameworkElement SecondItem
{
    get { return (FrameworkElement)GetValue(SecondItemProperty); }
    set { SetValue(SecondItemProperty, value); }
}

public static readonly DependencyProperty SecondItemProperty =
    DependencyProperty.Register("SecondItem", typeof(FrameworkElement), typeof(HeightBehavior), new PropertyMetadata(null));

public DependencyObject AssociatedObject { get; set; }

public void Attach(DependencyObject associatedObject)
{
    this.AssociatedObject = associatedObject;

    var control = (Panel)this.AssociatedObject;
    control.Loaded += AssociatedObject_Loaded;
}

private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
    this.FirstItem.SizeChanged += FirstItem_SizeChanged;
    this.SecondItem.SizeChanged += SecondItem_SizeChanged;

    // force to re-calculate the Height
    this.FirstItem.Width += 0.5;
}

private void FirstItem_SizeChanged(object sender, SizeChangedEventArgs e)
{
    this.SetAssociatedObjectsHeight();
}

private void SecondItem_SizeChanged(object sender, SizeChangedEventArgs e)
{
    this.SetAssociatedObjectsHeight();
}

private void SetAssociatedObjectsHeight()
{
    this.GridView.Height = this.FirstItem.ActualHeight - this.SecondItem.ActualHeight;
}

public void Detach()
{
    this.FirstItem.SizeChanged -= FirstItem_SizeChanged;
    this.SecondItem.SizeChanged -= SecondItem_SizeChanged;

    var control = (Panel)this.AssociatedObject;
    control.Loaded -= AssociatedObject_Loaded;
}

}

Then in my XAML, I attach it to my top level Grid, like this.

<Grid x:Name="LayoutRoot">
        <Interactivity:Interaction.Behaviors>
            <local:HeightBehavior GridView="{Binding ElementName=itemGridView}" FirstItem="{Binding ElementName=item}" SecondItem="{Binding ElementName=itemTitle}"/>
        </Interactivity:Interaction.Behaviors>

Hope this helps.

Upvotes: 3

Related Questions