0

Lets say I have a list of custom control:

<ItemsControl>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <ctrl:AutoCompleteTextBox />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</StackPanel>

These controls are autocomplete text boxes and I want the suggestions list to be rendered above the rest of the controls.

If I design my control like this:

<UserControl>
    <StackPanel Orientation="Vertical">
        <TextBox Name="Value" />
        <ListView Name="Suggestions" />
    </StackPanel>
</UserControl>

When I populate my ListView, the stackpanel grows and the other autocomplete are just shifted. I want them to be covered by the suggestions list.

I tried to use a Canvas with Panel.ZIndex property set to 2000, but the canvas is always rendered below the other items:

Canvas fails

Here is the xaml:

<UserControl>
    <StackPanel Orientation="Vertical">
        <TextBox Name="Value" />
        <Canvas Panel.ZIndex="2000">
            <ListView Panel.ZIndex="2000" Name="Suggestions" />
        </Canvas>
    </StackPanel>
</UserControl> 

I also tried to use a PopUp. It works well except that the popup is rendered on top of the screen, even if another application got the focus:

PopUp fails

Here is the xaml:

<UserControl>
    <StackPanel Orientation="Vertical">
        <TextBox Name="Value" />
        <PopUp IsOpen="false">
            <ListView Name="Suggestions" />
        </PopUp>
    </StackPanel>
</UserControl> 

I am open to any other solution, if it applies to my initial requirement.

fharreau
  • 2,105
  • 1
  • 23
  • 46
  • I think that your stackpanel is transparent and you should give it background color. – Paviel Kraskoŭski Nov 22 '16 at 14:51
  • Nop, I tried that too. As you can see, the scroll bar of the listview is also behind the other controls. – fharreau Nov 22 '16 at 14:55
  • I suspect that you have to set Panel.ZIndex property of your UserControl. Your topmost autocomplete textbox has the smallest ZIndex, so every control, which inside this UserControl, will be under another UserControls. I think you should set ZIndext to UserControl when it got a focus. – Paviel Kraskoŭski Nov 22 '16 at 15:04
  • If you want something on top of your UI but contained within your window, I'd go with Daniel's suggestion and use Adorners. – Manfred Radlwimmer Nov 23 '16 at 08:10

4 Answers4

2

You should use and Adorner. It needs more work, however you can always control when to display or hide the content. If the application goes background then you can hide the adorner programmatically.

When you use a control that is part of the visual tree the overlapping of the controls are defined by their order in the visual tree. If you use a container, which inherits from Panel you can fine tune this behavior with Z-Order. However Z-index has this ordering effect only for controls in the same container.

Adorners on the contrary are not part of the visual tree. They can be shown above everything. Unfortunatelly, working with them is more complicated than with regular controls. The framework does not really restrcit what you can or cannot do with an Adorner. There is no ultimate answer, since it depends on the application how the adorner should behave. For example if you use tabs, you might need to hide the displayed adorner/popup when the tab is changed. Therefore these kind of behaviours are left to the programmer.

Popups use adorners internally but there is less control about hiding or showing them. So, I suggest to use an adorner and customize its behavor for your exact purpose.

You can find more information about this issue HERE or HERE

UPDATE:

I just have checked what I was using when I had to do similar things. I used NonTopmostPoupup with some modifications. As far as I understand it does exactly what you are looking for. So I have to reconsider my point about the Popup control vs the Adorner :)

Daniel Leiszen
  • 1,827
  • 20
  • 39
  • Can you help with providing some code ? I tried to do the same as in the article you provide but I get a NullReference on the last line (var adornerLayer = AdornerLayer.GetAdornerLayer(this.Propositions); var myAdorner = new MyAdorner(this.Propositions); adornerLayer.Add(myAdorner);). None of the obvious element is null at runtime and this.Suggestions is my ListView. I did all of this in the constructor of my UserControl. Unfortunately, all the links provided in the article are down ... – fharreau Nov 23 '16 at 08:57
  • I found a workaround but I am looking forward to see it working with adorners, if you have time to provide some code. I will change the answer with your solution if I can get it working. – fharreau Nov 23 '16 at 09:31
  • Please see updated answer. The referenced Git repo hopefully contains the code your are looking for. – Daniel Leiszen Nov 23 '16 at 10:55
  • That's looks complicated compared to my workaround. Probably more robust in complicated scenario (with several components overlaping mutually) but in my simple case, I will keep it simple. Thanks anyway ! – fharreau Nov 23 '16 at 14:24
  • You just have to copy and paste the control into a file in your project and you can use it. Does not sound really complicated to me. – Daniel Leiszen Nov 23 '16 at 14:41
  • Indeed, it is not complicated to use but it is more to understand. I prefer keep the code simple if there is no need for this complexity. If you provide a NuGet package with this control, it will be my pleasure to use it. – fharreau Nov 23 '16 at 16:05
  • So its not complicate if we cannot see how complicate it is, right? :) I usually have a 3rdParty folder in my solution for such source codes and keep them "away" from my codebase. But if something is not right there is the possibility to debug them. – Daniel Leiszen Nov 23 '16 at 16:19
1

Try to set the ZIndex of the User Control.

<UserControl Panel.ZIndex="your number">
<StackPanel Orientation="Vertical">
    <TextBox Name="Value" />
    <ListView Name="Suggestions" />
</StackPanel>
</UserControl>

Maybe you should also try to put your UserControl code at the bottom of your XAML code like this:

<!-- Your Code -->

<UserControl Panel.ZIndex="your number">
<StackPanel Orientation="Vertical">
    <TextBox Name="Value" />
    <ListView Name="Suggestions" />
</StackPanel>
</UserControl>
</Window>
nicoh
  • 374
  • 2
  • 12
  • Settings the ZIndex on the user control does not solve the problem. And I can't move my user control code at the bottom of the window because it is written in a separated file. – fharreau Nov 22 '16 at 15:58
0

You should be able to workaround this issue using the AirspacePopup since it "Is not always-on-top, but placed relative to the Window in which it is being placed" as mentioned in the original post.

You can see the full code and explanation in : AirspacePopup by Fredrik Hedblad

Community
  • 1
  • 1
Josep B.
  • 587
  • 1
  • 6
  • 16
0

While the adorner's solution looks the proper way do do this, until I get it working, I found a workaround with the PopUp solution simpler than Daniel's one. It consists of handling the application desactivation (when switching to another application for example):

public AutoCompleteTextBox()
{
    InitializeComponent();

    var currentWindow = App.Current.MainWindow;
    currentWindow.Deactivated += ApplicationDeactivated;
}

private void ApplicationDeactivated(object sender, EventArgs e)
{
    this.HidePropositions(); // Here is all my stuff for hiding the suggestions list
}
Community
  • 1
  • 1
fharreau
  • 2,105
  • 1
  • 23
  • 46