Адаптер региона Prism2 для панели документов и панели стыковки AvalonDock? - PullRequest
3 голосов
/ 29 мая 2009

У кого-нибудь есть пример кода о том, как создать адаптер региона для DocumentPane и DockingPane AvalonDock?

Ответы [ 4 ]

4 голосов
/ 16 октября 2009

Markus Raufer добавил два адаптера региона к проекту CompositeWpfContrib в CodePlex , который поддерживает DocumentPane и DockingPane.

3 голосов
/ 13 мая 2011

Я использовал Raffaeu Bermuda фрагменты для поддержки адаптера области вкладки Avalon, но обнаружил, что некоторые проблемы не решены:

1 - Он не поддерживает активацию определенного представления (aka - tab - DockableContent), поэтому код Region.Activate(object view) не будет работать.

2- Все вкладки по умолчанию активны в регионе. Так что Region.ActiveViews коллекция по умолчанию имеет все представления, это не идеально, так как иногда мне нужно было проверить, является ли представление активным или нет (можно представить кнопку сохранения в области панели инструментов, которая выполняет команду SaveCommand только для текущего активный вид = вкладка в нашем случае)

3 - Закрытые представления на самом деле не закрываются, а только скрываются. Даже если вы установите HideOnClose = true при добавлении нового DockableContent, он все равно не будет удален из коллекции Region.Views. Это может привести к проблемам с утечками памяти.

4 - Если у вас есть ранее добавленный DockableContent на панели, они не будут синхронизированы и добавлены в коллекцию Region.Views.

Итак, вот код, который я сейчас использую, это всего лишь небольшая настройка адаптера селектора и поведения селектора синхронизации, найденная в исходном коде PRISM:

Класс AvalonRegionAdapter:

public class AvalonRegionAdapter : RegionAdapterBase<DocumentPane>
{
    public AvalonRegionAdapter(IRegionBehaviorFactory factory) : base(factory) {}

    protected override void AttachBehaviors(IRegion region, DocumentPane regionTarget)
    {
        if (region == null) throw new System.ArgumentNullException("region");
        //Add the behavior that syncs the items source items with the rest of the items
        region.Behaviors.Add(AvalonDocumentSyncBehavior.BehaviorKey,
                             new AvalonDocumentSyncBehavior()
        {
            HostControl = regionTarget
        });
        base.AttachBehaviors(region, regionTarget);
    }

    protected override void Adapt(IRegion region, DocumentPane regionTarget){ }

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

AvalonDocumentSyncКод поведения:

public class AvalonDocumentSyncBehavior : RegionBehavior, IHostAwareRegionBehavior
{
    /// <summary>
    /// Name that identifies the SelectorItemsSourceSyncBehavior behavior in a collection of RegionsBehaviors. 
    /// </summary>
    public static readonly string BehaviorKey = "AvalonDocumentSyncBehavior";
    private bool updatingActiveViewsInHostControlSelectionChanged;
    private Selector hostControl;

    /// <summary>
    /// Gets or sets the <see cref="DependencyObject"/> that the <see cref="IRegion"/> is attached to.
    /// </summary>
    /// <value>
    /// A <see cref="DependencyObject"/> that the <see cref="IRegion"/> is attached to.
    /// </value>
    /// <remarks>For this behavior, the host control must always be a <see cref="Selector"/> or an inherited class.</remarks>
    public DependencyObject HostControl
    {
        get
        {
            return this.hostControl;
        }

        set
        {
            this.hostControl = value as Selector;
        }
    }

    /// <summary>
    /// Starts to monitor the <see cref="IRegion"/> to keep it in synch with the items of the <see cref="HostControl"/>.
    /// </summary>
    protected override void OnAttach()
    {
        bool itemsSourceIsSet = this.hostControl.ItemsSource != null;
        if (itemsSourceIsSet)
        {
            //throw new InvalidOperationException(Resources.ItemsControlHasItemsSourceException);
        }

        this.SynchronizeItems();

        this.hostControl.SelectionChanged += this.HostControlSelectionChanged;
        this.Region.ActiveViews.CollectionChanged += this.ActiveViews_CollectionChanged;
        this.Region.Views.CollectionChanged += this.Views_CollectionChanged;
    }

