Removing back swipe gesture from page using xamarin forms - xamarin

Is there a way i can disable the back swipe to previous page option for iOS on one single page of my project ?

You can achieve this by implementing a custom renderer and setting the right property for this. You can see a sample implementation underneath. The right property, in this case, is InteractivePopGestureRecognizer which you need to set to false.
Do this in the ViewWillAppear so the NavigationController is initialized.
using DisableSwipe.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ContentPage), typeof(NoBackSwipeRenderer))]
namespace DisableSwipe.iOS
{
public class NoBackSwipeRenderer : PageRenderer
{
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
if (ViewController?.NavigationController != null)
ViewController.NavigationController.InteractivePopGestureRecognizer.Enabled = false;
}
}
}

#Symorp
You could do it like so:
public class YourCustomPageRenderer : PageRenderer
{
private YourCustomPage _yourCustomPage;
protected override void OnElementChanged(VisualElementChangedEventArgs e)
{
base.OnElementChanged(e);
_yourCustomPage = e.NewElement as YourCustomPage;
if (_yourCustomPage != null)
{
_yourCustomPage.PropertyChanged += YourCustomPagePropertyChangedEventHandler;
}
}
public override void ViewWillAppear(bool animated)
{
base.ViewWillAppear(animated);
SetInteractivePopGestureRecognizerEnabled(isEnabled: false);
}
private void YourCustomPagePropertyChangedEventHandler(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
if (propertyChangedEventArgs.PropertyName == nameof(YourCustomPage.IsInteractivePopGestureRecognizerEnabled))
{
SetInteractivePopGestureRecognizerEnabled(_yourCustomPage.IsInteractivePopGestureRecognizerEnabled);
}
}
private void SetInteractivePopGestureRecognizerEnabled(bool isEnabled)
{
var interactivePopGestureRecognizer = ViewController?.NavigationController?.InteractivePopGestureRecognizer;
if (interactivePopGestureRecognizer != null)
{
//Prevents the back-swipe-gesture when the user wants to swipe a page away (from left edge of the screen)
interactivePopGestureRecognizer.Enabled = isEnabled;
}
}
}
public class YourCustomPage : ContentPage
{
/// <summary>
/// If you need it as bindable property, feel free to create a <see cref="BindableProperty"/>.
/// </summary>
public bool IsInteractivePopGestureRecognizerEnabled { get; set; }
}
Feel free to adjust to your needs! :-)
I omitted the export renderer attribute etc., just for simplicity.

Related

Get instance of rendered class Button (inside Xamarin.Forms) inside custom button renderer (Xamarin.Android)

I have the simple custom button in my code:
public class CustomButton : Button
{
public bool State { get; set; } = false;
}
and its renderer:
public class CustomButtonRenderer : Xamarin.Forms.Platform.Android.AppCompat.ButtonRenderer
{
public CustomButtonRenderer(Context context) : base(context) { }
ObjectAnimator objectAnimator;
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
{
base.OnElementChanged(e);
if (Control != null)
{
var button = (Control as Android.Widget.Button);
(Control as Android.Widget.Button).Touch += Control_Touch;
// this don't works:
if (Control.State) Control.SetBackgroundColor(global::Android.Graphics.Color.LightGray);
}
}
}
And I want to get access to State property of CustomButton instance inside my CustomButtonRenderer class. But I can't, because the Control has type Android.Support.V7.Widget.AppCompatButton, absolutly unrelated to my CustomButton class.
Are there any ways to gain access to fields of rendered CustomButton object inside its renderer?
Control is the native control rendering your CustomButton. What you are looking for is the property Element, which represents your Xamarin.Forms CustomButton.
Within OnElementChanged it is available as e.NewElement.
if (e.NewElement is CustomButton customButton
&& customButton.State)
{
Control.SetBackgroundColor(global::Android.Graphics.Color.LightGray);
}

Is there a way to recognize a short or long press on a screen with Xamarin.Forms?

