A WPF docking library supporting MVVM - wpf

I've already seen some questions like this one, but the docking library I'd like to use must have an important feature, which was not asked: it must support MVVM.
So, among Telerik, DotNetBar, DevZest, and the other libraries around there (excluding AvalonDock, which I have already tested), is there one you actually use with MVVM?
Thanks in advance

Hello Mike try with this:
Easy way: Implement Sofa, An adaptation of AvalonDock for Prism
Using AvalonDock and implementing a custom region adapter like this:
public class ResizingPanelRegionAdapter : RegionAdapterBase<DockingManager>
{
public ResizingPanelRegionAdapter(IRegionBehaviorFactory factory)
: base(factory)
{
}
protected override IRegion CreateRegion()
{
return new AllActiveRegion();
}
protected override void Adapt(IRegion region, DockingManager regionTarget)
{
region.Views.CollectionChanged += delegate(Object sender, NotifyCollectionChangedEventArgs e)
{
OnViewsCollectionChanged(sender, e, region, regionTarget);
};
}
private void OnViewsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e, IRegion region, DockingManager regionTarget)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (object item in e.NewItems)
{
UIElement view = item as UIElement;
if (view != null)
{
//Get
ResizingPanel resizingPanel = GetResizingPanel(regionTarget.Content);
resizingPanel.Background = Brushes.White;
DocumentPane document = GetDocumentPane(resizingPanel.Children);
//document.Background = Brushes.White;
DocumentContent newContentPane = new DocumentContent();
newContentPane.Content = item;
var itemView = (item as IViewBase);
if (itemView != null)
newContentPane.Title = itemView.Title;
//When contentPane is closed remove the associated region
newContentPane.Closed += (contentPaneSender, args) =>
{
region.Remove(item);
newContentPane.Content = null;
};
document.Items.Add(newContentPane);
if (!resizingPanel.Children.Contains(document))
resizingPanel.Children.Add(document);
regionTarget.Content = resizingPanel;
newContentPane.Activate();
region.Activate(item);
}
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
}
}
private DocumentPane GetDocumentPane(UIElementCollection collection)
{
foreach (object item in collection)
{
var documentPanel = item as DocumentPane;
if (documentPanel != null)
return documentPanel;
}
return new DocumentPane();
}
private ResizingPanel GetResizingPanel(object content)
{
var resizingPanel = content as ResizingPanel;
if (resizingPanel != null)
return resizingPanel;
return new ResizingPanel();
}
}
And your in your XAML you could implement it like this:
<avalon:DockingManager prism:RegionManager.RegionName="MainRegion">
</avalon:DockingManager>
How it works?
Simple, first at all you have to keep in mind that Region adapters are responsible for creating a region and associating it with the control. This allows you to use the IRegion interface to manage the UI control contents in a consistent way.
And a DockingManager is the core control in AvalonDock. It arranges contained panes, handles fly out panes and floating windows.
So, following this example you could have implemented a custom region adapter for avalon, I worked with this implementation in a project getting awesome results.
Regards

Related

How to implement this scenario in wpf

I'm allowing two instance of wpf application
private void Application_Startup(object sender, StartupEventArgs e)
{
Process thisProc = Process.GetCurrentProcess();
if (Process.GetProcessesByName(thisProc.ProcessName).Length == 1)
{
var w1 = new MainWindow("1");
w1.Show();
}
else if (Process.GetProcessesByName(thisProc.ProcessName).Length == 2)
{
var w2 = new MainWindow("2");
w2.Show();
}
}
Each window having different user setting like below
In Settings.setting
window1_city="xxx",window1_country="yyy"
window2_city="aaa",window2_country="bbb" .....etc
I want to apply the above settings on windows load(in constructor).
My current code is
public MainWindow(string window)
{
if(window=="1")
{
lbl.Content=setting.window1_city
}
if(window=="2")
{
lbl.Content=setting.window2_city
}
}and so
But this is very difficult to apply if no.of user settings more. If user made any changes then I've to save those values according to window. So I've to use more no.of if loops.
Is there any other way to implement this?
Use each window DataContext property to pass an object with all property needed, pass that object in the Startup, before Show.
Maybe you would better use some MVVM toolkit, if the app is going to be more complex.
An example in using plain DataContextes:
private void Application_Startup(object sender, StartupEventArgs e)
{
Process thisProc = Process.GetCurrentProcess();
if (Process.GetProcessesByName(thisProc.ProcessName).Length == 1)
{
var w1 = new MainWindow("1");
var a = new MySettings();
a.x=...;
a.y=....;
w1.DataContext=a;
w1.Show();
}
else if (Process.GetProcessesByName(thisProc.ProcessName).Length == 2)
{
var w2 = new MainWindow("2");
var b = new MySettings();
b.x=...;
b.y=....;
w2.DataContext=b;
w2.Show();
}
}

