UserControl (View) not being measured correctly when loaded into region

Jul 16, 2008 at 4:12 AM

I am loading a view which holds a usercontrol into a region using the StaticModuleEnumerator in the Bootstrapper class as described in the docs.

Because the usercontrol has no width or height set, and the horizontal and vertical alignment defaults to "Stretch", I would expect the usercontrol to grow to fill the region when loaded.

What I am seeing however is that the listbox's height seems to be set to fit all the items in the list (way off the screen), so that most of the listbox is clipped at the edge of the region, leaving the vertical scrollbar inactive.

If I explicitly set the height of the usercontrol to "400", then it's height remains at 400 when loaded into the region, and the scrollviewer works as expected with the scrollbar working, but I want the usercontrol to automatically grow to fill the region.

I suspect it's due to the usercontrol being dynamically loaded, and something's not being measured correctly, or at the right time.
I am fairly new to WPF so could use some help if anyone know's what's going on?

The usercontrol is defined as follows:

<UserControl x:Class="EventListView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    MinHeight="200" MinWidth="240">
    <ScrollViewer VerticalScrollBarVisibility="Visible">
        <ListBox Name="listBox" ItemsSource="{Binding}" DisplayMemberPath="EventName" />
    </ScrollViewer>
</UserControl>

Jul 16, 2008 at 12:06 PM
Hi there Jason, one good thing about some ItemsControl classes like the ListBox is that they already have a ScrollViewer present in their template, to show the vertical scrollbar all you need to do is set the property as in the following:

    <ListBox ScrollViewer.VerticalScrollBarVisibility="Visible"
             Name="listBox" DisplayMemberPath="EventName">
    </ListBox>

You should find that the ListBox has this set to "Auto" by default. Your control should occupy all the space provided to it where possible, what context are you using the user control in that's preventing it from occupying the whole space? If you place the above UserControl in a Window you should find it will occupy the whole window.

     <Window x:Class="BrettRyan.Sample.Window1"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:elv="clr-namespace:BrettRyan.EventListView;assembly=BrettRyan.EventListView"
            Title="Window1" Height="300" Width="300">
        <elv:EventListView/>
    </Window>

Hope this helps
-Brett
Jul 18, 2008 at 8:38 PM

Jason,

I am having same problem and wasted hours so far trying to get the desired behavior. Basically, I have a Module which contain a View (UserControl). The View has a ListBox within a Border. I want to set to Height and Width so that it expands to take up all the space in the MainRegion when the View is added to the MainRegion. The issue is that when I fill the ListBox with data, the Border and ListBox expand to way off the screen and there is no scroll bar or anything. I would think that the Border and ListBox would only expand to take up the available space in the MainRegion and then no more. Only thing I can do right now is set a value for Height, like 400. However, this makes it so that ListBox will only expand to 400 and then adds ScrollBar. It doesn't take up all the space in the MainRegion. If I set to Auto, then it expands off the screen. I have no idea how to get the host UserControl to honor the bounds of the container in the Shell.

Did you ever get a resolution on this? Any ideas would be greatly appreciated.
-Rob

Jul 19, 2008 at 8:22 AM
No I'm sorry I've not had time to investigate this further, but likewise, I've wasted hours on this already. I'll try spend some time on it tomorrow, but I'm not sure what to try next.
Jul 19, 2008 at 10:36 PM

I have a solution but please bear in mind that it may not be the correct solution; currently I'm investing my time in learning WPF and Unity and once I have them under my belt will attack the CompositeWPF (I should then know what I'm looking at); although I did cheat and had to figure out / understand how the RegionManager worked before I could move on  ;)

For anyone who wants to visualize the problem:
 
1. Load the Commanding solution and change  the Commanding.Modules.Order project's PresentationModels\OrdersEditorPresentationModel.cs  InitialOrdersCount constant int from 3 to 25 - when you run the app you'll find the listbox has no scrollbars and it extends below the visible grid cell area (there will be 25 items in the list versus 3).

2. First we'll fix the scrollbar issue - Views\OrdersEditorView.xaml.   Update the ListView element by adding x:Name="ListView1" and change the Height attribute from "Auto" to "200"; the listview will have scrollbars and fit nicely in the Grid cell.

3.  The solution lies in Event notification - the view is nicely decoupled from the shell so it doesn't know when the shell size changes (perhaps the CompositeWPF framework has something to notify???);  we'll let it know so that we can adjust the ListView1 height accordingly.  Let's get two very useful Unity Projects (Event processing may be in CompositeWPF but again, I do not know it yet :)
     a.  Load the EventBrokerExtension and SimpleEventBroker projects from the latest Unity download (UnityQuickStarts\language\EventBroker\Src).
     b.  Set references to each in the Commanding and Commanding.Modules.Order projects

4.  In the Commanding project's CommandBootStrapper.CreateShell method add the following statement before the Shell shell statement:
     Container.AddNewExtension<SimpleEventBrokerExtension>();  // and add using/imports as applicable

5.  Update your Commanding Shell XAML so that it will notify code behind when the size has changed:

     <Window x:Class="Commanding.Shell"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:cal="http://www.codeplex.com/CompositeWPF"
         Title="Shell" Height="385" Width="500" MinHeight="385"
         SizeChanged="Window_SizeChanged"
         >

6.  Update the Shell's code behind as follows:

        [Publishes("ShellSizeChanged")]
        public event EventHandler ShellSizeChanged;

        public Shell()
        {
            InitializeComponent();
        }

        private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (ShellSizeChanged != null)
                ShellSizeChanged(sender, e);
        }