I have an application that responds to a short tap on the screen. I do this by adding a gesture recognizer.
Is there a way that I can make it respond to either a short or a long press and have these call different methods?
You will have implement renderers for that. In case of iOS you can use UILongPressGestureRecognizer to detect a long-press action, while in case of Android, you can use GestureDetector to do the same.
Forms control
public class CustomView : ContentView
{
public event EventHandler<EventArgs> LongPressEvent;
public void RaiseLongPressEvent()
{
if (IsEnabled)
LongPressEvent?.Invoke(this, EventArgs.Empty);
}
}
iOS renderer
[assembly: ExportRenderer(typeof(CustomView), typeof(CustomViewRenderer))]
namespace AppNamespace.iOS
{
public class CustomViewRenderer : ViewRenderer<CustomView, UIView>
{
UILongPressGestureRecognizer longPressGestureRecognizer;
protected override void OnElementChanged(ElementChangedEventArgs<CustomView> e)
{
longPressGestureRecognizer = longPressGestureRecognizer ??
new UILongPressGestureRecognizer(() =>
{
Element.RaiseLongPressEvent();
});
if (longPressGestureRecognizer != null)
{
if (e.NewElement == null)
{
this.RemoveGestureRecognizer(longPressGestureRecognizer);
}
else if (e.OldElement == null)
{
this.AddGestureRecognizer(longPressGestureRecognizer);
}
}
}
}
}
Android renderer
[assembly: ExportRenderer(typeof(CustomView), typeof(CustomViewRenderer))]
namespace AppNamespace.Droid
{
public class CustomViewRenderer : ViewRenderer<CustomView, Android.Views.View>
{
private CustomViewListener _listener;
private GestureDetector _detector;
public CustomViewListener Listener
{
get
{
return _listener;
}
}
public GestureDetector Detector
{
get
{
return _detector;
}
}
protected override void OnElementChanged(ElementChangedEventArgs<CustomView> e)
{
base.OnElementChanged(e);
if (e.OldElement == null)
{
GenericMotion += HandleGenericMotion;
Touch += HandleTouch;
_listener = new CustomViewListener(Element);
_detector = new GestureDetector(_listener);
}
}
protected override void Dispose(bool disposing)
{
GenericMotion -= HandleGenericMotion;
Touch -= HandleTouch;
_listener = null;
_detector?.Dispose();
_detector = null;
base.Dispose(disposing);
}
void HandleTouch(object sender, TouchEventArgs e)
{
_detector.OnTouchEvent(e.Event);
}
void HandleGenericMotion(object sender, GenericMotionEventArgs e)
{
_detector.OnTouchEvent(e.Event);
}
}
public class CustomViewListener : GestureDetector.SimpleOnGestureListener
{
readonly CustomView _target;
public CustomViewListener(CustomView s)
{
_target = s;
}
public override void OnLongPress(MotionEvent e)
{
_target.RaiseLongPressEvent();
base.OnLongPress(e);
}
}
}
Sample Usage
<local:CustomView LongPressEvent="Handle_LongPress" />
Code-behind
void Handle_LongPressEvent(object sender, System.EventArgs e)
{
//handle long press event here
}
You can also customize above to add a command to make it more MVVM friendly.
You can refer this link for more details regarding gesture recognizers.
http://arteksoftware.com/gesture-recognizers-with-xamarin-forms/
You will have implement renderers for that. In case ios and android
best way for do that!

How can I interact with a xamarin forms image in Android?

