sobota, 19 listopada 2011

Prism tworzenie własnego adaptera regionu

W Prism mamy dostępnych kilka adapterów regionów "od ręki" - pozostałe musimy sami stworzyć.
W jakich sytuacjach może zajść konieczność stworzenia własnego adaptera regionów? Jeżeli chcemy użyć jakiejś kontrolki jako miejsca w które Prism będzie ładował widoki, a obecnie nie ma takiej obsługi w prosty sposób możemy taką obsługę dodać. Zobaczmy na przykład:

Code:
<Window x:Class="WpfApplication1.Shell"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Regions;assembly=Microsoft.Practices.Prism" 
        Title="Shell" Height="300" Width="300">
    <Grid>
        <StackPanel Orientation="Vertical">
            <StackPanel Orientation="Horizontal" prism:RegionManager.RegionName="NavigationRegion" />
            <ContentControl prism:RegionManager.RegionName="ContentRegion" />
        </StackPanel>
    </Grid>
</Window>

Uruchomienie aplikacji spowoduje następujący błąd:

W wolnym tłumaczeniu wyjątek oznacza iż dana kontrolka nie obsługuje regionów. Tak więc nie pozostaje nam nic innego jak dodać do kontrolki obsługę regionów.
Dodanie obsługi danego regionu składa się z 4 kroków:
  1. Tworzymy klasę dziedziczącą po RegionAdapterBase<T>
  2. Implementujemy metodę CreateRegion która zwraca jeden z trzech wyników determinujących zachowania regionu:
    1. SingleActiveRegion - jeden aktywny region w danym momencie (ContentControl)
    2. AllActiveRegion - wszystkie regiony aktywne (ItemsControls)
    3. Region - wiele aktywnych regionów (SelectorControls)
  3. Implementacja metody Adapt
  4. Rejestracja stworzonego adaptera w Bootstrapperze
Żeby lepiej zobrazować powyższe kroki przejdziemy je podczas implementowania obsługi regionów w kontrolce StackPanel.

1. Klasa dziedzicząca po klasie RegionAdapterBase<T> :


Code:
    public class StackPanelRegionAdapter : RegionAdapterBase<StackPanel>
    {
        protected override void  Adapt(IRegion region, StackPanel regionTarget)
        {
          throw new SystemNotImplementedException();
        }

        protected override IRegion  CreateRegion()
        {
          throw new SystemNotImplementedException();
        }
    }

2. Metoda CreateRegion


Code:
        protected override void Adapt(IRegion region, StackPanel regionTarget)
        {
            region.Views.CollectionChanged += (s, e) =>
                                                  {
                                                      if (e.Action == NotifyCollectionChangedAction.Add)
                                                      {
                                                          foreach (FrameworkElement item in e.NewItems)
                                                          {
                                                              regionTarget.Children.Add(item);
                                                          }
                                                      }
                                                      else if (e.Action == NotifyCollectionChangedAction.Remove)
                                                      {
                                                          foreach (FrameworkElement item in e.NewItems)
                                                          {
                                                              if (regionTarget.Children.Contains(item))
                                                              {
                                                                  regionTarget.Children.Remove(item);
                                                              }
                                                          }
                                                      }
                                                  };
        }

Tak więc w metodzie tej nasłuchujemy zdarzenia zmiany kolekcji. Na podstawie typu zdarzenia odpowiednio reagujemy
  • w przypadku dodawania nowego elementu - dodajemy go do naszego StackPanel-u za pomocą właściwości Children
  • w przypadku usuwania - usuwamy odpowiedni element ze StackPanel-u
 3. Metoda CreateRegion:


Code:
        protected override IRegion CreateRegion()
        {
            return new AllActiveRegion();
        }

Zwracamy informację że wszystkie elementy dodane do naszego StackPanel-a będą domyślne aktywne.

4. Dodanie konstruktora do tworzonej klasy StackPanelRegionAdapter


Code:
        public StackPanelRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
            : base(regionBehaviorFactory)
        {
        }

Nasza klasa wymaga jeszcze konstruktora - dodajemy go więc.

Całość przedstawia się następująco:

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

namespace WpfApplication1
{
    public class StackPanelRegionAdapter : RegionAdapterBase<StackPanel>
    {
        public StackPanelRegionAdapter(IRegionBehaviorFactory regionBehaviorFactory)
            : base(regionBehaviorFactory)
        {
        }

        protected override void Adapt(IRegion region, StackPanel regionTarget)
        {
            region.Views.CollectionChanged += (s, e) =>
                                                  {
                                                      //Kiedy użytkownik dodaje nowy element dodajemy go do
                                                      //naszego stackpanelu
                                                      if (e.Action == NotifyCollectionChangedAction.Add)
                                                      {
                                                          foreach (FrameworkElement item in e.NewItems)
                                                          {
                                                              regionTarget.Children.Add(item);
                                                          }
                                                      }
                                                      else if (e.Action == NotifyCollectionChangedAction.Remove)
                                                      {
                                                          foreach (FrameworkElement item in e.NewItems)
                                                          {
                                                              if (regionTarget.Children.Contains(item))
                                                              {
                                                                  regionTarget.Children.Remove(item);
                                                              }
                                                          }
                                                      }
                                                  };
        }

        protected override IRegion CreateRegion()
        {
            return new AllActiveRegion();
        }
    }
}


Tak więc pierwsza część za nami zostało tylko zarejestrowanie naszego adaptera w Bootstrapperze co przedstawia się następująco:


Code:
using System.Windows;
using System.Windows.Controls;
using Microsoft.Practices.Prism.Regions;
using Microsoft.Practices.Prism.UnityExtensions;
using Microsoft.Practices.Unity;

namespace WpfApplication1
{
    public class Bootstrapper : UnityBootstrapper
    {
        protected override DependencyObject CreateShell()
        {
            return Container.Resolve<Shell>();
        }

        protected override void InitializeShell()
        {
            Application.Current.MainWindow = (Shell)Shell;
            Application.Current.MainWindow.Show();
        }

        protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
        {
            var mappings = new RegionAdapterMappings();
            mappings.RegisterMapping(typeof(StackPanel), Container.Resolve<StackPanelRegionAdapter>());

            return mappings;
        }
    }
}

Za pomocą metody ConfigureRegionAdapterMappings dodajemy informację o naszym Adapterze StackPanel.


Brak komentarzy:

Prześlij komentarz