7.  Update the OrdersEditorView code behind as follows:

        [SubscribesTo("ShellSizeChanged")]
        public void OnSizeChanged(object sender, EventArgs e)
        {
            SizeChangedEventArgs args = e as SizeChangedEventArgs;
            ListView1.Height = args.NewSize.Height-160;
        }


If we (you or I) didn't miss anything, when you resize the form your ListView1 control will resize accordingly

Jul 21, 2008 at 2:52 PM

Bill,

Thanks for your detailed idea. I tried to implement your suggestions, but it is still not giving the desired result. Here are some points about what I did.

1. The CompositeWPF DOES include event pub/sub mechanism already, so there was no need to add in the extensions and such from Unity project.
2. I added a new Event to my Infrastructure project to be used by the Shell and module with View that needs to be notified when Shell size changes.
3. In the Shell, I hooked up the event for SizeChanged and published the SizeChangedEventArgs with the new event.
4. In the Module with the View containing ListView, I subscribed to the size changed event from the Shell.
5. I do receive the events when Shell size changes, and I can modify the ListView Height.
6. However, the real problem is not with the Shell changing sizes, but the Listview changes sizes and ignoring the bounds of the container. For example, I launch the program and Shell is shown and View from Module is loaded into the MainRegion. When I fill the ListView with data, the ListView expands and ignores the bounds of the MainRegion. It just expands outside the form and no Scrollbar is provided on the ListView. During all of this, the Shell size is not changing at all and therefore no event is fired here.
7. I think you may be on to something with the event mechanism, but the Shell SizeChanged event doesn't seem to be the correct place to plug-in to fix this issue.
8. I definitely think that something must be going on in the CompositeWPF code that is causing this. I don't have these problems if I just create a normal WPF application and add a UserControl to a Panel or something without using CompositeWPF.

I sure wish someone from the CompositeWPF team would comment on this or provide some guidance...

Rob

Jul 21, 2008 at 3:12 PM
Edited Jul 21, 2008 at 3:20 PM
Hi Rob,

Do you have the time to slap together a small prototype that will help us visualize the problem?   We (the community and perhaps the CompositeWPF team) can use it to come up with a solution that will help others now, and in the future.   I'm currently doing Bill Steeles 18 part webcast on WPF; perhaps one of them will hold the answer... 

EDITED:  [Rob] 6. However, the real problem is not with the Shell changing sizes, but the Listview changes sizes and ignoring the bounds of the container. For example, I launch the program and Shell is shown and View from Module is loaded into the MainRegion. When I fill the ListView with data, the ListView expands and ignores the bounds of the MainRegion. It just expands outside the form and no Scrollbar is provided on the ListView. During all of this, the Shell size is not changing at all and therefore no event is fired here.

If I understand your scenario correctly you should configure a height for your listbox (so that it is within the bounds of your container) and as the number of items grow the scrollbar will appear.   Then the event subscription can be used to resize your listbox (which now has a configured height) so that it will be anchored to the container.   I think I have enough to address this issue - I'll see if I can't crank out a solution and blog it this evening... 
Jul 21, 2008 at 10:55 PM

Bill,

I found solution to the problem by looking around at some of the other issues on this website. There was a post at http://www.codeplex.com/CompositeWPF/Thread/View.aspx?ThreadId=28522 that discussed an issue with getting the View to take up all the space available in the Shell. I figured this solution might help me out so I tried it. Everything is working like I want now.

Before, in the Shell, I had a StackPanel with the ItemsControl inside it. This was the MainRegion (This is how all the examples are for CompositeWPF). Whenever I loaded my View into this region, it wouldn't automatically take up the available space. Also, when the ListView in the View was filled with data, it would expand beyond view. Anyway, based on the above link:

1. In the Shell, I changed the StackPanel to a DockPanel and had it take up all available space.
2. In the Shell, I changed the ItemsControl to be docked to Top of DockPanel and set the ItemsControl.ItemsPanel to have a template of DockPanel. This is what was discussed in the View.
3. In my View that gets loaded, I got rid of all the MinHeight, MaxHeight stuff so that it would expand fully when loaded into MainRegion.
4. After this, when the View was loaded, it correctly took up all the space in the containing DockPanel. Also, when ListView was filled with data, it honors the bounds of the container and doesn't stretch beyone view.

HOORAY!!!! Not sure why this all works, but it does.

- Rob

Jul 22, 2008 at 12:30 AM
Great news!!  Thanks for providing the steps for us (as well as the link); when it's my turn I trust it will save me a lot of time :)
Jul 22, 2008 at 2:25 AM
YES! thanks so much for that. It did the trick. It still doesn't explain though, why it doesn't work with the default ItemsControl.ItemsPanel.
Aug 23, 2008 at 12:23 AM
Edited Aug 25, 2008 at 7:39 AM
Not so fast! This works for this particular case ... because we only ever populate the ItemsControl with one thing. In fact, I am wondering exactly why this region is an ItemsControl. Because the minute you add another view into this Main Region ... It puts the second view to the right of the first. Each subsequent view is stacked to the right ... and I can't figure out how to stop that. [Aside: I'm goint to ask why in a separate thread.]

This bit me when I was trying to use this same trick to have WPF manage the size of a ListView (think ListBox, ItemsControl, etc.) that is in one of my nested views. Of course it doesn't work; it expands without scrollbar and is cropped by the Window. I'm back to square one ... forced to put a fixed height on the ListView and figure out how to adjust that height dynamically.

I've scoured the web and found no answers. HELP!

Update 8/24: I elaborate on this topic in this Prism discussion thread.