I have an image in Xamarin Forms. I want to use the native android features to interact with this image. For example, when the image is tapped, I want to know the x,y coordinates of where the image was tapped. I can use Android ImageView but I'm not sure how to cast the Xamarin Forms image to Android ImageView
[assembly: ExportRenderer(typeof(Image), typeof(FloorplanImageRenderer))]
namespace EmployeeApp.Droid.Platform
{
public class FloorplanImageRenderer : ImageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
if (Control == null)
{
var imageView = (ImageView)e.NewElement; // This is not right
}
base.OnElementChanged(e);
}
}
}
But the Control is null....
No, it shouldn't be null. Back to your question, I think first of all, you will need to attach a touch event to the image control in PCL and create a property to hold the coordinate when image get touched. And I think here in your code:
[assembly: ExportRenderer(typeof(Image), typeof(FloorplanImageRenderer))]
I think the Image here should be your custom image control which inherits from Image in PCL.
Create a interface for touch event:
public interface IFloorplanImageController
{
void SendTouched();
}
Create a custom control for image:
public class FloorplanImage : Image, IFloorplanImageController
{
public event EventHandler Touched;
public void SendTouched()
{
Touched?.Invoke(this, EventArgs.Empty);
}
public Tuple<float, float> TouchedCoordinate
{
get { return (Tuple<float, float>)GetValue(TouchedCoordinateProperty); }
set { SetValue(TouchedCoordinateProperty, value); }
}
public static readonly BindableProperty TouchedCoordinateProperty =
BindableProperty.Create(
propertyName: "TouchedCoordinate",
returnType: typeof(Tuple<float, float>),
declaringType: typeof(FloorplanImage),
defaultValue: new Tuple<float, float>(0, 0),
propertyChanged: OnPropertyChanged);
public static void OnPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
}
}
Implement the custom renderer:
[assembly: ExportRenderer(typeof(FloorplanImage), typeof(FloorplanImageRenderer))]
namespace EmployeeApp.Droid.Platform
{
public class FloorplanImageRenderer : ImageRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
if (Control != null)
{
Control.Clickable = true;
Control.SetOnTouchListener(ImageTouchListener.Instance.Value);
Control.SetTag(Control.Id, new JavaObjectWrapper<FloorplanImage> { Obj = Element as FloorplanImage });
}
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (Control != null)
{
Control.SetOnTouchListener(null);
}
}
base.Dispose(disposing);
}
private class ImageTouchListener : Java.Lang.Object, Android.Views.View.IOnTouchListener
{
public static readonly Lazy<ImageTouchListener> Instance = new Lazy<ImageTouchListener>(
() => new ImageTouchListener());
public bool OnTouch(Android.Views.View v, MotionEvent e)
{
var obj = v.GetTag(v.Id) as JavaObjectWrapper<FloorplanImage>;
var element = obj.Obj;
var controller = element as IFloorplanImageController;
if (e.Action == Android.Views.MotionEventActions.Down)
{
var x = e.GetX();
var y = e.GetY();
element.TouchedCoordinate = new Tuple<float, float>(x, y);
controller?.SendTouched();
}
else if (e.Action == Android.Views.MotionEventActions.Up)
{
}
return false;
}
}
}
public class JavaObjectWrapper<T> : Java.Lang.Object
{
public T Obj { get; set; }
}
}
Use this control like this:
<local:FloorplanImage HeightRequest="300" x:Name="image" WidthRequest="300"
Aspect="AspectFit" Touched="image_Touched" />
code behind:
private void image_Touched(object sender, EventArgs e)
{
var cor = image.TouchedCoordinate;
}

Scrolling to start of Xamarin Forms ListView with header

I'm having some trouble scrolling to the top of a ListView in Xamarin Forms. I can scroll to the first item by calling ScrollTo and passing the first item. The problem is that when the list has a header item, I can't find a way to scroll to the header. Is this possible? The only work around I can think of is to not use the header and just have another item at the start of the ItemSource list that acts as a header but I'd rather use the header if possible. Thanks.
So I've solved this myself now. My solution was to subclass ListView and add a public ScrollToTop method which invokes an internal ScrollToTopRequestedEvent when called. I then subclassed the ListViewRenderer on each platform and registered for the event.
In the Android renderer I'm then calling Control.SmoothScrollToPositionFromTop(0, 0) to scroll to top.
In the iOS rendered I'm calling Control.ScrollRectToVisible(new CoreGraphics.CGRect(0, 0, 1, 1), true).
Wah all credits #Gareth Wynn, man that was cool thx.
Anyway here's the code for everyone to use, change class names and namespace, iOS not included, do same as for Android just using Gareth Wynn's hint in parallel answer:
SHARED NiftyListView.cs :
using System;
using Xamarin.Forms;
namespace AppoMobi
{
public class NiftyListView : CListView
{
public event EventHandler EventScrollToTop;
//-------------------------------------------------------------------
public void ScrollToTop(bool animate=true)
//-------------------------------------------------------------------
{
//bool animate is not used at this stage, it's always animated.
EventScrollToTop?.Invoke(this, EventArgs.Empty);
}
}
}
ANDROID NiftyListView.Android.cs :
using System;
using AppoMobi;
using AppoMobi.Droid.Renderers;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using ListView = Xamarin.Forms.ListView;
[assembly: ExportRenderer(typeof(NiftyListView), typeof(NiftyListViewRenderer))]
namespace AppoMobi.Droid.Renderers
{
//-------------------------------------------------------------------
class NiftyListViewRenderer : ListViewRenderer
//-------------------------------------------------------------------
{
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
var view = (NiftyListView)Element;
if (view == null) return;
view.EventScrollToTop += OnScrollToTop;
}
//-------------------------------------------------------------------------------------------
public async void OnScrollToTop(object sender, EventArgs eventArgs)
//-------------------------------------------------------------------------------------------
{
Control.SmoothScrollToPositionFromTop(0, 0);
}
}
}
ScrollTo(Object, Object, ScrollToPosition, Boolean)
Scrolls the ListView to the item in the group
https://learn.microsoft.com/en-us/dotnet/api/xamarin.forms.listview.scrollto?view=xamarin-forms
Model for each group:
public class Notification {
public int Id { get; set; }
public string Title { get; set; };
public Notification(int id, string title) {
Id = id;
Title = title;
}
}
Group-Model for ItemSource:
public class NotificationGroup: List<Notification> {
public string Title { get; set; }
public string ShortTitle { get; set; }
public NotificationGroup(string title) {
Title = title;
}
}
Sample Data & Usage:
//SAMPLE DATA
var notifications = new ObservableCollection <NotificationGroup> {
new NotificationGroup("Group-01") {
new Notification(1, "Item-1"),
new Notification(2, "Item-2")
},
new NotificationGroup("Group-02") {
new Notification(3, "Item-3"),
new Notification(4, "Item-4")
}
};
YourListViewName.ItemSource = notifications;
//USING
YourListViewName.ScrollTo(notifications.First()[0], notifications.First(), ScrollToPosition.Start, true);