    private void Views_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            int startIndex = e.NewStartingIndex;
            foreach (object newItem in e.NewItems)
            {
                UIElement view = newItem as UIElement;
                TabViewModel viewModel = ((UserControl)view).DataContext as TabViewModel;
                if (view != null)
                {
                    DockableContent newDockableContent = new DockableContent();
                    newDockableContent.Content = newItem;
                    //if associated view has metadata then apply it.
                    newDockableContent.Title = view.GetType().ToString();
                    if (viewModel != null)
                    {
                        //Image img = new Image();
                        //img.Source = new BitmapImage(new Uri(@"Resources/Alerts.png", UriKind.Relative));
                        newDockableContent.Title = viewModel.TabModel.Title;
                        newDockableContent.IsCloseable = viewModel.TabModel.CanClose;
                        //newContentPane.Icon = img.Source;
                    }

                    //When contentPane is closed remove from the associated region
                    newDockableContent.Closed += new EventHandler(newDockableContent_Closed);

                    newDockableContent.HideOnClose = false; 
                    this.hostControl.Items.Add(newDockableContent);
                    newDockableContent.Activate();
                }
            }
        }
        else if (e.Action == NotifyCollectionChangedAction.Remove)
        {
            foreach (object oldItem in e.OldItems)
            {
                this.hostControl.Items.Remove(oldItem);
            }
        }
    }

    void newDockableContent_Closed(object sender, EventArgs e)
    {
        var dockableContent = sender as DockableContent;
        if(dockableContent != null)
            if (this.Region.Views.Contains(dockableContent.Content))
            {
                this.Region.Remove(dockableContent.Content);
            }
    }

    private void SynchronizeItems()
    {
        List<object> existingItems = new List<object>();

        // Control must be empty before "Binding" to a region
        foreach (object childItem in this.hostControl.Items)
        {
            existingItems.Add(childItem);
        }

        foreach (object view in this.Region.Views)
        {
            this.hostControl.Items.Add(view);
        }

        foreach (object existingItem in existingItems)
        {
            this.Region.Add(existingItem);
        }
    }


    private void ActiveViews_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        if (this.updatingActiveViewsInHostControlSelectionChanged)
        {
            // If we are updating the ActiveViews collection in the HostControlSelectionChanged, that 
            // means the user has set the SelectedItem or SelectedItems himself and we don't need to do that here now
            return;
        }

        if (e.Action == NotifyCollectionChangedAction.Add)
        {
            var selectedDockableContent = this.hostControl.SelectedItem as DockableContent;
            if (selectedDockableContent != null
                && selectedDockableContent.Content != null
                && selectedDockableContent.Content != e.NewItems[0]
                && this.Region.ActiveViews.Contains(selectedDockableContent.Content))
            {
                this.Region.Deactivate(selectedDockableContent.Content);
            }

            var _UIElement = e.NewItems[0] as FrameworkElement;
            this.hostControl.SelectedItem = _UIElement.Parent;
        }
        else if (e.Action == NotifyCollectionChangedAction.Remove &&
                 e.OldItems.Contains(this.hostControl.SelectedItem))
        {
            this.hostControl.SelectedItem = null;
        }
    }

    private void HostControlSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        try
        {
            // Record the fact that we are now updating active views in the HostControlSelectionChanged method. 
            // This is needed to prevent the ActiveViews_CollectionChanged() method from firing. 
            this.updatingActiveViewsInHostControlSelectionChanged = true;

            object source;
            source = e.OriginalSource;

            if (source == sender)
            {
                foreach (object item in e.RemovedItems)
                {
                    // check if the view is in both Views and ActiveViews collections (there may be out of sync)
                    var dockableContent = item as DockableContent;
                    if (this.Region.Views.Contains(dockableContent.Content) && this.Region.ActiveViews.Contains(dockableContent.Content))
                    {
                        this.Region.Deactivate(dockableContent.Content);
                    }
                }

                foreach (object item in e.AddedItems)
                {
                    var dockableContent = item as DockableContent;
                    if (this.Region.Views.Contains(dockableContent.Content) && 
                         !this.Region.ActiveViews.Contains(dockableContent.Content))
                    {
                        this.Region.Activate(dockableContent.Content);
                    }
                }
            }
        }
        finally
        {
            this.updatingActiveViewsInHostControlSelectionChanged = false;
        }
    }
}

Код на загрузчике для настройки адаптера

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
    var mappings = base.ConfigureRegionAdapterMappings();
    mappings.RegisterMapping(typeof(AvalonDock.DocumentPane), 
                             this.Container.Resolve<AvalonRegionAdapter>());
    return mappings;
}