views navigation prism

Hey guys,
I'm using prism 4 to implement my presentation,
the thing is that i'm using view that contains region,now I want to navigate to other instance of the view under the same scope so I set the KeepAlive property of the view to false so that in navigation the view will be removed from the region and the new view will appear ,but I'm keep getting region name already exist exception.
how can I navigate between few instances of the same view that contain region (only one should be in memory at the time)
Thanks
Eran
It sounds like you want to use a scoped RegionManager with navigation. The reason you're seeing the region name already exists exception is because you have more than one region with the same name in the same RegionManager.
By default, PRISM doesn't support scoped RegionManagers with navigation, but it's pretty easy to add this in if you use custom RegionBehaviors. Essentially what you need to do is create an interface and then implement that interface on your view or view model. Then, create a RegionBehavior that looks for that interface and, if it meets the requirements, creates a new RegionManager instance for that view.
Here's what the interface might look like:
public interface IRegionScopeAware
{
bool IsRegionManagerScoped { get; }
}
And here's what the RegionBehavior might look like:
public class RegionScopeAwareBehavior : RegionBehavior
{
#region Overrides of RegionBehavior
protected override void OnAttach()
{
Region.Views.CollectionChanged += ViewsOnCollectionChanged;
ApplyScopedRegionManager(Region.Views.OfType<FrameworkElement>());
}
#endregion
private static void ViewsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems == null || e.Action != NotifyCollectionChangedAction.Add) return;
ApplyScopedRegionManager(e.NewItems.OfType<DependencyObject>());
}
private static void ApplyScopedRegionManager(IEnumerable<DependencyObject> views)
{
if (views == null) return;
foreach (var view in views)
{
ApplyScopedRegionManager(view);
}
}
private static void ApplyScopedRegionManager(DependencyObject view)
{
if (view == null) return;
IRegionScopeAware scopeAware = view as IRegionScopeAware;
if (scopeAware == null && view is FrameworkElement)
scopeAware = ((FrameworkElement) view).DataContext as IRegionScopeAware;
if (scopeAware != null)
ApplyScopedRegionManager(scopeAware, view);
}
private static void ApplyScopedRegionManager(IRegionScopeAware scopeAware, DependencyObject view)
{
if (view == null) return;
if (scopeAware == null) return;
if (scopeAware.IsRegionManagerScoped)
RegionManager.SetRegionManager(view, new RegionManager());
}
}
And don't forget that you'll need to register your RegionBehavior. I suggest registering it as a default RegionBehavior in the bootstrapper like so:
protected override IRegionBehaviorFactory ConfigureDefaultRegionBehaviors()
{
IRegionBehaviorFactory factory = base.ConfigureDefaultRegionBehaviors();
factory.AddIfMissing(typeof(RegionScopeAwareBehavior).Name, typeof(RegionScopeAwareBehavior));
return factory;
}

Prism, AvalonDock Region Adapter (Handling close DocumentPane event)

I have a Prism v4 / MEF / WPF solution that loads module views into a DocumentPane in my Avalon Dock. I'm trying to create a handle on the close event (when the 'X' is pressed to close a DocumentPane). I can't figure it out, this is the portion of my RegionAdapter class that creates the DockableContent and also where I attempt to handle the close event (I tried with 3 events):
private void OnViewsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e, IRegion region, DocumentPane regionTarget)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (object item in e.NewItems)
{
UIElement view = item as UIElement;
if (view != null)
{
DockableContent newContentPane = new DockableContent()
{
Content = item,
Title = ((ITabViewInfo)view).TabViewTitle
Icon = ((ITabViewInfo)view).TabViewIcon
};
newContentPane.Closed += new EventHandler(newContentPane_Closed);
newContentPane.Manager.DocumentClosed +=new EventHandler(Manager_DocumentClosed);
newContentPane.Manager.DocumentClosing +=new EventHandler<System.ComponentModel.CancelEventArgs>(Manager_DocumentClosing);
regionTarget.Items.Add(newContentPane);
newContentPane.Activate();
}
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
// this is never hit
Debug.WriteLine("removed");
}
}
These events are never hit (I have a debug.writeline in each one):
newContentPane.Closed += new EventHandler(newContentPane_Closed);
newContentPane.Manager.DocumentClosed +=new EventHandler(Manager_DocumentClosed);
newContentPane.Manager.DocumentClosing +=new EventHandler<System.ComponentModel.CancelEventArgs>(Manager_DocumentClosing);
How can I handle the close event of a DocumentPane?
The code I used for this RegionAdapter is based off of http://blog.raffaeu.com/archive/2010/07/04/wpf-and-prism-tab-region-adapter-part-02.aspx
Found the answer here http://avalondock.codeplex.com/discussions/231982?ProjectName=avalondock
I had to add:
IsCloseable = true
HideOnClose = false