Prism2 Region Adapter for AvalonDock's DocumentPane and DockingPane?

Does anyone have sample code on how to create a Region Adapter for AvalonDock's DocumentPane and DockingPane?
The Markus Raufer has added two region adapters to the CompositeWpfContrib project at CodePlex that supports both DocumentPane and DockingPane.
I have used Raffaeu Bermuda snippets to support Avalon tab region adapter, but found that there is some issues are not solved:
1- It does not support Activating a a certain view (aka - tab - DockableContent), so the code Region.Activate(object view) will not work.
2- All the Tabs are active by default in the region. So Region.ActiveViews collection by default has all the views, this is not ideal, as sometimes I needed to verify if a view is active or not (you could imagine a save button on a tool bar region that executes a SaveCommand only on the current active view = tab in our case)
3- Closed views doesn't actually get closed, only hidden. Even if you set the HideOnClose = true when adding the newDockableContent, it is still not removed from Region.Views collection. This could lead to memory leaks issues.
4- If you have a previously added DockableContent in the Pane, they will not get synchronized and added to the Region.Views collection.
So here are the code I am using now, it is just a small tweak from the Selector Adapter and Selector Sync Behavior found in PRISM source code:
AvalonRegionAdapter Class:
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();
}
}
AvalonDocumentSyncBehavior Behavior Code:
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;
}
}
}
Code on bootstrapper to configure the Adapter
protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
var mappings = base.ConfigureRegionAdapterMappings();
mappings.RegisterMapping(typeof(AvalonDock.DocumentPane),
this.Container.Resolve<AvalonRegionAdapter>());
return mappings;
}
Then you need the TabModel and the TabViewModel as fromRaffaeu 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));
}
And a TabViewModel acting as a base class:
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));
}
}
}
Let me know if you need further help, I will post a blog this in the near future.
Since the Avalon DocumentPane and DockingPane are both based on the System.Windows.Controls.Primitives.Selector you can use the default SelectorRegionAdapter in Prism.
Just base your control on 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>
on your main Shell.xmal set the regions in the dockablepane
<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>
then when you initialize your presenter for your control it will get displayed in the dock.
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; }
}
My advice would be to look in Microsoft.Practices.Composite.Presentation.Regions in the Prism source. Specifically, take a look at the ItemsControlRegionAdapter and use it as a template. Remember to inherit from RegionAdapterBase<>:
public class ItemsControlRegionAdapter : RegionAdapterBase<ItemsControl>
and to override ConfigureRegionAdapterMappings() in the bootstrapper. That would look something like:
protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
RegionAdapterMappings mappings = base.ConfigureRegionAdapterMappings();
mappings.RegisterMapping(typeof(Canvas), Container.Resolve<CanvasRegionAdapter>());
return mappings;
}

Resources