Тогда вам понадобятся TabModel и TabViewModel начиная с Raffaeu Bermuda

public sealed class TabModel : DependencyObject
{
   public string Title
   {
       get { return (string)GetValue(TitleProperty); }
       set { SetValue(TitleProperty, value); }
   }

   // Using a DependencyProperty as the backing store for Title.  This enables animation, styling, binding, etc...
   public static readonly DependencyProperty TitleProperty =
       DependencyProperty.Register("Title", typeof(string), typeof(TabModel));

   public bool CanClose
   {
       get { return (bool)GetValue(CanCloseProperty); }
       set { SetValue(CanCloseProperty, value); }
   }

   // Using a DependencyProperty as the backing store for CanClose.  This enables animation, styling, binding, etc...
   public static readonly DependencyProperty CanCloseProperty =
       DependencyProperty.Register("CanClose", typeof(bool), typeof(TabModel));

   public bool IsModified
   {
       get { return (bool)GetValue(IsModifiedProperty); }
       set { SetValue(IsModifiedProperty, value); }
   }

   // Using a DependencyProperty as the backing store for IsModified.  This enables animation, styling, binding, etc...
   public static readonly DependencyProperty IsModifiedProperty =
       DependencyProperty.Register("IsModified", typeof(bool), typeof(TabModel));

}

И TabViewModel, действующая как базовый класс:

public class TabViewModel : INotifyPropertyChanged
{
    private TabModel _tabModel;

    public TabModel TabModel
    {
        get { return this._tabModel; }
        set
        {
            this._tabModel = value;
            OnPropertyChanged("TabModel");
        }
    }
    public TabViewModel()
    {
        this.TabModel = new TabModel();
        this.TabModel.CanClose = true;
        this.TabModel.IsModified = false;
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public void OnPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }
}

Дайте мне знать, если вам нужна дополнительная помощь, я опубликую этот блог в ближайшее время.

2 голосов
/ 16 ноября 2010

Поскольку Avalon DocumentPane и DockingPane основаны на System.Windows.Controls.Primitives.Selector, вы можете использовать по умолчанию SelectorRegionAdapter в Prism.

Просто основывайте свой контроль на DockableContent

<ad:DockableContent x:Class="DesignerWPF.UserControl1"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             xmlns:ad="clr-namespace:AvalonDock;assembly=AvalonDock"
             d:DesignHeight="300" d:DesignWidth="300" Title="dans">
    <Grid>
        <TextBox Text="sdfdf"></TextBox>
    </Grid>
</ad:DockableContent>

на вашем основном Shell.xmal установите регионы в панели закрепления

       <ad:DockingManager x:Name="dockManager" Grid.Row="1" Margin="0,4,0,0">
            <ad:ResizingPanel Orientation="Horizontal">
                <ad:DockablePane cal:RegionManager.RegionName="LeftRegion">

                </ad:DockablePane>
                <ad:DocumentPane cal:RegionManager.RegionName="DocumentRegion">

                </ad:DocumentPane>
            </ad:ResizingPanel>
        </ad:DockingManager>

тогда, когда вы инициализируете докладчика для управления, он будет отображаться в доке.

public class UserTestControl : IModule
    {
        public UserTestControl(IUnityContainer container, IRegionManager regionManager)
        {
            Container = container;
            RegionManager = regionManager;
        }
        public void Initialize()
        {
            var addFundView = Container.Resolve<UserControl1>();
            RegionManager.Regions["LeftRegion"].Add(addFundView);
        }

        public IUnityContainer Container { get; private set; }
        public IRegionManager RegionManager { get; private set; }
    }
0 голосов
/ 29 мая 2009

Мой совет - искать в Microsoft.Practices.Composite.Presentation.Regions источник Prism В частности, взгляните на ItemsControlRegionAdapter и используйте его в качестве шаблона. Не забудьте наследовать от RegionAdapterBase <>:

public class ItemsControlRegionAdapter : RegionAdapterBase<ItemsControl>

и переопределить ConfigureRegionAdapterMappings () в загрузчике. Это будет выглядеть примерно так:

protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
    RegionAdapterMappings mappings =  base.ConfigureRegionAdapterMappings();
    mappings.RegisterMapping(typeof(Canvas), Container.Resolve<CanvasRegionAdapter>());
    return mappings;
}
...