Drag and Drop between 2 list boxes

Trying to implement drag and drop between 2 listboxes and all examples I've seen so far don't really smell good.
Can someone point me to or show me a good implementation?
Here's a sample form. Get started with a new WF project and drop two list boxes on the form. Make the code look like this:
public partial class Form1 : Form {
public Form1() {
InitializeComponent();
listBox1.Items.AddRange(new object[] { "one", "two", "three" });
listBox1.MouseDown += new MouseEventHandler(listBox1_MouseDown);
listBox1.MouseMove += new MouseEventHandler(listBox1_MouseMove);
listBox2.AllowDrop = true;
listBox2.DragEnter += new DragEventHandler(listBox2_DragEnter);
listBox2.DragDrop += new DragEventHandler(listBox2_DragDrop);
}
private Point mDownPos;
void listBox1_MouseDown(object sender, MouseEventArgs e) {
mDownPos = e.Location;
}
void listBox1_MouseMove(object sender, MouseEventArgs e) {
if (e.Button != MouseButtons.Left) return;
int index = listBox1.IndexFromPoint(e.Location);
if (index < 0) return;
if (Math.Abs(e.X - mDownPos.X) >= SystemInformation.DragSize.Width ||
Math.Abs(e.Y - mDownPos.Y) >= SystemInformation.DragSize.Height)
DoDragDrop(new DragObject(listBox1, listBox1.Items[index]), DragDropEffects.Move);
}
void listBox2_DragEnter(object sender, DragEventArgs e) {
DragObject obj = e.Data.GetData(typeof(DragObject)) as DragObject;
if (obj != null && obj.source != listBox2) e.Effect = e.AllowedEffect;
}
void listBox2_DragDrop(object sender, DragEventArgs e) {
DragObject obj = e.Data.GetData(typeof(DragObject)) as DragObject;
listBox2.Items.Add(obj.item);
obj.source.Items.Remove(obj.item);
}
private class DragObject {
public ListBox source;
public object item;
public DragObject(ListBox box, object data) { source = box; item = data; }
}
}
the proper way to do a drag-drop control in .net is by running code in the 2nd control's DragDrop event handler.
It may "smell" weird, but this is how it works in .NET.
Google gave this: http://www.codeproject.com/KB/dotnet/csdragndrop01.aspx
It seems a pretty reasonable tutorial. If it smells bad, I think that's more to do with the API for drag and drop being awkward to use rather than the tutorial itself being poor.

How can I use a Panel as a Region in Prism?

The prism documentation states that there are three region adapters available:
ContentControlRegionAdapter. This adapter adapts controls of type System.Windows.Controls.ContentControl and derived classes.
SelectorRegionAdapter. This adapter adapts controls derived from the class System.Windows.Controls.Primitives.Selector, such as the System.Windows.Controls.TabControl control.
ItemsControlRegionAdapter. This adapter adapts controls of type System.Windows.Controls.ItemsControl and derived classes.
Unfortunately, Panel does not fall into any of those categories, and I want to be able to write this in my .xaml.cs:
<Canvas cal:RegionManager.RegionName="{x:Static local:RegionNames.MainCanvas}">
How can we accomplish this?
The answer to this can be found in this very nice, descriptive blog post.
However, I want the answer stored on StackOverflow as well :) It took a bit of searching to get this from Google. Here is my code that works with a basic Panel.
Step 1 - create a new region adapter
public class PanelHostRegionAdapter : RegionAdapterBase<Panel>
{
public PanelHostRegionAdapter(IRegionBehaviorFactory behaviorFactory)
: base(behaviorFactory)
{
}
protected override void Adapt(IRegion region, Panel regionTarget)
{
region.Views.CollectionChanged += (s, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (FrameworkElement element in e.NewItems)
{
regionTarget.Children.Add(element);
}
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
foreach (FrameworkElement CurrentElement in e.OldItems)
regionTarget.Children.Remove(CurrentElement);
}
};
}
protected override IRegion CreateRegion()
{
return new AllActiveRegion();
}
}
Step 2 - update your bootstrapper
public class Bootstrapper : UnityBootstrapper
{
protected override DependencyObject CreateShell()
{
...
}
protected override IModuleCatalog GetModuleCatalog()
{
...
}
protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
RegionAdapterMappings Mappings = base.ConfigureRegionAdapterMappings();
Mappings.RegisterMapping(typeof(Panel), Container.Resolve<PanelHostRegionAdapter>());
return Mappings;
}
}

Resources