开发者

Prism: Stacking controls in a region?

开发者 https://www.devze.com 2023-02-10 02:39 出处:网络
My Pri开发者_如何学编程sm app needs to insert buttons from several modules into a Shell region. The buttons will be stacked vertically, like the navigation buttons (Mail, Calendar, and so on) in Outlo

My Pri开发者_如何学编程sm app needs to insert buttons from several modules into a Shell region. The buttons will be stacked vertically, like the navigation buttons (Mail, Calendar, and so on) in Outlook 2010. I am using custom controls as buttons, so I don't need to worry about templating--my problem is the same as if I was inserting plain radio buttons.

How do set up the region so that the buttons will appear stacked vertically? Thanks for your help.


StackPanel immediately jumps to mind when thinking of stacking items vertically. Unfortunately Prism doesn't support the StackPanel to be a region out of the box. Luckily you can create a RegionAdapter to fix this problem.

Public Class StackPanelRegionAdapter
Inherits RegionAdapterBase(Of StackPanel)

    Public Sub New(ByVal behaviorFactory As IRegionBehaviorFactory)
        MyBase.New(behaviorFactory)
    End Sub

    Protected Overrides Sub Adapt(ByVal region As IRegion, ByVal regionTarget As StackPanel)
        AddHandler region.Views.CollectionChanged, Sub(sender As Object, e As NotifyCollectionChangedEventArgs)
            If e.Action = NotifyCollectionChangedAction.Add Then
                For Each element As FrameworkElement In e.NewItems
                    regionTarget.Children.Add(element)
                Next
            Else
            If e.Action = NotifyCollectionChangedAction.Remove Then
                For Each element In e.OldItems
                    If regionTarget.Children.Contains(element) Then
                        regionTarget.Children.Remove(element)
                    End If
                Next
            End If
        End Sub
    End Sub

    Protected Overrides Function CreateRegion() As Microsoft.Practices.Prism.Regions.IRegion
        Return New AllActiveRegion
    End Function
End Class

From there you just need to add the mapping in the ConfigureRegionAdapterMappings Override in your bootstrapper.

Protected Overrides Function ConfigureRegionAdapterMappings() As Microsoft.Practices.Prism.Regions.RegionAdapterMappings
    Dim mappings = MyBase.ConfigureRegionAdapterMappings()
    mappings.RegisterMapping(GetType(Grid), Container.Resolve(Of PrismExtensions.GridRegionAdapter))
    mappings.RegisterMapping(GetType(StackPanel), Container.Resolve(Of PrismExtensions.StackPanelRegionAdapter))
    Return mappings
End Function

Edit: Found the John Papa link I originally got the code from. (In C# if that's what you're using) Fill My Prism Region, Please


Why not just use an ItemsControl as a region? It stacks items vertically and Prism has builtin region adapters for it. I don't understand why you'd want to use a StackPanel for anything but layout.

An ItemsControl by default uses an ItemsPanel that contains a StackPanel, so it's equivalent to the answers already provided, but without the unnecessary code.


From NVenhola's comment in the Fill My Prism Region, Please article, there is an easy, adaptable solution:

<ItemsControl cal:RegionManager.RegionName="XXRegionNameXX">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>


Matt's solution is explained in the 'Developers Guide to Microsoft Prism' (V4) at pp. 189-191.

For C# developers researching this issue, here is a translation of Matt's adapter to C#:

using System.Collections.Specialized;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Practices.Prism.Regions;

namespace FsNoteMaster3.Shell.Views.Utility
{
    /// <summary>
    /// Enables use of a StackPanel in a Prism region.
    /// </summary>
    /// <remarks> See stackoverflow.com/questions/4950464/prism-stacking-controls-in-a-region</remarks>
    public class StackPanelRegionAdapter : RegionAdapterBase<StackPanel>
    {
        /// <summary>
        /// Default constructor.
        /// </summary>
        /// <param name="behaviorFactory">Allows the registration of the default set of RegionBehaviors.</param>
        public StackPanelRegionAdapter(IRegionBehaviorFactory behaviorFactory) : base(behaviorFactory)
        {
        }

        /// <summary>
        /// Adapts a ContentControl to an IRegion. 
        /// </summary>
        /// <param name="region">The new region being used.</param>
        /// <param name="regionTarget">The object to adapt.</param>
        protected override void Adapt(IRegion region, StackPanel regionTarget)
        {
            region.Views.CollectionChanged += (sender, e) =>
            {
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        foreach (FrameworkElement element in e.NewItems) 
                        {
                            regionTarget.Children.Add(element);
                        }
                        break;

                    case NotifyCollectionChangedAction.Remove:
                        foreach (UIElement elementLoopVariable in e.OldItems) 
                        {
                            var element = elementLoopVariable;
                            if (regionTarget.Children.Contains(element)) 
                            {
                                regionTarget.Children.Remove(element);
                            }
                        }
                        break;
                }
            };
        }

        /// <summary>
        /// Template method to create a new instance of IRegion that will be used to adapt the object. 
        /// </summary>
        /// <returns>A new instance of IRegion.</returns>
        protected override Microsoft.Practices.Prism.Regions.IRegion CreateRegion()
        {
            return new AllActiveRegion();
        }
    }
}

And for the Bootstrapper, here is the ConfigureRegionAdapterMappings() override in C#, updated for Prism 4:

/// <summary>
/// Configures the default region adapter mappings to use in the application.
/// </summary>
/// <returns>The RegionAdapterMappings instance containing all the mappings.</returns>
protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
    var mappings = base.ConfigureRegionAdapterMappings();
    mappings.RegisterMapping(typeof(StackPanel), ServiceLocator.Current.GetInstance<StackPanelRegionAdapter>());
    return mappings;
}


How about just using itemscontrol that is just doing doing what you want. By default, ItemsControl will use stackpanel to render the module views.


also be very careful about RegionAdapterMapping instance in Bootstrapper.cs: write it like this:

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
    //this is the correct way
    RegionAdapterMappings regionAdapterMappings = base.ConfigureRegionAdapterMappings(); 
    regionAdapterMappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackPanelRegionAdapter>());
    return regionAdapterMappings;
 }

It took me hours to figure out you cant write:

//RegionAdapterMappings regionAdapterMappings = new RegionAdapterMappings(); 
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号