R4cOOn
R4cOOn

Reputation: 2575

Dynamically set the Popup position/placement

The Popup control is left-aligned by default. Its left edge is aligned with the left edge of its parent.

What I would like to do is to have the control right-aligned so that its right edge is aligned with the right edge of its container.

I want this to be dynamic because I dynamically bind the data and I don't know how big the popup will be.

I tried playing with the Opened, SizeChanged and Loaded event to get the Child's width and set it to the HorizontalOffset but there seems to be timing issues. Basically it works fine the first time the control is loaded and then never after (the HorizontalOffset is set to 0).

Is this a bug? Am I doing it the wrong way?

EDIT I got it working. It seems that there are timing issues. If I hook up the Opened event and set the HorizontalOffset asynchronously using the Dispatcher, then it works :(

    private static void OnPopupOpened(object sender, System.EventArgs e)
    {
        var popup = (Popup)sender;
        popup.Dispatcher.BeginInvoke(() => popup.HorizontalOffset = -popup.ActualWidth);
    }

EDIT 2 I now realise that I was doing something stupid. I wanted the Popup of the ComboBox to be right-aligned. I didn't see that it was reinitialized in the ArrangePopup private method call.
I tried inheriting from ComboBox to override the placement function but I must be doing something wrong because it still doesn't seem to work although I overrode the methods that call ArrangePopup

Cheers.

Upvotes: 3

Views: 7318

Answers (2)

Ewert
Ewert

Reputation: 301

What you can do is use MS Expression Blend to generate the default template for a ComboBox. In the template you will find a Popup named Popup, change it's FlowDirection attribute to RightToLeft...

<Popup x:Name="Popup" FlowDirection="RightToLeft">

Note the ScrollViewer object inside the Popup will inherit the FlowDirection set so you have to explicitly set its FlowDirection to LeftToRight...

<ScrollViewer x:Name="ScrollViewer" BorderThickness="0" Padding="1" FlowDirection="LeftToRight">

...else it will fill from RightToLeft and the scrollbar will be on the left hand side.

Upvotes: 0

R4cOOn
R4cOOn

Reputation: 2575

I now realize that if I don't answer to my own question no one will be able to mark my work-around as an answer. So here's the full code. I implemented it as a PRISM behavior.

public static class PopupRightAlignBehavior
{
    public static readonly DependencyProperty InstanceProperty =
        DependencyProperty.RegisterAttached("Instance", typeof(object), typeof(PopupRightAlignBehavior), new PropertyMetadata(OnSetInstanceCallback));

    public static object GetInstance(DependencyObject obj)
    {
        return (object)obj.GetValue(InstanceProperty);
    }

    public static void SetInstance(DependencyObject obj, object value)
    {
        obj.SetValue(InstanceProperty, value);
    }

    private static void OnSetInstanceCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var popup = (Popup)d;
        popup.Opened -= OnPopupOpened;
        popup.Opened += OnPopupOpened;
    }

    private static void OnPopupOpened(object sender, System.EventArgs e)
    {
        var popup = (Popup)sender;
        popup.Dispatcher.BeginInvoke(() => popup.HorizontalOffset = -popup.ActualWidth);
    }
}

Upvotes: 3

Related Questions