项目结构调整

This commit is contained in:
艾竹
2023-04-16 20:11:40 +08:00
parent cbfbf96033
commit 81f91f3f35
2124 changed files with 218 additions and 5516 deletions

22
Others/Dragablz/.gitattributes vendored Normal file
View File

@@ -0,0 +1,22 @@
# Auto detect text files and perform LF normalization
* text=auto
# Custom for Visual Studio
*.cs diff=csharp
*.sln merge=union
*.csproj merge=union
*.vbproj merge=union
*.fsproj merge=union
*.dbproj merge=union
# Standard to msysgit
*.doc diff=astextplain
*.DOC diff=astextplain
*.docx diff=astextplain
*.DOCX diff=astextplain
*.dot diff=astextplain
*.DOT diff=astextplain
*.pdf diff=astextplain
*.PDF diff=astextplain
*.rtf diff=astextplain
*.RTF diff=astextplain

234
Others/Dragablz/.gitignore vendored Normal file
View File

@@ -0,0 +1,234 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
# Visual Studio 2015+ cache/options directory
.vs/
# Roslyn cache directories
*.ide/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
#NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding addin-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# If using the old MSBuild-Integrated Package Restore, uncomment this:
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# =========================
# Operating System Files
# =========================
# OSX
# =========================
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear on external disk
.Spotlight-V100
.Trashes
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# Windows
# =========================
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Windows Installer files
*.cab
*.msi
*.msm

View File

@@ -0,0 +1,105 @@
using NUnit.Framework;
using System.Collections;
using System.Collections.Generic;
using FakeItEasy;
namespace Dragablz.Core.Tests
{
[TestFixture]
public class CollectionTeaserFixture
{
[Test]
public void WillCreateForList()
{
var myList = new ArrayList();
CollectionTeaser collectionTeaser;
var result = CollectionTeaser.TryCreate(myList, out collectionTeaser);
Assert.IsTrue(result);
Assert.IsNotNull(collectionTeaser);
}
[Test]
public void WillCreateForGenericCollection()
{
var myList = A.Fake<ICollection<string>>();
CollectionTeaser collectionTeaser;
var result = CollectionTeaser.TryCreate(myList, out collectionTeaser);
Assert.IsTrue(result);
Assert.IsNotNull(collectionTeaser);
}
[Test]
public void WillCreateForCollection()
{
var myList = A.Fake<ICollection>();
CollectionTeaser collectionTeaser;
var result = CollectionTeaser.TryCreate(myList, out collectionTeaser);
Assert.IsFalse(result);
Assert.IsNull(collectionTeaser);
}
[Test]
public void WillAddForList()
{
var myList = new ArrayList();
CollectionTeaser collectionTeaser;
Assert.IsTrue(CollectionTeaser.TryCreate(myList, out collectionTeaser));
collectionTeaser.Add("i am going to type this in, manually, twice.");
CollectionAssert.AreEquivalent(new[] { "i am going to type this in, manually, twice." }, myList);
//i didnt really. i copied and pasted it.
}
[Test]
public void WillRemoveForList()
{
var myList = new ArrayList
{
1,
2,
3,
4,
5
};
CollectionTeaser collectionTeaser;
Assert.IsTrue(CollectionTeaser.TryCreate(myList, out collectionTeaser));
collectionTeaser.Remove(3);
CollectionAssert.AreEquivalent(new[] { 1, 2, 4, 5 }, myList);
}
[Test]
public void WillAddForGenericCollection()
{
var myList = A.Fake<ICollection<string>>();
CollectionTeaser collectionTeaser;
Assert.IsTrue(CollectionTeaser.TryCreate(myList, out collectionTeaser));
collectionTeaser.Add("hello");
A.CallTo(() => myList.Add("hello")).MustHaveHappened();
A.CallTo(() => myList.Remove("hello")).MustNotHaveHappened();
}
[Test]
public void WillRemoveForGenericCollection()
{
var myList = A.Fake<ICollection<string>>();
CollectionTeaser collectionTeaser;
Assert.IsTrue(CollectionTeaser.TryCreate(myList, out collectionTeaser));
collectionTeaser.Remove("bye");
A.CallTo(() => myList.Remove("bye")).MustHaveHappened();
A.CallTo(() => myList.Add("bye")).MustNotHaveHappened();
}
}
}

View File

@@ -0,0 +1,28 @@
using NUnit.Framework;
namespace Dragablz.Dockablz.Tests
{
[TestFixture]
public class TilerCalculatorFixture
{
[TestCase(0, new int[0])]
[TestCase(1, new[] { 1 })]
[TestCase(2, new[] { 1, 1 })]
[TestCase(3, new[] { 1, 2 })]
[TestCase(4, new[] { 2, 2 })]
[TestCase(5, new[] { 2, 3 })]
[TestCase(6, new[] { 3, 3 })]
[TestCase(7, new[] { 2, 2, 3 })]
[TestCase(9, new[] { 3, 3, 3 })]
[TestCase(10, new[] { 3, 3, 4 })]
[TestCase(25, new[] { 5, 5, 5, 5, 5 })]
[TestCase(26, new[] { 5, 5, 5, 5, 6 })]
[TestCase(29, new[] { 5, 6, 6, 6, 6 })]
[TestCase(30, new[] { 6, 6, 6, 6, 6 })]
public void WillCalculateCellsPerColumn(int totalCells, int[] expectedCellsPerColumn)
{
var result = TilerCalculator.GetCellCountPerColumn(totalCells);
CollectionAssert.AreEqual(expectedCellsPerColumn, result);
}
}
}

View File

@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.0;net45;net40</TargetFrameworks>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="1.1.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FakeItEasy" Version="5.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Dragablz\Dragablz.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,65 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29424.173
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dragablz", "Dragablz\Dragablz.csproj", "{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DragablzDemo", "DragablzDemo\DragablzDemo.csproj", "{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dragablz.Test", "Dragablz.Test\Dragablz.Test.csproj", "{F8636090-2153-401A-A00F-C5ADB66EF612}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}.Debug|x86.ActiveCfg = Debug|Any CPU
{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}.Debug|x86.Build.0 = Debug|Any CPU
{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}.Release|Any CPU.Build.0 = Release|Any CPU
{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}.Release|x86.ActiveCfg = Release|Any CPU
{C4D2C8A3-2B3A-46C6-83D3-6DCC83B34646}.Release|x86.Build.0 = Release|Any CPU
{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}.Debug|x86.ActiveCfg = Debug|Any CPU
{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}.Debug|x86.Build.0 = Debug|Any CPU
{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}.Release|Any CPU.Build.0 = Release|Any CPU
{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}.Release|x86.ActiveCfg = Release|Any CPU
{2B28B8D0-67E7-461C-97C8-8A2ECA6C9314}.Release|x86.Build.0 = Release|Any CPU
{F8636090-2153-401A-A00F-C5ADB66EF612}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F8636090-2153-401A-A00F-C5ADB66EF612}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F8636090-2153-401A-A00F-C5ADB66EF612}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{F8636090-2153-401A-A00F-C5ADB66EF612}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{F8636090-2153-401A-A00F-C5ADB66EF612}.Debug|x86.ActiveCfg = Debug|Any CPU
{F8636090-2153-401A-A00F-C5ADB66EF612}.Debug|x86.Build.0 = Debug|Any CPU
{F8636090-2153-401A-A00F-C5ADB66EF612}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F8636090-2153-401A-A00F-C5ADB66EF612}.Release|Any CPU.Build.0 = Release|Any CPU
{F8636090-2153-401A-A00F-C5ADB66EF612}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{F8636090-2153-401A-A00F-C5ADB66EF612}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{F8636090-2153-401A-A00F-C5ADB66EF612}.Release|x86.ActiveCfg = Release|Any CPU
{F8636090-2153-401A-A00F-C5ADB66EF612}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AD581326-677F-4EEB-8B87-71546D0AAF5C}
EndGlobalSection
EndGlobal

View File

@@ -0,0 +1,2 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/CodeAnnotations/NamespacesWithAnnotations/=DragablzDemo_002EAnnotations/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

View File

@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
namespace Dragablz
{
public class CanvasOrganiser : IItemsOrganiser
{
public virtual void Organise(DragablzItemsControl requestor, Size measureBounds, IEnumerable<DragablzItem> items)
{
}
public virtual void Organise(DragablzItemsControl requestor, Size measureBounds, IOrderedEnumerable<DragablzItem> items)
{
}
public virtual void OrganiseOnMouseDownWithin(DragablzItemsControl requestor, Size measureBounds, List<DragablzItem> siblingItems, DragablzItem dragablzItem)
{
var zIndex = int.MaxValue;
foreach (var source in siblingItems.OrderByDescending(Panel.GetZIndex))
{
Panel.SetZIndex(source, --zIndex);
}
Panel.SetZIndex(dragablzItem, int.MaxValue);
}
public virtual void OrganiseOnDragStarted(DragablzItemsControl requestor, Size measureBounds, IEnumerable<DragablzItem> siblingItems, DragablzItem dragItem)
{
}
public virtual void OrganiseOnDrag(DragablzItemsControl requestor, Size measureBounds, IEnumerable<DragablzItem> siblingItems, DragablzItem dragItem)
{
}
public virtual void OrganiseOnDragCompleted(DragablzItemsControl requestor, Size measureBounds, IEnumerable<DragablzItem> siblingItems, DragablzItem dragItem)
{
}
public virtual Point ConstrainLocation(DragablzItemsControl requestor, Size measureBounds, Point itemCurrentLocation, Size itemCurrentSize, Point itemDesiredLocation, Size itemDesiredSize)
{
//we will stop it pushing beyond the bounds...unless it's already beyond...
var reduceBoundsWidth = itemCurrentLocation.X + itemCurrentSize.Width > measureBounds.Width
? 0
: itemDesiredSize.Width;
var reduceBoundsHeight = itemCurrentLocation.Y + itemCurrentSize.Height > measureBounds.Height
? 0
: itemDesiredSize.Height;
return new Point(
Math.Min(Math.Max(itemDesiredLocation.X, 0), measureBounds.Width - reduceBoundsWidth),
Math.Min(Math.Max(itemDesiredLocation.Y, 0), measureBounds.Height - reduceBoundsHeight));
}
public virtual Size Measure(DragablzItemsControl requestor, Size availableSize, IEnumerable<DragablzItem> items)
{
return availableSize;
}
public virtual IEnumerable<DragablzItem> Sort(IEnumerable<DragablzItem> items)
{
return items;
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Windows;
namespace Dragablz
{
internal class ContainerCustomisations
{
private readonly Func<DragablzItem> _getContainerForItemOverride;
private readonly Action<DependencyObject, object> _prepareContainerForItemOverride;
private readonly Action<DependencyObject, object> _clearingContainerForItemOverride;
public ContainerCustomisations(Func<DragablzItem> getContainerForItemOverride = null, Action<DependencyObject, object> prepareContainerForItemOverride = null, Action<DependencyObject, object> clearingContainerForItemOverride = null)
{
_getContainerForItemOverride = getContainerForItemOverride;
_prepareContainerForItemOverride = prepareContainerForItemOverride;
_clearingContainerForItemOverride = clearingContainerForItemOverride;
}
public Func<DragablzItem> GetContainerForItemOverride
{
get { return _getContainerForItemOverride; }
}
public Action<DependencyObject, object> PrepareContainerForItemOverride
{
get { return _prepareContainerForItemOverride; }
}
public Action<DependencyObject, object> ClearingContainerForItemOverride
{
get { return _clearingContainerForItemOverride; }
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;
namespace Dragablz.Converters
{
public class BooleanAndToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null)
return Visibility.Collapsed;
return values.Select(GetBool).All(b => b)
? Visibility.Visible
: Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
private static bool GetBool(object value)
{
if (value is bool)
{
return (bool)value;
}
return false;
}
}
}

View File

@@ -0,0 +1,19 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace Dragablz.Converters
{
public class EqualityToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Equals(value, parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}

View File

@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace Dragablz.Converters
{
public class EqualityToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return Equals(value, parameter)
? Visibility.Visible
: Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace Dragablz.Converters
{
public class ShowDefaultCloseButtonConverter : IMultiValueConverter
{
/// <summary>
/// [0] is owning tabcontrol ShowDefaultCloseButton value.
/// [1] is owning tabcontrol FixedHeaderCount value.
/// [2] is item LogicalIndex
/// </summary>
/// <param name="values"></param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return ((values[0] == DependencyProperty.UnsetValue ? false : (bool)values[0]) &&
(values[2] == DependencyProperty.UnsetValue ? 0 : (int)values[2]) >=
(values[1] == DependencyProperty.UnsetValue ? 0 : (int)values[1])) ? Visibility.Visible : Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
}

View File

@@ -0,0 +1,65 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Dragablz.Core
{
internal class CollectionTeaser
{
private readonly Action<object> _addMethod;
private readonly Action<object> _removeMethod;
private CollectionTeaser(Action<object> addMethod, Action<object> removeMethod)
{
_addMethod = addMethod;
_removeMethod = removeMethod;
}
public static bool TryCreate(object items, out CollectionTeaser collectionTeaser)
{
collectionTeaser = null;
var list = items as IList;
if (list != null)
{
collectionTeaser = new CollectionTeaser(i => list.Add(i), list.Remove);
}
else if (items != null)
{
var itemsType = items.GetType();
var genericCollectionType = typeof (ICollection<>);
//TODO, *IF* we really wanted to we could get the consumer to inform us of the correct type
//if there are multiple impls. havent got time for this edge case right now though
var collectionImplType = itemsType.GetInterfaces().SingleOrDefault(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == genericCollectionType);
if (collectionImplType != null)
{
var genericArgType = collectionImplType.GetGenericArguments().First();
var addMethodInfo = collectionImplType.GetMethod("Add", new[] {genericArgType});
var removeMethodInfo = collectionImplType.GetMethod("Remove", new[] { genericArgType });
collectionTeaser = new CollectionTeaser(
i => addMethodInfo.Invoke(items, new[] {i}),
i => removeMethodInfo.Invoke(items, new[] {i}));
}
}
return collectionTeaser != null;
}
public void Add(object item)
{
_addMethod(item);
}
public void Remove(object item)
{
_removeMethod(item);
}
}
}

View File

@@ -0,0 +1,127 @@
#if NET40
using System.Collections;
using System.Reflection;
#endif
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace Dragablz.Core
{
internal static class Extensions
{
public static IEnumerable<TContainer> Containers<TContainer>(this ItemsControl itemsControl) where TContainer : class
{
#if NET40
var fieldInfo = typeof(ItemContainerGenerator).GetField("_items", BindingFlags.NonPublic | BindingFlags.Instance);
var list = (IList)fieldInfo.GetValue(itemsControl.ItemContainerGenerator);
for (var i = 0; i < list.Count; i++)
#else
for (var i = 0; i < itemsControl.ItemContainerGenerator.Items.Count; i++)
#endif
{
var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as TContainer;
if (container != null)
yield return container;
}
}
public static IEnumerable<TObject> Except<TObject>(this IEnumerable<TObject> first, params TObject[] second)
{
return first.Except((IEnumerable<TObject>)second);
}
public static IEnumerable<object> LogicalTreeDepthFirstTraversal(this DependencyObject node)
{
if (node == null) yield break;
yield return node;
foreach (var child in LogicalTreeHelper.GetChildren(node).OfType<DependencyObject>()
.SelectMany(depObj => depObj.LogicalTreeDepthFirstTraversal()))
yield return child;
}
public static IEnumerable<object> VisualTreeDepthFirstTraversal(this DependencyObject node)
{
if (node == null) yield break;
yield return node;
for (var i = 0; i < VisualTreeHelper.GetChildrenCount(node); i++)
{
var child = VisualTreeHelper.GetChild(node, i);
foreach (var d in child.VisualTreeDepthFirstTraversal())
{
yield return d;
}
}
}
/// <summary>
/// Yields the visual ancestory (including the starting point).
/// </summary>
/// <param name="dependencyObject"></param>
/// <returns></returns>
public static IEnumerable<DependencyObject> VisualTreeAncestory(this DependencyObject dependencyObject)
{
if (dependencyObject == null) throw new ArgumentNullException("dependencyObject");
while (dependencyObject != null)
{
yield return dependencyObject;
dependencyObject = VisualTreeHelper.GetParent(dependencyObject);
}
}
/// <summary>
/// Yields the logical ancestory (including the starting point).
/// </summary>
/// <param name="dependencyObject"></param>
/// <returns></returns>
public static IEnumerable<DependencyObject> LogicalTreeAncestory(this DependencyObject dependencyObject)
{
if (dependencyObject == null) throw new ArgumentNullException("dependencyObject");
while (dependencyObject != null)
{
yield return dependencyObject;
dependencyObject = LogicalTreeHelper.GetParent(dependencyObject);
}
}
/// <summary>
/// Returns the actual Left of the Window independently from the WindowState
/// </summary>
/// <param name="window"></param>
/// <returns></returns>
public static double GetActualLeft(this Window window)
{
if (window.WindowState == WindowState.Maximized)
{
var leftField = typeof(Window).GetField("_actualLeft", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
return leftField?.GetValue(window) as double? ?? 0;
}
return window.Left;
}
/// <summary>
/// Returns the actual Top of the Window independently from the WindowState
/// </summary>
/// <param name="window"></param>
/// <returns></returns>
public static double GetActualTop(this Window window)
{
if (window.WindowState == WindowState.Maximized)
{
var topField = typeof(Window).GetField("_actualTop", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
return topField?.GetValue(window) as double? ?? 0;
}
return window.Top;
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
namespace Dragablz.Core
{
internal class FuncComparer<TObject> : IComparer<TObject>
{
private readonly Func<TObject, TObject, int> _comparer;
public FuncComparer(Func<TObject, TObject, int> comparer)
{
if (comparer == null) throw new ArgumentNullException("comparer");
_comparer = comparer;
}
public int Compare(TObject x, TObject y)
{
return _comparer(x, y);
}
}
}

View File

@@ -0,0 +1,33 @@
namespace Dragablz.Core
{
/// <summary>
/// Non-client hit test values, HT*
/// </summary>
internal enum HitTest
{
HT_ERROR = -2,
HT_TRANSPARENT = -1,
HT_NOWHERE = 0,
HT_CLIENT = 1,
HT_CAPTION = 2,
HT_SYSMENU = 3,
HT_GROWBOX = 4,
HT_MENU = 5,
HT_HSCROLL = 6,
HT_VSCROLL = 7,
HT_MINBUTTON = 8,
HT_MAXBUTTON = 9,
HT_LEFT = 10,
HT_RIGHT = 11,
HT_TOP = 12,
HT_TOPLEFT = 13,
HT_TOPRIGHT = 14,
HT_BOTTOM = 15,
HT_BOTTOMLEFT = 16,
HT_BOTTOMRIGHT = 17,
HT_BORDER = 18,
HT_OBJECT = 19,
HT_CLOSE = 20,
HT_HELP = 21
}
}

View File

@@ -0,0 +1,112 @@
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using Dragablz.Dockablz;
namespace Dragablz.Core
{
internal enum InterTabTransferReason
{
Breach,
Reentry
}
internal class InterTabTransfer
{
private readonly object _item;
private readonly DragablzItem _originatorContainer;
private readonly Orientation _breachOrientation;
private readonly Point _dragStartWindowOffset;
private readonly Point _dragStartItemOffset;
private readonly Point _itemPositionWithinHeader;
private readonly Size _itemSize;
private readonly IList<FloatingItemSnapShot> _floatingItemSnapShots;
private readonly bool _isTransposing;
private readonly InterTabTransferReason _transferReason;
public InterTabTransfer(object item, DragablzItem originatorContainer, Orientation breachOrientation, Point dragStartWindowOffset, Point dragStartItemOffset, Point itemPositionWithinHeader, Size itemSize, IList<FloatingItemSnapShot> floatingItemSnapShots, bool isTransposing)
{
if (item == null) throw new ArgumentNullException("item");
if (originatorContainer == null) throw new ArgumentNullException("originatorContainer");
if (floatingItemSnapShots == null) throw new ArgumentNullException("floatingItemSnapShots");
_transferReason = InterTabTransferReason.Breach;
_item = item;
_originatorContainer = originatorContainer;
_breachOrientation = breachOrientation;
_dragStartWindowOffset = dragStartWindowOffset;
_dragStartItemOffset = dragStartItemOffset;
_itemPositionWithinHeader = itemPositionWithinHeader;
_itemSize = itemSize;
_floatingItemSnapShots = floatingItemSnapShots;
_isTransposing = isTransposing;
}
public InterTabTransfer(object item, DragablzItem originatorContainer, Point dragStartItemOffset,
IList<FloatingItemSnapShot> floatingItemSnapShots)
{
if (item == null) throw new ArgumentNullException("item");
if (originatorContainer == null) throw new ArgumentNullException("originatorContainer");
if (floatingItemSnapShots == null) throw new ArgumentNullException("floatingItemSnapShots");
_transferReason = InterTabTransferReason.Reentry;
_item = item;
_originatorContainer = originatorContainer;
_dragStartItemOffset = dragStartItemOffset;
_floatingItemSnapShots = floatingItemSnapShots;
}
public Orientation BreachOrientation
{
get { return _breachOrientation; }
}
public Point DragStartWindowOffset
{
get { return _dragStartWindowOffset; }
}
public object Item
{
get { return _item; }
}
public DragablzItem OriginatorContainer
{
get { return _originatorContainer; }
}
public InterTabTransferReason TransferReason
{
get { return _transferReason; }
}
public Point DragStartItemOffset
{
get { return _dragStartItemOffset; }
}
public Point ItemPositionWithinHeader
{
get { return _itemPositionWithinHeader; }
}
public Size ItemSize
{
get { return _itemSize; }
}
public IList<FloatingItemSnapShot> FloatingItemSnapShots
{
get { return _floatingItemSnapShots; }
}
public bool IsTransposing
{
get { return _isTransposing; }
}
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Dragablz.Core
{
internal class MultiComparer<TObject> : IComparer<TObject>
{
private readonly IList<FuncComparer<TObject>> _attributeComparers;
private MultiComparer(FuncComparer<TObject> firstComparer)
{
_attributeComparers = new List<FuncComparer<TObject>>
{
firstComparer
};
}
public static MultiComparer<TObject> Ascending<TAttribute>(Func<TObject, TAttribute> accessor)
where TAttribute : IComparable
{
if (accessor == null) throw new ArgumentNullException("accessor");
return new MultiComparer<TObject>(BuildAscendingComparer(accessor));
}
public static MultiComparer<TObject> Descending<TAttribute>(Func<TObject, TAttribute> accessor)
where TAttribute : IComparable
{
if (accessor == null) throw new ArgumentNullException("accessor");
return new MultiComparer<TObject>(BuildDescendingComparer(accessor));
}
public MultiComparer<TObject> ThenAscending<TAttribute>(Func<TObject, TAttribute> accessor)
where TAttribute : IComparable
{
if (accessor == null) throw new ArgumentNullException("accessor");
_attributeComparers.Add(BuildAscendingComparer(accessor));
return this;
}
public MultiComparer<TObject> ThenDescending<TAttribute>(Func<TObject, TAttribute> accessor)
where TAttribute : IComparable
{
if (accessor == null) throw new ArgumentNullException("accessor");
_attributeComparers.Add(BuildDescendingComparer(accessor));
return this;
}
public int Compare(TObject x, TObject y)
{
var nonEqual = _attributeComparers.Select(c => new {result = c.Compare(x, y)}).FirstOrDefault(a => a.result != 0);
return nonEqual == null ? 0 : nonEqual.result;
}
private static FuncComparer<TObject> BuildAscendingComparer<TAttribute>(Func<TObject, TAttribute> accessor)
where TAttribute : IComparable
{
//TODO handle ref types better
return new FuncComparer<TObject>((x, y) => accessor(x).CompareTo(accessor(y)));
}
private static FuncComparer<TObject> BuildDescendingComparer<TAttribute>(Func<TObject, TAttribute> accessor)
where TAttribute : IComparable
{
//TODO handle ref types better
return new FuncComparer<TObject>((x, y) => accessor(y).CompareTo(accessor(x)));
}
}
}

View File

@@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
namespace Dragablz.Core
{
internal static class Native
{
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public static implicit operator Point(POINT point)
{
return new Point(point.X, point.Y);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
private static extern bool GetCursorPos(out POINT lpPoint);
public static POINT GetRawCursorPos()
{
POINT lpPoint;
GetCursorPos(out lpPoint);
return lpPoint;
}
public static Point GetCursorPos()
{
POINT lpPoint;
GetCursorPos(out lpPoint);
return lpPoint;
}
[DllImport("User32.dll")]
private static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32.dll")]
private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
[DllImport("user32.dll")]
private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
public static Point ToWpf(this Point pixelPoint)
{
var desktop = GetDC(IntPtr.Zero);
var dpi = GetDeviceCaps(desktop, 88);
ReleaseDC(IntPtr.Zero, desktop);
var physicalUnitSize = 96d / dpi ;
var wpfPoint = new Point(physicalUnitSize * pixelPoint.X, physicalUnitSize * pixelPoint.Y);
return wpfPoint;
}
public static IEnumerable<Window> SortWindowsTopToBottom(IEnumerable<Window> windows)
{
var windowsByHandle = windows.Select(window =>
{
var hwndSource = PresentationSource.FromVisual(window) as HwndSource;
var handle = hwndSource != null ? hwndSource.Handle : IntPtr.Zero;
return new {window, handle};
}).Where(x => x.handle != IntPtr.Zero)
.ToDictionary(x => x.handle, x => x.window);
for (var hWnd = GetTopWindow(IntPtr.Zero); hWnd != IntPtr.Zero; hWnd = GetWindow(hWnd, GW_HWNDNEXT))
if (windowsByHandle.ContainsKey((hWnd)))
yield return windowsByHandle[hWnd];
}
public const int SW_SHOWNORMAL = 1;
[DllImport("user32.dll")]
public static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
private const uint GW_HWNDNEXT = 2;
[DllImport("User32")]
public static extern IntPtr GetTopWindow(IntPtr hWnd);
[DllImport("User32")]
public static extern IntPtr GetWindow(IntPtr hWnd, uint wCmd);
[Serializable]
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT
{
public int length;
public int flags;
public int showCmd;
public POINT minPosition;
public POINT maxPosition;
public RECT normalPosition;
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(IntPtr hWnd, WindowMessage msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr PostMessage(IntPtr hWnd, WindowMessage msg, IntPtr wParam, IntPtr lParam);
[DllImport("dwmapi.dll", EntryPoint = "#127")]
internal static extern void DwmGetColorizationParameters(ref DWMCOLORIZATIONPARAMS dp);
[StructLayout(LayoutKind.Sequential)]
internal struct DWMCOLORIZATIONPARAMS
{
public UInt32 ColorizationColor;
public UInt32 ColorizationAfterglow;
public UInt32 ColorizationColorBalance;
public UInt32 ColorizationAfterglowBalance;
public UInt32 ColorizationBlurBalance;
public UInt32 ColorizationGlassReflectionIntensity;
public UInt32 ColorizationOpaqueBlend;
}
}
}

View File

@@ -0,0 +1,26 @@
namespace Dragablz.Core
{
internal enum SystemCommand
{
SC_CLOSE = 0xF060,
SC_CONTEXTHELP = 0xF180,
SC_DEFAULT = 0xF160,
SC_HOTKEY = 0xF150,
SC_HSCROLL = 0xF080,
SCF_ISSECURE = 0x00000001,
SC_KEYMENU = 0xF100,
SC_MAXIMIZE = 0xF030,
SC_MINIMIZE = 0xF020,
SC_MONITORPOWER = 0xF170,
SC_MOUSEMENU = 0xF090,
SC_MOVE = 0xF010,
SC_MOUSEMOVE = 0xF012,
SC_NEXTWINDOW = 0xF040,
SC_PREVWINDOW = 0xF050,
SC_RESTORE = 0xF120,
SC_SCREENSAVE = 0xF140,
SC_SIZE = 0xF000,
SC_TASKLIST = 0xF130,
SC_VSCROLL = 0xF070
}
}

View File

@@ -0,0 +1,51 @@
using System;
namespace Dragablz.Core
{
internal class TabHeaderDragStartInformation
{
private readonly DragablzItem _dragItem;
private readonly double _dragablzItemsControlHorizontalOffset;
private readonly double _dragablzItemControlVerticalOffset;
private readonly double _dragablzItemHorizontalOffset;
private readonly double _dragablzItemVerticalOffset;
public TabHeaderDragStartInformation(
DragablzItem dragItem,
double dragablzItemsControlHorizontalOffset, double dragablzItemControlVerticalOffset, double dragablzItemHorizontalOffset, double dragablzItemVerticalOffset)
{
if (dragItem == null) throw new ArgumentNullException("dragItem");
_dragItem = dragItem;
_dragablzItemsControlHorizontalOffset = dragablzItemsControlHorizontalOffset;
_dragablzItemControlVerticalOffset = dragablzItemControlVerticalOffset;
_dragablzItemHorizontalOffset = dragablzItemHorizontalOffset;
_dragablzItemVerticalOffset = dragablzItemVerticalOffset;
}
public double DragablzItemsControlHorizontalOffset
{
get { return _dragablzItemsControlHorizontalOffset; }
}
public double DragablzItemControlVerticalOffset
{
get { return _dragablzItemControlVerticalOffset; }
}
public double DragablzItemHorizontalOffset
{
get { return _dragablzItemHorizontalOffset; }
}
public double DragablzItemVerticalOffset
{
get { return _dragablzItemVerticalOffset; }
}
public DragablzItem DragItem
{
get { return _dragItem; }
}
}
}

View File

@@ -0,0 +1,245 @@
namespace Dragablz.Core
{
internal enum WindowMessage
{
WM_NULL = 0,
WM_CREATE = 1,
WM_DESTROY = 2,
WM_MOVE = 3,
WM_SIZE = 5,
WM_ACTIVATE = 6,
WM_SETFOCUS = 7,
WM_KILLFOCUS = 8,
WM_ENABLE = 10,
WM_SETREDRAW = 11,
WM_SETTEXT = 12,
WM_GETTEXT = 13,
WM_GETTEXTLENGTH = 14,
WM_PAINT = 15,
WM_CLOSE = 16,
WM_QUERYENDSESSION = 17,
WM_QUIT = 18,
WM_QUERYOPEN = 19,
WM_ERASEBKGND = 20,
WM_SYSCOLORCHANGE = 21,
WM_ENDSESSION = 22,
WM_SHOWWINDOW = 24,
WM_CTLCOLOR = 25,
WM_SETTINGCHANGE = 26,
WM_WININICHANGE = 26,
WM_DEVMODECHANGE = 27,
WM_ACTIVATEAPP = 28,
WM_FONTCHANGE = 29,
WM_TIMECHANGE = 30,
WM_CANCELMODE = 31,
WM_SETCURSOR = 32,
WM_TABLET_MAXOFFSET = 32,
WM_MOUSEACTIVATE = 33,
WM_CHILDACTIVATE = 34,
WM_QUEUESYNC = 35,
WM_GETMINMAXINFO = 36,
WM_PAINTICON = 38,
WM_ICONERASEBKGND = 39,
WM_NEXTDLGCTL = 40,
WM_SPOOLERSTATUS = 42,
WM_DRAWITEM = 43,
WM_MEASUREITEM = 44,
WM_DELETEITEM = 45,
WM_VKEYTOITEM = 46,
WM_CHARTOITEM = 47,
WM_SETFONT = 48,
WM_GETFONT = 49,
WM_SETHOTKEY = 50,
WM_GETHOTKEY = 51,
WM_QUERYDRAGICON = 55,
WM_COMPAREITEM = 57,
WM_GETOBJECT = 61,
WM_COMPACTING = 65,
WM_COMMNOTIFY = 68,
WM_WINDOWPOSCHANGING = 70,
WM_WINDOWPOSCHANGED = 71,
WM_POWER = 72,
WM_COPYDATA = 74,
WM_CANCELJOURNAL = 75,
WM_NOTIFY = 78,
WM_INPUTLANGCHANGEREQUEST = 80,
WM_INPUTLANGCHANGE = 81,
WM_TCARD = 82,
WM_HELP = 83,
WM_USERCHANGED = 84,
WM_NOTIFYFORMAT = 85,
WM_CONTEXTMENU = 123,
WM_STYLECHANGING = 124,
WM_STYLECHANGED = 125,
WM_DISPLAYCHANGE = 126,
WM_GETICON = 127,
WM_SETICON = 128,
WM_NCCREATE = 129,
WM_NCDESTROY = 130,
WM_NCCALCSIZE = 131,
WM_NCHITTEST = 132,
WM_NCPAINT = 133,
WM_NCACTIVATE = 134,
WM_GETDLGCODE = 135,
WM_SYNCPAINT = 136,
WM_MOUSEQUERY = 155,
WM_NCMOUSEMOVE = 160,
WM_NCLBUTTONDOWN = 161,
WM_NCLBUTTONUP = 162,
WM_NCLBUTTONDBLCLK = 163,
WM_NCRBUTTONDOWN = 164,
WM_NCRBUTTONUP = 165,
WM_NCRBUTTONDBLCLK = 166,
WM_NCMBUTTONDOWN = 167,
WM_NCMBUTTONUP = 168,
WM_NCMBUTTONDBLCLK = 169,
WM_NCXBUTTONDOWN = 171,
WM_NCXBUTTONUP = 172,
WM_NCXBUTTONDBLCLK = 173,
WM_INPUT = 255,
WM_KEYDOWN = 256,
WM_KEYFIRST = 256,
WM_KEYUP = 257,
WM_CHAR = 258,
WM_DEADCHAR = 259,
WM_SYSKEYDOWN = 260,
WM_SYSKEYUP = 261,
WM_SYSCHAR = 262,
WM_SYSDEADCHAR = 263,
WM_KEYLAST = 264,
WM_IME_STARTCOMPOSITION = 269,
WM_IME_ENDCOMPOSITION = 270,
WM_IME_COMPOSITION = 271,
WM_IME_KEYLAST = 271,
WM_INITDIALOG = 272,
WM_COMMAND = 273,
WM_SYSCOMMAND = 274,
WM_TIMER = 275,
WM_HSCROLL = 276,
WM_VSCROLL = 277,
WM_INITMENU = 278,
WM_INITMENUPOPUP = 279,
WM_MENUSELECT = 287,
WM_MENUCHAR = 288,
WM_ENTERIDLE = 289,
WM_UNINITMENUPOPUP = 293,
WM_CHANGEUISTATE = 295,
WM_UPDATEUISTATE = 296,
WM_QUERYUISTATE = 297,
WM_CTLCOLORMSGBOX = 306,
WM_CTLCOLOREDIT = 307,
WM_CTLCOLORLISTBOX = 308,
WM_CTLCOLORBTN = 309,
WM_CTLCOLORDLG = 310,
WM_CTLCOLORSCROLLBAR = 311,
WM_CTLCOLORSTATIC = 312,
WM_MOUSEFIRST = 512,
WM_MOUSEMOVE = 512,
WM_LBUTTONDOWN = 513,
WM_LBUTTONUP = 514,
WM_LBUTTONDBLCLK = 515,
WM_RBUTTONDOWN = 516,
WM_RBUTTONUP = 517,
WM_RBUTTONDBLCLK = 518,
WM_MBUTTONDOWN = 519,
WM_MBUTTONUP = 520,
WM_MBUTTONDBLCLK = 521,
WM_MOUSEWHEEL = 522,
WM_XBUTTONDOWN = 523,
WM_XBUTTONUP = 524,
WM_XBUTTONDBLCLK = 525,
WM_MOUSEHWHEEL = 526,
WM_MOUSELAST = 526,
WM_PARENTNOTIFY = 528,
WM_ENTERMENULOOP = 529,
WM_EXITMENULOOP = 530,
WM_NEXTMENU = 531,
WM_SIZING = 532,
WM_CAPTURECHANGED = 533,
WM_MOVING = 534,
WM_POWERBROADCAST = 536,
WM_DEVICECHANGE = 537,
WM_MDICREATE = 544,
WM_MDIDESTROY = 545,
WM_MDIACTIVATE = 546,
WM_MDIRESTORE = 547,
WM_MDINEXT = 548,
WM_MDIMAXIMIZE = 549,
WM_MDITILE = 550,
WM_MDICASCADE = 551,
WM_MDIICONARRANGE = 552,
WM_MDIGETACTIVE = 553,
WM_MDISETMENU = 560,
WM_ENTERSIZEMOVE = 561,
WM_EXITSIZEMOVE = 562,
WM_DROPFILES = 563,
WM_MDIREFRESHMENU = 564,
WM_IME_SETCONTEXT = 641,
WM_IME_NOTIFY = 642,
WM_IME_CONTROL = 643,
WM_IME_COMPOSITIONFULL = 644,
WM_IME_SELECT = 645,
WM_IME_CHAR = 646,
WM_IME_REQUEST = 648,
WM_IME_KEYDOWN = 656,
WM_IME_KEYUP = 657,
WM_MOUSEHOVER = 673,
WM_NCMOUSELEAVE = 674,
WM_MOUSELEAVE = 675,
WM_WTSSESSION_CHANGE = 689,
WM_TABLET_DEFBASE = 704,
WM_TABLET_ADDED = 712,
WM_TABLET_DELETED = 713,
WM_TABLET_FLICK = 715,
WM_TABLET_QUERYSYSTEMGESTURESTATUS = 716,
WM_CUT = 768,
WM_COPY = 769,
WM_PASTE = 770,
WM_CLEAR = 771,
WM_UNDO = 772,
WM_RENDERFORMAT = 773,
WM_RENDERALLFORMATS = 774,
WM_DESTROYCLIPBOARD = 775,
WM_DRAWCLIPBOARD = 776,
WM_PAINTCLIPBOARD = 777,
WM_VSCROLLCLIPBOARD = 778,
WM_SIZECLIPBOARD = 779,
WM_ASKCBFORMATNAME = 780,
WM_CHANGECBCHAIN = 781,
WM_HSCROLLCLIPBOARD = 782,
WM_QUERYNEWPALETTE = 783,
WM_PALETTEISCHANGING = 784,
WM_PALETTECHANGED = 785,
WM_HOTKEY = 786,
WM_PRINT = 791,
WM_PRINTCLIENT = 792,
WM_APPCOMMAND = 793,
WM_THEMECHANGED = 794,
WM_DWMCOMPOSITIONCHANGED = 798,
WM_DWMNCRENDERINGCHANGED = 799,
WM_DWMCOLORIZATIONCOLORCHANGED = 800,
WM_DWMWINDOWMAXIMIZEDCHANGE = 801,
WM_DWMSENDICONICTHUMBNAIL = 803,
WM_DWMSENDICONICLIVEPREVIEWBITMAP = 806,
WM_HANDHELDFIRST = 856,
WM_HANDHELDLAST = 863,
WM_AFXFIRST = 864,
WM_AFXLAST = 895,
WM_PENWINFIRST = 896,
WM_PENWINLAST = 911,
WM_USER = 1024,
WM_APP = 32768,
}
public enum WindowSizingMessage
{
WMSZ_BOTTOM = 6,
WMSZ_BOTTOMLEFT = 7,
WMSZ_BOTTOMRIGHT = 8,
WMSZ_LEFT = 1,
WMSZ_RIGHT = 2,
WMSZ_TOP = 3,
WMSZ_TOPLEFT = 4,
WMSZ_TOPRIGHT = 5,
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Runtime.ConstrainedExecution;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace Dragablz
{
/// <summary>
/// Provides a simple implementation of <see cref="IInterLayoutClient"/>, but only really useful if
/// <see cref="TabItem"/> instances are specified in XAML. If you are binding via ItemsSource then
/// you most likely want to create your own implementation of <see cref="IInterLayoutClient"/>.
/// </summary>
public class DefaultInterLayoutClient : IInterLayoutClient
{
public INewTabHost<UIElement> GetNewHost(object partition, TabablzControl source)
{
var tabablzControl = new TabablzControl {DataContext = source.DataContext};
Clone(source, tabablzControl);
if (source.InterTabController == null)
throw new InvalidOperationException("Source tab does not have an InterTabCOntroller set. Ensure this is set on initial, and subsequently generated tab controls.");
var newInterTabController = new InterTabController
{
Partition = source.InterTabController.Partition
};
Clone(source.InterTabController, newInterTabController);
tabablzControl.SetCurrentValue(TabablzControl.InterTabControllerProperty, newInterTabController);
return new NewTabHost<UIElement>(tabablzControl, tabablzControl);
}
private static void Clone(DependencyObject from, DependencyObject to)
{
var localValueEnumerator = from.GetLocalValueEnumerator();
while (localValueEnumerator.MoveNext())
{
if (localValueEnumerator.Current.Property.ReadOnly ||
localValueEnumerator.Current.Value is FrameworkElement) continue;
if (!(localValueEnumerator.Current.Value is BindingExpressionBase))
to.SetCurrentValue(localValueEnumerator.Current.Property, localValueEnumerator.Current.Value);
}
}
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Linq;
using System.Windows;
using System.Windows.Threading;
using Dragablz.Core;
namespace Dragablz
{
public class DefaultInterTabClient : IInterTabClient
{
public virtual INewTabHost<Window> GetNewHost(IInterTabClient interTabClient, object partition, TabablzControl source)
{
if (source == null) throw new ArgumentNullException("source");
var sourceWindow = Window.GetWindow(source);
if (sourceWindow == null) throw new ApplicationException("Unable to ascertain source window.");
var newWindow = (Window)Activator.CreateInstance(sourceWindow.GetType());
newWindow.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.DataBind);
var newTabablzControl = newWindow.LogicalTreeDepthFirstTraversal().OfType<TabablzControl>().FirstOrDefault();
if (newTabablzControl == null) throw new ApplicationException("Unable to ascertain tab control.");
if (newTabablzControl.ItemsSource == null)
newTabablzControl.Items.Clear();
return new NewTabHost<Window>(newWindow, newTabablzControl);
}
public virtual TabEmptiedResponse TabEmptiedHandler(TabablzControl tabControl, Window window)
{
return TabEmptiedResponse.CloseWindowOrLayoutBranch;
}
}
}

View File

@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Dragablz.Dockablz
{
[TemplatePart(Name = FirstContentPresenterPartName, Type=typeof(ContentPresenter))]
[TemplatePart(Name = SecondContentPresenterPartName, Type = typeof(ContentPresenter))]
public class Branch : Control
{
private const string FirstContentPresenterPartName = "PART_FirstContentPresenter";
private const string SecondContentPresenterPartName = "PART_SecondContentPresenter";
static Branch()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Branch), new FrameworkPropertyMetadata(typeof(Branch)));
}
public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register(
"Orientation", typeof (Orientation), typeof (Branch), new PropertyMetadata(default(Orientation)));
public Orientation Orientation
{
get { return (Orientation) GetValue(OrientationProperty); }
set { SetValue(OrientationProperty, value); }
}
public static readonly DependencyProperty FirstItemProperty = DependencyProperty.Register(
"FirstItem", typeof(object), typeof(Branch), new PropertyMetadata(default(object)));
public object FirstItem
{
get { return GetValue(FirstItemProperty); }
set { SetValue(FirstItemProperty, value); }
}
public static readonly DependencyProperty FirstItemLengthProperty = DependencyProperty.Register(
"FirstItemLength", typeof (GridLength), typeof (Branch), new FrameworkPropertyMetadata(new GridLength(0.49999, GridUnitType.Star), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public GridLength FirstItemLength
{
get { return (GridLength) GetValue(FirstItemLengthProperty); }
set { SetValue(FirstItemLengthProperty, value); }
}
public static readonly DependencyProperty SecondItemProperty = DependencyProperty.Register(
"SecondItem", typeof(object), typeof(Branch), new PropertyMetadata(default(object)));
public object SecondItem
{
get { return GetValue(SecondItemProperty); }
set { SetValue(SecondItemProperty, value); }
}
public static readonly DependencyProperty SecondItemLengthProperty = DependencyProperty.Register(
"SecondItemLength", typeof(GridLength), typeof(Branch), new FrameworkPropertyMetadata(new GridLength(0.50001, GridUnitType.Star), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public GridLength SecondItemLength
{
get { return (GridLength) GetValue(SecondItemLengthProperty); }
set { SetValue(SecondItemLengthProperty, value); }
}
/// <summary>
/// Gets the proportional size of the first item, between 0 and 1, where 1 would represent the entire size of the branch.
/// </summary>
/// <returns></returns>
public double GetFirstProportion()
{
return (1/(FirstItemLength.Value + SecondItemLength.Value))*FirstItemLength.Value;
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
FirstContentPresenter = GetTemplateChild(FirstContentPresenterPartName) as ContentPresenter;
SecondContentPresenter = GetTemplateChild(SecondContentPresenterPartName) as ContentPresenter;
}
internal ContentPresenter FirstContentPresenter { get; private set; }
internal ContentPresenter SecondContentPresenter { get; private set; }
}
}

View File

@@ -0,0 +1,129 @@
using System;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using Dragablz.Core;
namespace Dragablz.Dockablz
{
public class BranchAccessor
{
private readonly Branch _branch;
private readonly BranchAccessor _firstItemBranchAccessor;
private readonly BranchAccessor _secondItemBranchAccessor;
private readonly TabablzControl _firstItemTabablzControl;
private readonly TabablzControl _secondItemTabablzControl;
public BranchAccessor(Branch branch)
{
if (branch == null) throw new ArgumentNullException("branch");
_branch = branch;
var firstChildBranch = branch.FirstItem as Branch;
if (firstChildBranch != null)
_firstItemBranchAccessor = new BranchAccessor(firstChildBranch);
else
_firstItemTabablzControl = FindTabablzControl(branch.FirstItem, branch.FirstContentPresenter);
var secondChildBranch = branch.SecondItem as Branch;
if (secondChildBranch != null)
_secondItemBranchAccessor = new BranchAccessor(secondChildBranch);
else
_secondItemTabablzControl = FindTabablzControl(branch.SecondItem, branch.SecondContentPresenter);
}
private static TabablzControl FindTabablzControl(object item, DependencyObject contentPresenter)
{
var result = item as TabablzControl;
return result ?? contentPresenter.VisualTreeDepthFirstTraversal().OfType<TabablzControl>().FirstOrDefault();
}
public Branch Branch
{
get { return _branch; }
}
public BranchAccessor FirstItemBranchAccessor
{
get { return _firstItemBranchAccessor; }
}
public BranchAccessor SecondItemBranchAccessor
{
get { return _secondItemBranchAccessor; }
}
public TabablzControl FirstItemTabablzControl
{
get { return _firstItemTabablzControl; }
}
public TabablzControl SecondItemTabablzControl
{
get { return _secondItemTabablzControl; }
}
/// <summary>
/// Visits the content of the first or second side of a branch, according to its content type. No more than one of the provided <see cref="Action"/>
/// callbacks will be called.
/// </summary>
/// <param name="childItem"></param>
/// <param name="childBranchVisitor"></param>
/// <param name="childTabablzControlVisitor"></param>
/// <param name="childContentVisitor"></param>
/// <returns></returns>
public BranchAccessor Visit(BranchItem childItem,
Action<BranchAccessor> childBranchVisitor = null,
Action<TabablzControl> childTabablzControlVisitor = null,
Action<object> childContentVisitor = null)
{
Func<BranchAccessor> branchGetter;
Func<TabablzControl> tabGetter;
Func<object> contentGetter;
switch (childItem)
{
case BranchItem.First:
branchGetter = () => _firstItemBranchAccessor;
tabGetter = () => _firstItemTabablzControl;
contentGetter = () => _branch.FirstItem;
break;
case BranchItem.Second:
branchGetter = () => _secondItemBranchAccessor;
tabGetter = () => _secondItemTabablzControl;
contentGetter = () => _branch.SecondItem;
break;
default:
throw new ArgumentOutOfRangeException("childItem");
}
var branchDescription = branchGetter();
if (branchDescription != null)
{
if (childBranchVisitor != null)
childBranchVisitor(branchDescription);
return this;
}
var tabablzControl = tabGetter();
if (tabablzControl != null)
{
if (childTabablzControlVisitor != null)
childTabablzControlVisitor(tabablzControl);
return this;
}
if (childContentVisitor == null) return this;
var content = contentGetter();
if (content != null)
childContentVisitor(content);
return this;
}
}
}

View File

@@ -0,0 +1,8 @@
namespace Dragablz.Dockablz
{
public enum BranchItem
{
First,
Second
}
}

View File

@@ -0,0 +1,35 @@
using System;
namespace Dragablz.Dockablz
{
public class BranchResult
{
private readonly Branch _branch;
private readonly TabablzControl _tabablzControl;
public BranchResult(Branch branch, TabablzControl tabablzControl)
{
if (branch == null) throw new ArgumentNullException("branch");
if (tabablzControl == null) throw new ArgumentNullException("tabablzControl");
_branch = branch;
_tabablzControl = tabablzControl;
}
/// <summary>
/// The new branch.
/// </summary>
public Branch Branch
{
get { return _branch; }
}
/// <summary>
/// The new tab control.
/// </summary>
public TabablzControl TabablzControl
{
get { return _tabablzControl; }
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Windows;
using System.Windows.Controls;
namespace Dragablz.Dockablz
{
public class CouldBeHeaderedStyleSelector : StyleSelector
{
public Style NonHeaderedStyle { get; set; }
public Style HeaderedStyle { get; set; }
public override Style SelectStyle(object item, DependencyObject container)
{
return container is HeaderedDragablzItem || container is HeaderedContentControl
? HeaderedStyle
: NonHeaderedStyle;
}
}
}

View File

@@ -0,0 +1,37 @@
using System.Windows;
using System.Windows.Controls;
namespace Dragablz.Dockablz
{
public class DropZone : Control
{
static DropZone()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DropZone), new FrameworkPropertyMetadata(typeof(DropZone)));
}
public static readonly DependencyProperty LocationProperty = DependencyProperty.Register(
"Location", typeof (DropZoneLocation), typeof (DropZone), new PropertyMetadata(default(DropZoneLocation)));
public DropZoneLocation Location
{
get { return (DropZoneLocation) GetValue(LocationProperty); }
set { SetValue(LocationProperty, value); }
}
private static readonly DependencyPropertyKey IsOfferedPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsOffered", typeof (bool), typeof (DropZone),
new PropertyMetadata(default(bool)));
public static readonly DependencyProperty IsOfferedProperty =
IsOfferedPropertyKey.DependencyProperty;
public bool IsOffered
{
get { return (bool) GetValue(IsOfferedProperty); }
internal set { SetValue(IsOfferedPropertyKey, value); }
}
}
}

View File

@@ -0,0 +1,11 @@
namespace Dragablz.Dockablz
{
public enum DropZoneLocation
{
Top,
Right,
Bottom,
Left,
Floating
}
}

View File

@@ -0,0 +1,86 @@
using System;
using System.Runtime.Serialization;
namespace Dragablz.Dockablz
{
public static class Extensions
{
/// <summary>
/// Begin a query of layout content, returning an accessor for examining the children.
/// </summary>
/// <param name="layout"></param>
/// <returns></returns>
public static LayoutAccessor Query(this Layout layout)
{
if (layout == null) throw new ArgumentNullException("layout");
return new LayoutAccessor(layout);
}
/// <summary>
/// Helper method for <see cref="LayoutAccessor.Visit"/> which allows a context to be passed through.
/// </summary>
/// <typeparam name="TContext"></typeparam>
/// <param name="layoutAccessor"></param>
/// <param name="context"></param>
/// <param name="branchVisitor"></param>
/// <param name="tabablzControlVisitor"></param>
/// <param name="contentVisitor"></param>
/// <returns></returns>
public static LayoutAccessor Visit<TContext>(
this LayoutAccessor layoutAccessor,
TContext context,
Action<TContext, BranchAccessor> branchVisitor = null,
Action<TContext, TabablzControl> tabablzControlVisitor = null,
Action<TContext, object> contentVisitor = null)
{
if (layoutAccessor == null) throw new ArgumentNullException("layoutAccessor");
layoutAccessor.Visit(
WrapVisitor(context, branchVisitor),
WrapVisitor(context, tabablzControlVisitor),
WrapVisitor(context, contentVisitor)
);
return layoutAccessor;
}
/// <summary>
/// Helper method for <see cref="BranchAccessor.Visit"/> which allows a context to be passed through.
/// </summary>
/// <typeparam name="TContext"></typeparam>
/// <param name="branchAccessor"></param>
/// <param name="context"></param>
/// <param name="childItem"></param>
/// <param name="branchVisitor"></param>
/// <param name="tabablzControlVisitor"></param>
/// <param name="contentVisitor"></param>
/// <returns></returns>
public static BranchAccessor Visit<TContext>(
this BranchAccessor branchAccessor,
TContext context,
BranchItem childItem,
Action<TContext, BranchAccessor> branchVisitor = null,
Action<TContext, TabablzControl> tabablzControlVisitor = null,
Action<TContext, object> contentVisitor = null)
{
if (branchAccessor == null) throw new ArgumentNullException("branchAccessor");
branchAccessor.Visit(
childItem,
WrapVisitor(context, branchVisitor),
WrapVisitor(context, tabablzControlVisitor),
WrapVisitor(context, contentVisitor)
);
return branchAccessor;
}
private static Action<TVisitArg> WrapVisitor<TContext, TVisitArg>(TContext context, Action<TContext, TVisitArg> visitor)
{
if (visitor == null) return null;
return a => visitor(context, a);
}
}
}

View File

@@ -0,0 +1,53 @@
using System;
namespace Dragablz.Dockablz
{
internal static class Finder
{
internal static LocationReport Find(TabablzControl tabablzControl)
{
if (tabablzControl == null) throw new ArgumentNullException("tabablzControl");
var locationReportBuilder = new LocationReportBuilder(tabablzControl);
foreach (var loadedInstance in Layout.GetLoadedInstances())
{
locationReportBuilder.CurrentLayout = loadedInstance;
loadedInstance.Query().Visit(
locationReportBuilder,
BranchVisitor,
TabablzControlVisitor
);
if (locationReportBuilder.IsFound)
break;
}
if (!locationReportBuilder.IsFound)
throw new LocationReportException("Instance not within any layout.");
return locationReportBuilder.ToLocationReport();
}
private static void BranchVisitor(LocationReportBuilder locationReportBuilder, BranchAccessor branchAccessor)
{
if (Equals(branchAccessor.FirstItemTabablzControl, locationReportBuilder.TargetTabablzControl))
locationReportBuilder.MarkFound(branchAccessor.Branch, false);
else if (Equals(branchAccessor.SecondItemTabablzControl, locationReportBuilder.TargetTabablzControl))
locationReportBuilder.MarkFound(branchAccessor.Branch, true);
else
{
branchAccessor.Visit(BranchItem.First, ba => BranchVisitor(locationReportBuilder, ba));
if (locationReportBuilder.IsFound) return;
branchAccessor.Visit(BranchItem.Second, ba => BranchVisitor(locationReportBuilder, ba));
}
}
private static void TabablzControlVisitor(LocationReportBuilder locationReportBuilder, TabablzControl tabablzControl)
{
if (Equals(tabablzControl, locationReportBuilder.TargetTabablzControl))
locationReportBuilder.MarkFound();
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace Dragablz.Dockablz
{
public delegate void FloatRequestedEventHandler(object sender, FloatRequestedEventArgs e);
public class FloatRequestedEventArgs : DragablzItemEventArgs
{
public FloatRequestedEventArgs(RoutedEvent routedEvent, object source, DragablzItem dragablzItem)
: base(routedEvent, source, dragablzItem)
{ }
public FloatRequestedEventArgs(RoutedEvent routedEvent, DragablzItem dragablzItem)
: base(routedEvent, dragablzItem)
{ }
}
}

View File

@@ -0,0 +1,46 @@
using System;
namespace Dragablz.Dockablz
{
internal class FloatTransfer
{
private readonly double _width;
private readonly double _height;
private readonly object _content;
public FloatTransfer(double width, double height, object content)
{
if (content == null) throw new ArgumentNullException("content");
_width = width;
_height = height;
_content = content;
}
public static FloatTransfer TakeSnapshot(DragablzItem dragablzItem, TabablzControl sourceTabControl)
{
if (dragablzItem == null) throw new ArgumentNullException("dragablzItem");
return new FloatTransfer(sourceTabControl.ActualWidth, sourceTabControl.ActualHeight, dragablzItem.UnderlyingContent ?? dragablzItem.Content ?? dragablzItem);
}
[Obsolete]
//TODO width and height transfer obsolete
public double Width
{
get { return _width; }
}
[Obsolete]
//TODO width and height transfer obsolete
public double Height
{
get { return _height; }
}
public object Content
{
get { return _content; }
}
}
}

View File

@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace Dragablz.Dockablz
{
/// <summary>
/// experimentational. might have to puish this back to mvvm only
/// </summary>
internal class FloatingItemSnapShot
{
private readonly object _content;
private readonly Rect _location;
private readonly int _zIndex;
private readonly WindowState _state;
public FloatingItemSnapShot(object content, Rect location, int zIndex, WindowState state)
{
if (content == null) throw new ArgumentNullException("content");
_content = content;
_location = location;
_zIndex = zIndex;
_state = state;
}
public static FloatingItemSnapShot Take(DragablzItem dragablzItem)
{
if (dragablzItem == null) throw new ArgumentNullException("dragablzItem");
return new FloatingItemSnapShot(
dragablzItem.Content,
new Rect(dragablzItem.X, dragablzItem.Y, dragablzItem.ActualWidth, dragablzItem.ActualHeight),
Panel.GetZIndex(dragablzItem),
Layout.GetFloatingItemState(dragablzItem));
}
public void Apply(DragablzItem dragablzItem)
{
if (dragablzItem == null) throw new ArgumentNullException("dragablzItem");
dragablzItem.SetCurrentValue(DragablzItem.XProperty, Location.Left);
dragablzItem.SetCurrentValue(DragablzItem.YProperty, Location.Top);
dragablzItem.SetCurrentValue(FrameworkElement.WidthProperty, Location.Width);
dragablzItem.SetCurrentValue(FrameworkElement.HeightProperty, Location.Height);
Layout.SetFloatingItemState(dragablzItem, State);
Panel.SetZIndex(dragablzItem, ZIndex);
}
public object Content
{
get { return _content; }
}
public Rect Location
{
get { return _location; }
}
public int ZIndex
{
get { return _zIndex; }
}
public WindowState State
{
get { return _state; }
}
}
}

View File

@@ -0,0 +1,986 @@
using System;
using System.CodeDom;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using Dragablz.Core;
namespace Dragablz.Dockablz
{
public delegate void ClosingFloatingItemCallback(ItemActionCallbackArgs<Layout> args);
[TemplatePart(Name = TopDropZonePartName, Type = typeof(DropZone))]
[TemplatePart(Name = RightDropZonePartName, Type = typeof(DropZone))]
[TemplatePart(Name = BottomDropZonePartName, Type = typeof(DropZone))]
[TemplatePart(Name = LeftDropZonePartName, Type = typeof(DropZone))]
[TemplatePart(Name = FloatingDropZonePartName, Type = typeof(DropZone))]
[TemplatePart(Name = FloatingContentPresenterPartName, Type = typeof(ContentPresenter))]
public class Layout : ContentControl
{
private static readonly HashSet<Layout> LoadedLayouts = new HashSet<Layout>();
private const string TopDropZonePartName = "PART_TopDropZone";
private const string RightDropZonePartName = "PART_RightDropZone";
private const string BottomDropZonePartName = "PART_BottomDropZone";
private const string LeftDropZonePartName = "PART_LeftDropZone";
private const string FloatingDropZonePartName = "PART_FloatDropZone";
private const string FloatingContentPresenterPartName = "PART_FloatContentPresenter";
private readonly IDictionary<DropZoneLocation, DropZone> _dropZones = new Dictionary<DropZoneLocation, DropZone>();
private static Tuple<Layout, DropZone> _currentlyOfferedDropZone;
public static RoutedCommand UnfloatItemCommand = new RoutedCommand();
public static RoutedCommand MaximiseFloatingItem = new RoutedCommand();
public static RoutedCommand RestoreFloatingItem = new RoutedCommand();
public static RoutedCommand CloseFloatingItem = new RoutedCommand();
public static RoutedCommand TileFloatingItemsCommand = new RoutedCommand();
public static RoutedCommand TileFloatingItemsVerticallyCommand = new RoutedCommand();
public static RoutedCommand TileFloatingItemsHorizontallyCommand = new RoutedCommand();
private readonly DragablzItemsControl _floatingItems;
private static bool _isDragOpWireUpPending;
private FloatTransfer _floatTransfer;
static Layout()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Layout), new FrameworkPropertyMetadata(typeof(Layout)));
EventManager.RegisterClassHandler(typeof(DragablzItem), DragablzItem.DragStarted, new DragablzDragStartedEventHandler(ItemDragStarted));
EventManager.RegisterClassHandler(typeof(DragablzItem), DragablzItem.PreviewDragDelta, new DragablzDragDeltaEventHandler(PreviewItemDragDelta), true);
EventManager.RegisterClassHandler(typeof(DragablzItem), DragablzItem.DragCompleted, new DragablzDragCompletedEventHandler(ItemDragCompleted));
}
public Layout()
{
Loaded += (sender, args) =>
{
LoadedLayouts.Add(this);
MarkTopLeftItem(this);
};
Unloaded += (sender, args) => LoadedLayouts.Remove(this);
CommandBindings.Add(new CommandBinding(UnfloatItemCommand, UnfloatExecuted, CanExecuteUnfloat));
CommandBindings.Add(new CommandBinding(MaximiseFloatingItem, MaximiseFloatingItemExecuted, CanExecuteMaximiseFloatingItem));
CommandBindings.Add(new CommandBinding(CloseFloatingItem, CloseFloatingItemExecuted, CanExecuteCloseFloatingItem));
CommandBindings.Add(new CommandBinding(RestoreFloatingItem, RestoreFloatingItemExecuted, CanExecuteRestoreFloatingItem));
CommandBindings.Add(new CommandBinding(TileFloatingItemsCommand, TileFloatingItemsExecuted));
CommandBindings.Add(new CommandBinding(TileFloatingItemsCommand, TileFloatingItemsExecuted));
CommandBindings.Add(new CommandBinding(TileFloatingItemsVerticallyCommand, TileFloatingItemsVerticallyExecuted));
CommandBindings.Add(new CommandBinding(TileFloatingItemsHorizontallyCommand, TileFloatingItemsHorizontallyExecuted));
//TODO bad bad behaviour. Pick up this from the template.
_floatingItems = new DragablzItemsControl
{
ContainerCustomisations = new ContainerCustomisations(
GetFloatingContainerForItemOverride,
PrepareFloatingContainerForItemOverride,
ClearingFloatingContainerForItemOverride)
};
var floatingItemsSourceBinding = new Binding("FloatingItemsSource") { Source = this };
_floatingItems.SetBinding(ItemsControl.ItemsSourceProperty, floatingItemsSourceBinding);
var floatingItemsControlStyleBinding = new Binding("FloatingItemsControlStyle") { Source = this };
_floatingItems.SetBinding(StyleProperty, floatingItemsControlStyleBinding);
var floatingItemTemplateBinding = new Binding("FloatingItemTemplate") { Source = this };
_floatingItems.SetBinding(ItemsControl.ItemTemplateProperty, floatingItemTemplateBinding);
var floatingItemTemplateSelectorBinding = new Binding("FloatingItemTemplateSelector") { Source = this };
_floatingItems.SetBinding(ItemsControl.ItemTemplateSelectorProperty, floatingItemTemplateSelectorBinding);
var floatingItemContainerStyeBinding = new Binding("FloatingItemContainerStyle") { Source = this };
_floatingItems.SetBinding(ItemsControl.ItemContainerStyleProperty, floatingItemContainerStyeBinding);
var floatingItemContainerStyleSelectorBinding = new Binding("FloatingItemContainerStyleSelector") { Source = this };
_floatingItems.SetBinding(ItemsControl.ItemContainerStyleSelectorProperty, floatingItemContainerStyleSelectorBinding);
}
/// <summary>
/// Helper method to get all the currently loaded layouts.
/// </summary>
/// <returns></returns>
public static IEnumerable<Layout> GetLoadedInstances()
{
return LoadedLayouts.ToList();
}
/// <summary>
/// Finds the location of a tab control withing a layout.
/// </summary>
/// <param name="tabablzControl"></param>
/// <returns></returns>
public static LocationReport Find(TabablzControl tabablzControl)
{
if (tabablzControl == null) throw new ArgumentNullException("tabablzControl");
return Finder.Find(tabablzControl);
}
/// <summary>
/// Creates a split in a layout, at the location of a specified <see cref="TabablzControl"/>.
/// </summary>
/// <para></para>
/// <param name="tabablzControl">Tab control to be split.</param>
/// <param name="orientation">Direction of split.</param>
/// <param name="makeSecond">Set to <c>true</c> to make the current tab control push into the right hand or bottom of the split.</param>
/// <remarks>The tab control to be split must be hosted in a layout control.</remarks>
public static BranchResult Branch(TabablzControl tabablzControl, Orientation orientation, bool makeSecond)
{
return Branch(tabablzControl, orientation, makeSecond, .5);
}
/// <summary>
/// Creates a split in a layout, at the location of a specified <see cref="TabablzControl"/>.
/// </summary>
/// <para></para>
/// <param name="tabablzControl">Tab control to be split.</param>
/// <param name="orientation">Direction of split.</param>
/// <param name="makeSecond">Set to <c>true</c> to make the current tab control push into the right hand or bottom of the split.</param>
/// <param name="firstItemProportion">Sets the proportion of the first tab control, with 0.5 being 50% of available space.</param>
/// <remarks>The tab control to be split must be hosted in a layout control. <see cref="Layout.BranchTemplate" /> should be set (typically via XAML).</remarks>
public static BranchResult Branch(TabablzControl tabablzControl, Orientation orientation, bool makeSecond, double firstItemProportion)
{
return Branch(tabablzControl, null, orientation, makeSecond, firstItemProportion);
}
/// <summary>
/// Creates a split in a layout, at the location of a specified <see cref="TabablzControl"/>.
/// </summary>
/// <para></para>
/// <param name="tabablzControl">Tab control to be split.</param>
/// <param name="newSiblingTabablzControl">New sibling tab control (otherwise <see cref="Layout.BranchTemplate"/> will be used).</param>
/// <param name="orientation">Direction of split.</param>
/// <param name="makeCurrentSecond">Set to <c>true</c> to make the current tab control push into the right hand or bottom of the split.</param>
/// <param name="firstItemProportion">Sets the proportion of the first tab control, with 0.5 being 50% of available space.</param>
/// <remarks>The tab control to be split must be hosted in a layout control. </remarks>
public static BranchResult Branch(TabablzControl tabablzControl, TabablzControl newSiblingTabablzControl, Orientation orientation, bool makeCurrentSecond,
double firstItemProportion)
{
if (firstItemProportion < 0.0 || firstItemProportion > 1.0) throw new ArgumentOutOfRangeException("firstItemProportion", "Must be >= 0.0 and <= 1.0");
var locationReport = Find(tabablzControl);
Action<Branch> applier;
object existingContent;
if (!locationReport.IsLeaf)
{
existingContent = locationReport.RootLayout.Content;
applier = branch => locationReport.RootLayout.Content = branch;
}
else if (!locationReport.IsSecondLeaf)
{
existingContent = locationReport.ParentBranch.FirstItem;
applier = branch => locationReport.ParentBranch.FirstItem = branch;
}
else
{
existingContent = locationReport.ParentBranch.SecondItem;
applier = branch => locationReport.ParentBranch.SecondItem = branch;
}
var selectedItem = tabablzControl.SelectedItem;
var branchResult = Branch(orientation, firstItemProportion, makeCurrentSecond, locationReport.RootLayout.BranchTemplate, newSiblingTabablzControl, existingContent, applier);
tabablzControl.SelectedItem = selectedItem;
tabablzControl.Dispatcher.BeginInvoke(new Action(() =>
{
tabablzControl.SetCurrentValue(Selector.SelectedItemProperty, selectedItem);
MarkTopLeftItem(locationReport.RootLayout);
}),
DispatcherPriority.Loaded);
return branchResult;
}
/// <summary>
/// Use in conjuction with the <see cref="InterTabController.Partition"/> on a <see cref="TabablzControl"/>
/// to isolate drag and drop spaces/control instances.
/// </summary>
public string Partition { get; set; }
public static readonly DependencyProperty InterLayoutClientProperty = DependencyProperty.Register(
"InterLayoutClient", typeof (IInterLayoutClient), typeof (Layout), new PropertyMetadata(new DefaultInterLayoutClient()));
public IInterLayoutClient InterLayoutClient
{
get { return (IInterLayoutClient) GetValue(InterLayoutClientProperty); }
set { SetValue(InterLayoutClientProperty, value); }
}
internal static bool IsContainedWithinBranch(DependencyObject dependencyObject)
{
do
{
dependencyObject = VisualTreeHelper.GetParent(dependencyObject);
if (dependencyObject is Branch)
return true;
} while (dependencyObject != null);
return false;
}
private static readonly DependencyPropertyKey IsParticipatingInDragPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsParticipatingInDrag", typeof (bool), typeof (Layout),
new PropertyMetadata(default(bool)));
public static readonly DependencyProperty IsParticipatingInDragProperty =
IsParticipatingInDragPropertyKey.DependencyProperty;
public bool IsParticipatingInDrag
{
get { return (bool) GetValue(IsParticipatingInDragProperty); }
private set { SetValue(IsParticipatingInDragPropertyKey, value); }
}
public static readonly DependencyProperty BranchTemplateProperty = DependencyProperty.Register(
"BranchTemplate", typeof (DataTemplate), typeof (Layout), new PropertyMetadata(default(DataTemplate)));
public DataTemplate BranchTemplate
{
get { return (DataTemplate) GetValue(BranchTemplateProperty); }
set { SetValue(BranchTemplateProperty, value); }
}
public static readonly DependencyProperty IsFloatDropZoneEnabledProperty = DependencyProperty.Register(
"IsFloatDropZoneEnabled", typeof (bool), typeof (Layout), new PropertyMetadata(default(bool)));
public bool IsFloatDropZoneEnabled
{
get { return (bool) GetValue(IsFloatDropZoneEnabledProperty); }
set { SetValue(IsFloatDropZoneEnabledProperty, value); }
}
/// <summary>
/// Defines a margin for the container which hosts all floating items.
/// </summary>
public static readonly DependencyProperty FloatingItemsContainerMarginProperty = DependencyProperty.Register(
"FloatingItemsContainerMargin", typeof (Thickness), typeof (Layout), new PropertyMetadata(default(Thickness)));
/// <summary>
/// Defines a margin for the container which hosts all floating items.
/// </summary>
public Thickness FloatingItemsContainerMargin
{
get { return (Thickness) GetValue(FloatingItemsContainerMarginProperty); }
set { SetValue(FloatingItemsContainerMarginProperty, value); }
}
/// <summary>
/// Floating items, such as tool/MDI windows, which will sit above the <see cref="Content"/>.
/// </summary>
public ItemCollection FloatingItems
{
get { return _floatingItems.Items; }
}
public static readonly DependencyProperty FloatingItemsSourceProperty = DependencyProperty.Register(
"FloatingItemsSource", typeof (IEnumerable), typeof (Layout), new PropertyMetadata(default(IEnumerable)));
/// <summary>
/// Floating items, such as tool/MDI windows, which will sit above the <see cref="Content"/>.
/// </summary>
public IEnumerable FloatingItemsSource
{
get { return (IEnumerable) GetValue(FloatingItemsSourceProperty); }
set { SetValue(FloatingItemsSourceProperty, value); }
}
public static readonly DependencyProperty FloatingItemsControlStyleProperty = DependencyProperty.Register(
"FloatingItemsControlStyle", typeof (Style), typeof (Layout), new PropertyMetadata((Style)null));
/// <summary>
/// The style to be applied to the <see cref="DragablzItemsControl"/> which is used to display floating items.
/// In most scenarios it should be OK to leave this to that applied by the default style.
/// </summary>
public Style FloatingItemsControlStyle
{
get { return (Style) GetValue(FloatingItemsControlStyleProperty); }
set { SetValue(FloatingItemsControlStyleProperty, value); }
}
public static readonly DependencyProperty FloatingItemContainerStyleProperty = DependencyProperty.Register(
"FloatingItemContainerStyle", typeof (Style), typeof (Layout), new PropertyMetadata(default(Style)));
public Style FloatingItemContainerStyle
{
get { return (Style) GetValue(FloatingItemContainerStyleProperty); }
set { SetValue(FloatingItemContainerStyleProperty, value); }
}
public static readonly DependencyProperty FloatingItemContainerStyleSelectorProperty = DependencyProperty.Register(
"FloatingItemContainerStyleSelector", typeof (StyleSelector), typeof (Layout), new PropertyMetadata(new CouldBeHeaderedStyleSelector()));
public StyleSelector FloatingItemContainerStyleSelector
{
get { return (StyleSelector) GetValue(FloatingItemContainerStyleSelectorProperty); }
set { SetValue(FloatingItemContainerStyleSelectorProperty, value); }
}
public static readonly DependencyProperty FloatingItemTemplateProperty = DependencyProperty.Register(
"FloatingItemTemplate", typeof (DataTemplate), typeof (Layout), new PropertyMetadata(default(DataTemplate)));
public DataTemplate FloatingItemTemplate
{
get { return (DataTemplate) GetValue(FloatingItemTemplateProperty); }
set { SetValue(FloatingItemTemplateProperty, value); }
}
public static readonly DependencyProperty FloatingItemTemplateSelectorProperty = DependencyProperty.Register(
"FloatingItemTemplateSelector", typeof (DataTemplateSelector), typeof (Layout), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector FloatingItemTemplateSelector
{
get { return (DataTemplateSelector) GetValue(FloatingItemTemplateSelectorProperty); }
set { SetValue(FloatingItemTemplateSelectorProperty, value); }
}
public static readonly DependencyProperty FloatingItemHeaderMemberPathProperty = DependencyProperty.Register(
"FloatingItemHeaderMemberPath", typeof (string), typeof (Layout), new PropertyMetadata(default(string)));
public string FloatingItemHeaderMemberPath
{
get { return (string) GetValue(FloatingItemHeaderMemberPathProperty); }
set { SetValue(FloatingItemHeaderMemberPathProperty, value); }
}
public static readonly DependencyProperty FloatingItemDisplayMemberPathProperty = DependencyProperty.Register(
"FloatingItemDisplayMemberPath", typeof (string), typeof (Layout), new PropertyMetadata(default(string)));
public string FloatingItemDisplayMemberPath
{
get { return (string) GetValue(FloatingItemDisplayMemberPathProperty); }
set { SetValue(FloatingItemDisplayMemberPathProperty, value); }
}
public static readonly DependencyProperty ClosingFloatingItemCallbackProperty = DependencyProperty.Register(
"ClosingFloatingItemCallback", typeof (ClosingFloatingItemCallback), typeof (Layout), new PropertyMetadata(default(ClosingFloatingItemCallback)));
public ClosingFloatingItemCallback ClosingFloatingItemCallback
{
get { return (ClosingFloatingItemCallback) GetValue(ClosingFloatingItemCallbackProperty); }
set { SetValue(ClosingFloatingItemCallbackProperty, value); }
}
public static readonly DependencyPropertyKey KeyIsFloatingInLayoutPropertyKey = DependencyProperty.RegisterAttachedReadOnly(
"IsFloatingInLayout", typeof (bool), typeof (Layout), new PropertyMetadata(default(bool)));
private static void SetIsFloatingInLayout(DependencyObject element, bool value)
{
element.SetValue(KeyIsFloatingInLayoutPropertyKey, value);
}
public static bool GetIsFloatingInLayout(DependencyObject element)
{
return (bool)element.GetValue(KeyIsFloatingInLayoutPropertyKey.DependencyProperty);
}
private static readonly DependencyPropertyKey IsTopLeftItemPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsTopLeftItem", typeof(bool), typeof(Layout),
new PropertyMetadata(default(bool)));
/// <summary>
/// Indicates if an item/tab control within a layout is contained at the top most and left most branch item.
/// </summary>
public static readonly DependencyProperty IsTopLeftItemProperty = IsTopLeftItemPropertyKey.DependencyProperty;
/// <summary>
/// Indicates if an item/tab control within a layout is contained at the top most and left most branch item.
/// </summary>
private static void SetIsTopLeftItem(DependencyObject element, bool value)
{
element.SetValue(IsTopLeftItemPropertyKey, value);
}
/// <summary>
/// Indicates if an item/tab control within a layout is contained at the top most and left most branch item.
/// </summary>
public static bool GetIsTopLeftItem(DependencyObject element)
{
return (bool)element.GetValue(IsTopLeftItemProperty);
}
/// <summary>When overridden in a derived class, is invoked whenever application code or internal processes call <see cref="M:System.Windows.FrameworkElement.ApplyTemplate" />.</summary>
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var floatingItemsContentPresenter = GetTemplateChild(FloatingContentPresenterPartName) as ContentPresenter;
if (floatingItemsContentPresenter != null)
floatingItemsContentPresenter.Content = _floatingItems;
_dropZones[DropZoneLocation.Top] = GetTemplateChild(TopDropZonePartName) as DropZone;
_dropZones[DropZoneLocation.Right] = GetTemplateChild(RightDropZonePartName) as DropZone;
_dropZones[DropZoneLocation.Bottom] = GetTemplateChild(BottomDropZonePartName) as DropZone;
_dropZones[DropZoneLocation.Left] = GetTemplateChild(LeftDropZonePartName) as DropZone;
_dropZones[DropZoneLocation.Floating] = GetTemplateChild(FloatingDropZonePartName) as DropZone;
}
internal IEnumerable<DragablzItem> FloatingDragablzItems()
{
return _floatingItems.DragablzItems();
}
internal static void RestoreFloatingItemSnapShots(DependencyObject ancestor, IEnumerable<FloatingItemSnapShot> floatingItemSnapShots)
{
var layouts = ancestor.VisualTreeDepthFirstTraversal().OfType<Layout>().ToList();
foreach (var floatingDragablzItem in layouts.SelectMany(l => l.FloatingDragablzItems()))
{
var itemSnapShots = floatingItemSnapShots as FloatingItemSnapShot[] ?? floatingItemSnapShots.ToArray();
var floatingItemSnapShot = itemSnapShots.FirstOrDefault(
ss => ss.Content == floatingDragablzItem.Content);
if (floatingItemSnapShot != null)
floatingItemSnapShot.Apply(floatingDragablzItem);
}
}
private static void ItemDragStarted(object sender, DragablzDragStartedEventArgs e)
{
//we wait until drag is in full flow so we know the partition has been setup by the owning tab control
_isDragOpWireUpPending = true;
}
private static void SetupParticipatingLayouts(DragablzItem dragablzItem)
{
var sourceOfDragItemsControl = ItemsControl.ItemsControlFromItemContainer(dragablzItem) as DragablzItemsControl;
if (sourceOfDragItemsControl == null || sourceOfDragItemsControl.Items.Count != 1) return;
var draggingWindow = Window.GetWindow(dragablzItem);
if (draggingWindow == null) return;
foreach (var loadedLayout in LoadedLayouts.Where(l =>
l.Partition == dragablzItem.PartitionAtDragStart &&
!Equals(Window.GetWindow(l), draggingWindow)))
{
loadedLayout.IsParticipatingInDrag = true;
}
}
private void MonitorDropZones(Point cursorPos)
{
var myWindow = Window.GetWindow(this);
if (myWindow == null) return;
foreach (var dropZone in _dropZones.Values.Where(dz => dz != null))
{
var pointFromScreen = myWindow.PointFromScreen(cursorPos);
var pointRelativeToDropZone = myWindow.TranslatePoint(pointFromScreen, dropZone);
var inputHitTest = dropZone.InputHitTest(pointRelativeToDropZone);
//TODO better halding when windows are layered over each other
if (inputHitTest != null)
{
if (_currentlyOfferedDropZone != null)
_currentlyOfferedDropZone.Item2.IsOffered = false;
dropZone.IsOffered = true;
_currentlyOfferedDropZone = new Tuple<Layout, DropZone>(this, dropZone);
}
else
{
dropZone.IsOffered = false;
if (_currentlyOfferedDropZone != null && _currentlyOfferedDropZone.Item2 == dropZone)
_currentlyOfferedDropZone = null;
}
}
}
private static bool TryGetSourceTabControl(DragablzItem dragablzItem, out TabablzControl tabablzControl)
{
var sourceOfDragItemsControl = ItemsControl.ItemsControlFromItemContainer(dragablzItem) as DragablzItemsControl;
if (sourceOfDragItemsControl == null) throw new ApplicationException("Unable to determine source items control.");
tabablzControl = TabablzControl.GetOwnerOfHeaderItems(sourceOfDragItemsControl);
return tabablzControl != null;
}
private void Branch(DropZoneLocation location, DragablzItem sourceDragablzItem)
{
if (InterLayoutClient == null)
throw new InvalidOperationException("InterLayoutClient is not set.");
var sourceOfDragItemsControl = ItemsControl.ItemsControlFromItemContainer(sourceDragablzItem) as DragablzItemsControl;
if (sourceOfDragItemsControl == null) throw new ApplicationException("Unable to determin source items control.");
var sourceTabControl = TabablzControl.GetOwnerOfHeaderItems(sourceOfDragItemsControl);
if (sourceTabControl == null) throw new ApplicationException("Unable to determin source tab control.");
var floatingItemSnapShots = sourceTabControl.VisualTreeDepthFirstTraversal()
.OfType<Layout>()
.SelectMany(l => l.FloatingDragablzItems().Select(FloatingItemSnapShot.Take))
.ToList();
var sourceItem = sourceOfDragItemsControl.ItemContainerGenerator.ItemFromContainer(sourceDragablzItem);
sourceTabControl.RemoveItem(sourceDragablzItem);
var branchItem = new Branch
{
Orientation = (location == DropZoneLocation.Right || location == DropZoneLocation.Left) ? Orientation.Horizontal : Orientation.Vertical
};
object newContent;
if (BranchTemplate == null)
{
var newTabHost = InterLayoutClient.GetNewHost(Partition, sourceTabControl);
if (newTabHost == null)
throw new ApplicationException("InterLayoutClient did not provide a new tab host.");
newTabHost.TabablzControl.AddToSource(sourceItem);
newTabHost.TabablzControl.SelectedItem = sourceItem;
newContent = newTabHost.Container;
Dispatcher.BeginInvoke(new Action(() => RestoreFloatingItemSnapShots(newTabHost.TabablzControl, floatingItemSnapShots)), DispatcherPriority.Loaded);
}
else
{
newContent = new ContentControl
{
Content = new object(),
ContentTemplate = BranchTemplate,
};
((ContentControl) newContent).Dispatcher.BeginInvoke(new Action(() =>
{
//TODO might need to improve this a bit, make it a bit more declarative for complex trees
var newTabControl = ((ContentControl)newContent).VisualTreeDepthFirstTraversal().OfType<TabablzControl>().FirstOrDefault();
if (newTabControl == null) return;
newTabControl.DataContext = sourceTabControl.DataContext;
newTabControl.AddToSource(sourceItem);
newTabControl.SelectedItem = sourceItem;
Dispatcher.BeginInvoke(new Action(() => RestoreFloatingItemSnapShots(newTabControl, floatingItemSnapShots)), DispatcherPriority.Loaded);
}), DispatcherPriority.Loaded);
}
if (location == DropZoneLocation.Right || location == DropZoneLocation.Bottom)
{
branchItem.FirstItem = Content;
branchItem.SecondItem = newContent;
}
else
{
branchItem.FirstItem = newContent;
branchItem.SecondItem = Content;
}
SetCurrentValue(ContentProperty, branchItem);
Dispatcher.BeginInvoke(new Action(() => MarkTopLeftItem(this)), DispatcherPriority.Loaded);
}
internal static bool ConsolidateBranch(DependencyObject redundantNode)
{
bool isSecondLineageWhenOwnerIsBranch;
var ownerBranch = FindLayoutOrBranchOwner(redundantNode, out isSecondLineageWhenOwnerIsBranch) as Branch;
if (ownerBranch == null) return false;
var survivingItem = isSecondLineageWhenOwnerIsBranch ? ownerBranch.FirstItem : ownerBranch.SecondItem;
var grandParent = FindLayoutOrBranchOwner(ownerBranch, out isSecondLineageWhenOwnerIsBranch);
if (grandParent == null) throw new ApplicationException("Unexpected structure, grandparent Layout or Branch not found");
var layout = grandParent as Layout;
if (layout != null)
{
layout.Content = survivingItem;
MarkTopLeftItem(layout);
return true;
}
var branch = (Branch) grandParent;
if (isSecondLineageWhenOwnerIsBranch)
branch.SecondItem = survivingItem;
else
branch.FirstItem = survivingItem;
var rootLayout = branch.VisualTreeAncestory().OfType<Layout>().FirstOrDefault();
if (rootLayout != null)
MarkTopLeftItem(rootLayout);
return true;
}
private static object FindLayoutOrBranchOwner(DependencyObject node, out bool isSecondLineageWhenOwnerIsBranch)
{
isSecondLineageWhenOwnerIsBranch = false;
var ancestoryStack = new Stack<DependencyObject>();
do
{
ancestoryStack.Push(node);
node = VisualTreeHelper.GetParent(node);
if (node is Layout)
return node;
var branch = node as Branch;
if (branch == null) continue;
isSecondLineageWhenOwnerIsBranch = ancestoryStack.Contains(branch.SecondContentPresenter);
return branch;
} while (node != null);
return null;
}
private static BranchResult Branch(Orientation orientation, double proportion, bool makeSecond, DataTemplate branchTemplate, TabablzControl newSibling, object existingContent, Action<Branch> applier)
{
var branchItem = new Branch
{
Orientation = orientation
};
var newContent = new ContentControl
{
Content = newSibling ?? new object(),
ContentTemplate = branchTemplate,
};
if (!makeSecond)
{
branchItem.FirstItem = existingContent;
branchItem.SecondItem = newContent;
}
else
{
branchItem.FirstItem = newContent;
branchItem.SecondItem = existingContent;
}
branchItem.SetCurrentValue(Dockablz.Branch.FirstItemLengthProperty, new GridLength(proportion, GridUnitType.Star));
branchItem.SetCurrentValue(Dockablz.Branch.SecondItemLengthProperty, new GridLength(1-proportion, GridUnitType.Star));
applier(branchItem);
newContent.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.Loaded);
var newTabablzControl = newContent.VisualTreeDepthFirstTraversal().OfType<TabablzControl>().FirstOrDefault();
if (newTabablzControl != null) return new BranchResult(branchItem, newTabablzControl);
//let#s be kinf and give WPF an extra change to gen the controls
newContent.Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.Background);
newTabablzControl = newContent.VisualTreeDepthFirstTraversal().OfType<TabablzControl>().FirstOrDefault();
if (newTabablzControl == null)
throw new ApplicationException("New TabablzControl was not generated inside branch.");
return new BranchResult(branchItem, newTabablzControl);
}
private static void ItemDragCompleted(object sender, DragablzDragCompletedEventArgs e)
{
_isDragOpWireUpPending = false;
foreach (var loadedLayout in LoadedLayouts)
loadedLayout.IsParticipatingInDrag = false;
if (_currentlyOfferedDropZone == null || e.DragablzItem.IsDropTargetFound) return;
TabablzControl tabablzControl;
if (TryGetSourceTabControl(e.DragablzItem, out tabablzControl))
{
if (tabablzControl.Items.Count > 1) return;
if (_currentlyOfferedDropZone.Item2.Location == DropZoneLocation.Floating)
Float(_currentlyOfferedDropZone.Item1, e.DragablzItem);
else
_currentlyOfferedDropZone.Item1.Branch(_currentlyOfferedDropZone.Item2.Location, e.DragablzItem);
}
_currentlyOfferedDropZone = null;
}
private static void Float(Layout layout, DragablzItem dragablzItem)
{
//TODO we need eq of IManualInterTabClient here, so consumer can control this op'.
//remove from source
var sourceOfDragItemsControl = ItemsControl.ItemsControlFromItemContainer(dragablzItem) as DragablzItemsControl;
if (sourceOfDragItemsControl == null) throw new ApplicationException("Unable to determin source items control.");
var sourceTabControl = TabablzControl.GetOwnerOfHeaderItems(sourceOfDragItemsControl);
layout._floatTransfer = FloatTransfer.TakeSnapshot(dragablzItem, sourceTabControl);
var floatingItemSnapShots = sourceTabControl.VisualTreeDepthFirstTraversal()
.OfType<Layout>()
.SelectMany(l => l.FloatingDragablzItems().Select(FloatingItemSnapShot.Take))
.ToList();
if (sourceTabControl == null) throw new ApplicationException("Unable to determin source tab control.");
sourceTabControl.RemoveItem(dragablzItem);
//add to float layer
CollectionTeaser collectionTeaser;
if (CollectionTeaser.TryCreate(layout.FloatingItemsSource, out collectionTeaser))
collectionTeaser.Add(layout._floatTransfer.Content);
else
layout.FloatingItems.Add(layout._floatTransfer.Content);
layout.Dispatcher.BeginInvoke(new Action(() => RestoreFloatingItemSnapShots(layout, floatingItemSnapShots)), DispatcherPriority.Loaded);
}
private static void PreviewItemDragDelta(object sender, DragablzDragDeltaEventArgs e)
{
if (e.Cancel) return;
if (_isDragOpWireUpPending)
{
SetupParticipatingLayouts(e.DragablzItem);
_isDragOpWireUpPending = false;
}
foreach (var layout in LoadedLayouts.Where(l => l.IsParticipatingInDrag))
{
var cursorPos = Native.GetCursorPos();
layout.MonitorDropZones(cursorPos);
}
}
private void PrepareFloatingContainerForItemOverride(DependencyObject dependencyObject, object o)
{
var headeredDragablzItem = dependencyObject as HeaderedDragablzItem;
if (headeredDragablzItem == null) return;
SetIsFloatingInLayout(dependencyObject, true);
var headerBinding = new Binding(FloatingItemHeaderMemberPath) {Source = o};
headeredDragablzItem.SetBinding(HeaderedDragablzItem.HeaderContentProperty, headerBinding);
if (!string.IsNullOrWhiteSpace(FloatingItemDisplayMemberPath))
{
var contentBinding = new Binding(FloatingItemDisplayMemberPath) {Source = o};
headeredDragablzItem.SetBinding(ContentProperty, contentBinding);
}
if (_floatTransfer == null || (o != _floatTransfer.Content && dependencyObject != _floatTransfer.Content))
return;
var dragablzItem = (DragablzItem) dependencyObject;
Dispatcher.BeginInvoke(new Action(() =>
{
//TODO might be nice to allow user a bit of control over sizing...especially the .75 thing i have handily hard coded. shoot me.
dragablzItem.Measure(new Size(_floatingItems.ActualWidth, _floatingItems.ActualHeight));
var newWidth = Math.Min(_floatingItems.ActualWidth*.75, dragablzItem.DesiredSize.Width);
var newHeight = Math.Min(_floatingItems.ActualHeight * .75, dragablzItem.DesiredSize.Height);
dragablzItem.SetCurrentValue(DragablzItem.XProperty, _floatingItems.ActualWidth/2 - newWidth/2);
dragablzItem.SetCurrentValue(DragablzItem.YProperty, _floatingItems.ActualHeight/2 - newHeight/2);
dragablzItem.SetCurrentValue(WidthProperty, newWidth);
dragablzItem.SetCurrentValue(HeightProperty, newHeight);
}), DispatcherPriority.Loaded);
_floatTransfer = null;
}
private DragablzItem GetFloatingContainerForItemOverride()
{
if (string.IsNullOrWhiteSpace(FloatingItemHeaderMemberPath))
return new DragablzItem();
return new HeaderedDragablzItem();
}
private static void ClearingFloatingContainerForItemOverride(DependencyObject dependencyObject, object o)
{
SetIsFloatingInLayout(dependencyObject, false);
}
private void TileFloatingItemsExecuted(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
{
var dragablzItems = _floatingItems.DragablzItems();
Tiler.Tile(dragablzItems, new Size(_floatingItems.ActualWidth, _floatingItems.ActualHeight));
}
private void TileFloatingItemsHorizontallyExecuted(object sender, ExecutedRoutedEventArgs e)
{
var dragablzItems = _floatingItems.DragablzItems();
Tiler.TileHorizontally(dragablzItems, new Size(_floatingItems.ActualWidth, _floatingItems.ActualHeight));
}
private void TileFloatingItemsVerticallyExecuted(object sender, ExecutedRoutedEventArgs e)
{
var dragablzItems = _floatingItems.DragablzItems();
Tiler.TileVertically(dragablzItems, new Size(_floatingItems.ActualWidth, _floatingItems.ActualHeight));
}
public static readonly DependencyProperty FloatingItemStateProperty = DependencyProperty.RegisterAttached(
"FloatingItemState", typeof (WindowState), typeof (Layout), new PropertyMetadata(default(WindowState)));
public static void SetFloatingItemState(DependencyObject element, WindowState value)
{
element.SetValue(FloatingItemStateProperty, value);
}
public static WindowState GetFloatingItemState(DependencyObject element)
{
return (WindowState) element.GetValue(FloatingItemStateProperty);
}
internal static readonly DependencyProperty LocationSnapShotProperty = DependencyProperty.RegisterAttached(
"LocationSnapShot", typeof (LocationSnapShot), typeof (Layout), new PropertyMetadata(default(LocationSnapShot)));
internal static void SetLocationSnapShot(FrameworkElement element, LocationSnapShot value)
{
element.SetValue(LocationSnapShotProperty, value);
}
internal static LocationSnapShot GetLocationSnapShot(FrameworkElement element)
{
return (LocationSnapShot) element.GetValue(LocationSnapShotProperty);
}
private static void CanExecuteMaximiseFloatingItem(object sender, CanExecuteRoutedEventArgs canExecuteRoutedEventArgs)
{
canExecuteRoutedEventArgs.CanExecute = false;
canExecuteRoutedEventArgs.Handled = true;
var dragablzItem = canExecuteRoutedEventArgs.Parameter as DragablzItem;
if (dragablzItem != null)
{
canExecuteRoutedEventArgs.CanExecute = new[] {WindowState.Normal, WindowState.Minimized}.Contains(GetFloatingItemState(dragablzItem));
}
}
private static void CanExecuteRestoreFloatingItem(object sender, CanExecuteRoutedEventArgs canExecuteRoutedEventArgs)
{
canExecuteRoutedEventArgs.CanExecute = false;
canExecuteRoutedEventArgs.Handled = true;
var dragablzItem = canExecuteRoutedEventArgs.Parameter as DragablzItem;
if (dragablzItem != null)
{
canExecuteRoutedEventArgs.CanExecute = new[] { WindowState.Maximized, WindowState.Minimized }.Contains(GetFloatingItemState(dragablzItem));
}
}
private static void CanExecuteCloseFloatingItem(object sender, CanExecuteRoutedEventArgs canExecuteRoutedEventArgs)
{
canExecuteRoutedEventArgs.CanExecute = true;
canExecuteRoutedEventArgs.Handled = true;
}
private void CloseFloatingItemExecuted(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
{
var dragablzItem = executedRoutedEventArgs.Parameter as DragablzItem;
if (dragablzItem == null) throw new ApplicationException("Parameter must be a DragablzItem");
var cancel = false;
if (ClosingFloatingItemCallback != null)
{
var callbackArgs = new ItemActionCallbackArgs<Layout>(Window.GetWindow(this), this, dragablzItem);
ClosingFloatingItemCallback(callbackArgs);
cancel = callbackArgs.IsCancelled;
}
if (cancel) return;
//TODO ...need a similar tp manual inter tab controlller here for the extra hook
var item = _floatingItems.ItemContainerGenerator.ItemFromContainer(dragablzItem);
CollectionTeaser collectionTeaser;
if (CollectionTeaser.TryCreate(_floatingItems.ItemsSource, out collectionTeaser))
collectionTeaser.Remove(item);
else
_floatingItems.Items.Remove(item);
}
private static void MaximiseFloatingItemExecuted(object sender, ExecutedRoutedEventArgs e)
{
var dragablzItem = e.Parameter as DragablzItem;
if (dragablzItem == null) return;
SetLocationSnapShot(dragablzItem, LocationSnapShot.Take(dragablzItem));
SetFloatingItemState(dragablzItem, WindowState.Maximized);
}
private static void RestoreFloatingItemExecuted(object sender, ExecutedRoutedEventArgs e)
{
var dragablzItem = e.Parameter as DragablzItem;
if (dragablzItem == null) return;
SetFloatingItemState(dragablzItem, WindowState.Normal);
var locationSnapShot = GetLocationSnapShot(dragablzItem);
if (locationSnapShot != null)
locationSnapShot.Apply(dragablzItem);
}
private bool IsHostingTab()
{
return this.VisualTreeDepthFirstTraversal().OfType<TabablzControl>()
.FirstOrDefault(t => t.InterTabController != null && t.InterTabController.Partition == Partition)
!= null;
}
private static void MarkTopLeftItem(Layout layout)
{
var layoutAccessor = layout.Query();
if (layoutAccessor.TabablzControl != null)
{
SetIsTopLeftItem(layoutAccessor.TabablzControl, true);
return;
}
var branchAccessor = layoutAccessor.BranchAccessor;
while (branchAccessor != null && branchAccessor.FirstItemTabablzControl == null)
{
branchAccessor = branchAccessor.FirstItemBranchAccessor;
}
foreach (var tabablzControl in layoutAccessor.TabablzControls())
{
SetIsTopLeftItem(tabablzControl, branchAccessor != null && Equals(tabablzControl, branchAccessor.FirstItemTabablzControl));
}
}
private void CanExecuteUnfloat(object sender, CanExecuteRoutedEventArgs canExecuteRoutedEventArgs)
{
canExecuteRoutedEventArgs.CanExecute = IsHostingTab();
canExecuteRoutedEventArgs.ContinueRouting = false;
canExecuteRoutedEventArgs.Handled = true;
}
private void UnfloatExecuted(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
{
var dragablzItem = executedRoutedEventArgs.Parameter as DragablzItem;
if (dragablzItem == null) return;
var exemplarTabControl = this.VisualTreeDepthFirstTraversal().OfType<TabablzControl>()
.FirstOrDefault(t => t.InterTabController != null && t.InterTabController.Partition == Partition);
if (exemplarTabControl == null) return;
//TODO passing the exemplar tab in here isnt ideal, as strictly speaking there isnt one.
var newTabHost = exemplarTabControl.InterTabController.InterTabClient.GetNewHost(exemplarTabControl.InterTabController.InterTabClient,
exemplarTabControl.InterTabController.Partition, exemplarTabControl);
if (newTabHost == null || newTabHost.TabablzControl == null || newTabHost.Container == null)
throw new ApplicationException("New tab host was not correctly provided");
var floatingItemSnapShots = dragablzItem.VisualTreeDepthFirstTraversal()
.OfType<Layout>()
.SelectMany(l => l.FloatingDragablzItems().Select(FloatingItemSnapShot.Take))
.ToList();
var content = dragablzItem.Content ?? dragablzItem;
//remove from source
CollectionTeaser collectionTeaser;
if (CollectionTeaser.TryCreate(FloatingItemsSource, out collectionTeaser))
collectionTeaser.Remove(content);
else
FloatingItems.Remove(content);
var myWindow = Window.GetWindow(this);
if (myWindow == null) throw new ApplicationException("Unable to find owning window.");
newTabHost.Container.Width = myWindow.RestoreBounds.Width;
newTabHost.Container.Height = myWindow.RestoreBounds.Height;
newTabHost.Container.Left = myWindow.Left + 20;
newTabHost.Container.Top = myWindow.Top + 20;
Dispatcher.BeginInvoke(new Action(() =>
{
newTabHost.TabablzControl.AddToSource(content);
newTabHost.TabablzControl.SelectedItem = content;
newTabHost.Container.Show();
newTabHost.Container.Activate();
Dispatcher.BeginInvoke(
new Action(() => RestoreFloatingItemSnapShots(newTabHost.TabablzControl, floatingItemSnapShots)));
}), DispatcherPriority.DataBind);
}
}
}

View File

@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
namespace Dragablz.Dockablz
{
/// <summary>
/// Provides information about the <see cref="Layout"/> instance.
/// </summary>
public class LayoutAccessor
{
private readonly Layout _layout;
private readonly BranchAccessor _branchAccessor;
private readonly TabablzControl _tabablzControl;
public LayoutAccessor(Layout layout)
{
if (layout == null) throw new ArgumentNullException("layout");
_layout = layout;
var branch = Layout.Content as Branch;
if (branch != null)
_branchAccessor = new BranchAccessor(branch);
else
_tabablzControl = Layout.Content as TabablzControl;
}
public Layout Layout
{
get { return _layout; }
}
public IEnumerable<DragablzItem> FloatingItems
{
get { return _layout.FloatingDragablzItems(); }
}
/// <summary>
/// <see cref="BranchAccessor"/> and <see cref="TabablzControl"/> are mutually exclusive, according to whether the layout has been split, or just contains a tab control.
/// </summary>
public BranchAccessor BranchAccessor
{
get { return _branchAccessor; }
}
/// <summary>
/// <see cref="BranchAccessor"/> and <see cref="TabablzControl"/> are mutually exclusive, according to whether the layout has been split, or just contains a tab control.
/// </summary>
public TabablzControl TabablzControl
{
get { return _tabablzControl; }
}
/// <summary>
/// Visits the content of the layout, according to its content type. No more than one of the provided <see cref="Action"/>
/// callbacks will be called.
/// </summary>
public LayoutAccessor Visit(
Action<BranchAccessor> branchVisitor = null,
Action<TabablzControl> tabablzControlVisitor = null,
Action<object> contentVisitor = null)
{
if (_branchAccessor != null)
{
if (branchVisitor != null)
{
branchVisitor(_branchAccessor);
}
return this;
}
if (_tabablzControl != null)
{
if (tabablzControlVisitor != null)
tabablzControlVisitor(_tabablzControl);
return this;
}
if (_layout.Content != null && contentVisitor != null)
contentVisitor(_layout.Content);
return this;
}
/// <summary>
/// Gets all the Tabablz controls in a Layout, regardless of location.
/// </summary>
/// <returns></returns>
public IEnumerable<TabablzControl> TabablzControls()
{
var tabablzControls = new List<TabablzControl>();
this.Visit(tabablzControls, BranchAccessorVisitor, TabablzControlVisitor);
return tabablzControls;
}
private static void TabablzControlVisitor(IList<TabablzControl> resultSet, TabablzControl tabablzControl)
{
resultSet.Add(tabablzControl);
}
private static void BranchAccessorVisitor(IList<TabablzControl> resultSet, BranchAccessor branchAccessor)
{
branchAccessor
.Visit(resultSet, BranchItem.First, BranchAccessorVisitor, TabablzControlVisitor)
.Visit(resultSet, BranchItem.Second, BranchAccessorVisitor, TabablzControlVisitor);
}
}
}

View File

@@ -0,0 +1,65 @@
using System;
namespace Dragablz.Dockablz
{
/// <summary>
/// Provides information about where a tab control is withing a layout structure.
/// </summary>
public class LocationReport
{
private readonly TabablzControl _tabablzControl;
private readonly Layout _rootLayout;
private readonly Branch _parentBranch;
private readonly bool _isLeaf;
private readonly bool _isSecondLeaf;
//TODO I've internalised constructor for now, so I can come back and add Window without breaking.
internal LocationReport(TabablzControl tabablzControl, Layout rootLayout)
: this(tabablzControl, rootLayout, null, false)
{ }
internal LocationReport(TabablzControl tabablzControl, Layout rootLayout, Branch parentBranch, bool isSecondLeaf)
{
if (tabablzControl == null) throw new ArgumentNullException("tabablzControl");
if (rootLayout == null) throw new ArgumentNullException("rootLayout");
_tabablzControl = tabablzControl;
_rootLayout = rootLayout;
_parentBranch = parentBranch;
_isLeaf = _parentBranch != null;
_isSecondLeaf = isSecondLeaf;
}
public TabablzControl TabablzControl
{
get { return _tabablzControl; }
}
public Layout RootLayout
{
get { return _rootLayout; }
}
/// <summary>
/// Gets the parent branch if this is a leaf. If the <see cref="TabablzControl"/> is directly under the <see cref="RootLayout"/> will be <c>null</c>.
/// </summary>
public Branch ParentBranch
{
get { return _parentBranch; }
}
/// <summary>
/// Idicates if this is a leaf in a branch. <c>True</c> if <see cref="ParentBranch"/> is not null.
/// </summary>
public bool IsLeaf
{
get { return _isLeaf; }
}
public bool IsSecondLeaf
{
get { return _isSecondLeaf; }
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
namespace Dragablz.Dockablz
{
internal class LocationReportBuilder
{
private readonly TabablzControl _targetTabablzControl;
private Branch _branch;
private bool _isSecondLeaf;
private Layout _layout;
public LocationReportBuilder(TabablzControl targetTabablzControl)
{
_targetTabablzControl = targetTabablzControl;
}
public TabablzControl TargetTabablzControl
{
get { return _targetTabablzControl; }
}
public bool IsFound { get; private set; }
public void MarkFound()
{
if (IsFound)
throw new InvalidOperationException("Already found.");
IsFound = true;
_layout = CurrentLayout;
}
public void MarkFound(Branch branch, bool isSecondLeaf)
{
if (branch == null) throw new ArgumentNullException("branch");
if (IsFound)
throw new InvalidOperationException("Already found.");
IsFound = true;
_layout = CurrentLayout;
_branch = branch;
_isSecondLeaf = isSecondLeaf;
}
public Layout CurrentLayout { get; set; }
public LocationReport ToLocationReport()
{
return new LocationReport(_targetTabablzControl, _layout, _branch, _isSecondLeaf);
}
}
}

View File

@@ -0,0 +1,22 @@
using System;
namespace Dragablz.Dockablz
{
/// <summary>
///
/// </summary>
public class LocationReportException : Exception
{
public LocationReportException()
{
}
public LocationReportException(string message) : base(message)
{
}
public LocationReportException(string message, Exception innerException) : base(message, innerException)
{
}
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Windows;
using System.Windows.Navigation;
namespace Dragablz.Dockablz
{
/// <summary>
/// Initially needed to restore MDI dragablz items styles after a max then restore,
/// as the trigger which binds the item width to the canvas width sets the Width back to the default
/// (e.g double.NaN) when the trigger is unset. so we need to re-apply sizes manually
/// </summary>
internal class LocationSnapShot
{
private readonly double _width;
private readonly double _height;
public static LocationSnapShot Take(FrameworkElement frameworkElement)
{
if (frameworkElement == null) throw new ArgumentNullException("frameworkElement");
return new LocationSnapShot(frameworkElement.Width, frameworkElement.Height);
}
private LocationSnapShot(double width, double height)
{
_width = width;
_height = height;
}
public void Apply(FrameworkElement frameworkElement)
{
if (frameworkElement == null) throw new ArgumentNullException("frameworkElement");
frameworkElement.SetCurrentValue(FrameworkElement.WidthProperty, _width);
frameworkElement.SetCurrentValue(FrameworkElement.HeightProperty, _height);
}
}
}

View File

@@ -0,0 +1,83 @@
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
namespace Dragablz.Dockablz
{
internal class Tiler
{
public static void Tile(IEnumerable<DragablzItem> dragablzItems, Size bounds)
{
if (dragablzItems == null) throw new ArgumentNullException("dragablzItems");
var items = new Queue<DragablzItem>(dragablzItems.OrderBy(Panel.GetZIndex));
var cellCountPerColumn = TilerCalculator.GetCellCountPerColumn(items.Count());
var x = 0d;
var cellWidth = bounds.Width / cellCountPerColumn.Length;
foreach (var cellCount in cellCountPerColumn)
{
var y = 0d;
var cellHeight = bounds.Height / cellCount;
for (var cell = 0; cell < cellCount; cell++)
{
var item = items.Dequeue();
Layout.SetFloatingItemState(item, WindowState.Normal);
item.SetCurrentValue(DragablzItem.XProperty, x);
item.SetCurrentValue(DragablzItem.YProperty, y);
item.SetCurrentValue(FrameworkElement.WidthProperty, cellWidth);
item.SetCurrentValue(FrameworkElement.HeightProperty, cellHeight);
y += cellHeight;
}
x += cellWidth;
}
}
public static void TileHorizontally(IEnumerable<DragablzItem> dragablzItems, Size bounds)
{
if (dragablzItems == null) throw new ArgumentNullException("dragablzItems");
var items = dragablzItems.ToList();
var x = 0.0;
var width = bounds.Width/items.Count;
foreach (var dragablzItem in items)
{
Layout.SetFloatingItemState(dragablzItem, WindowState.Normal);
dragablzItem.SetCurrentValue(DragablzItem.XProperty, x);
dragablzItem.SetCurrentValue(DragablzItem.YProperty, 0d);
x += width;
dragablzItem.SetCurrentValue(FrameworkElement.WidthProperty, width);
dragablzItem.SetCurrentValue(FrameworkElement.HeightProperty, bounds.Height);
}
}
public static void TileVertically(IEnumerable<DragablzItem> dragablzItems, Size bounds)
{
if (dragablzItems == null) throw new ArgumentNullException("dragablzItems");
var items = dragablzItems.ToList();
var y = 0.0;
var height = bounds.Height / items.Count;
foreach (var dragablzItem in items)
{
Layout.SetFloatingItemState(dragablzItem, WindowState.Normal);
dragablzItem.SetCurrentValue(DragablzItem.YProperty, y);
dragablzItem.SetCurrentValue(DragablzItem.XProperty, 0d);
y += height;
dragablzItem.SetCurrentValue(FrameworkElement.HeightProperty, height);
dragablzItem.SetCurrentValue(FrameworkElement.WidthProperty, bounds.Width);
}
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Dragablz.Dockablz
{
internal static class TilerCalculator
{
public static int[] GetCellCountPerColumn(int totalCells)
{
if (totalCells == 2)
return new[] {1, 1};
var sqrt = Math.Sqrt(totalCells);
if (unchecked(sqrt == (int) sqrt))
return Enumerable.Repeat((int) sqrt, (int) sqrt).ToArray();
var columns = (int)Math.Round(sqrt, MidpointRounding.AwayFromZero);
var minimumCellsPerColumns = (int)Math.Floor(sqrt);
var result = Enumerable.Repeat(minimumCellsPerColumns, columns).ToArray();
for (var i = columns - 1; result.Aggregate((current, next) => current + next) < totalCells; i--)
result[i]+=1;
return result;
}
}
}

View File

@@ -0,0 +1,33 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<PropertyGroup>
<TargetFrameworks>netcoreapp3.1;net45;net40</TargetFrameworks>
<UseWPF>true</UseWPF>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<PackageIconUrl>https://raw.githubusercontent.com/ButchersBoy/Dragablz/master/Resources/D32.png</PackageIconUrl>
<PackageLicenseExpression></PackageLicenseExpression>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageProjectUrl>http://dragablz.net</PackageProjectUrl>
<RepositoryUrl>https://github.com/ButchersBoy/Dragablz</RepositoryUrl>
<PackageTags>WPF TabControl Tab Tearable</PackageTags>
<PackageReleaseNotes>Support for .Net Core 3.0</PackageReleaseNotes>
<RepositoryType>git</RepositoryType>
<DocumentationFile>..\bin\Release\Dragablz.xml</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath>..\bin\Debug</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<OutputPath>..\bin\Release</OutputPath>
</PropertyGroup>
<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>Dragablz</id>
<version>0.0.0.0</version>
<title>Dragablz - Dragable and tearable tab control for WPF</title>
<authors>James Willock</authors>
<owners>James Willock</owners>
<iconUrl>https://raw.githubusercontent.com/ButchersBoy/Dragablz/master/Resources/D32.png</iconUrl>
<licenseUrl>https://raw.githubusercontent.com/ButchersBoy/Dragablz/master/LICENSE</licenseUrl>
<projectUrl>http://dragablz.net</projectUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Dragable and tearable tab control for WPF</description>
<releaseNotes>Support for .NET Core 3.0</releaseNotes>
<copyright>Copyright 2014-20 James Willock/Mulholland Software Ltd</copyright>
<tags>WPF TabControl Tab Tearable</tags>
</metadata>
<files>
<file src="..\bin\Release\netcoreapp3.0\Dragablz.dll" target="lib\netcoreapp3.0" />
<file src="..\bin\Release\netcoreapp3.0\Dragablz.pdb" target="lib\netcoreapp3.0" />
<file src="..\bin\Release\netcoreapp3.0\Dragablz.xml" target="lib\netcoreapp3.0" />
<file src="..\bin\Release\net45\Dragablz.dll" target="lib\net45" />
<file src="..\bin\Release\net45\Dragablz.pdb" target="lib\net45" />
<file src="..\bin\Release\net45\Dragablz.xml" target="lib\net45" />
<file src="..\bin\Release\net40\Dragablz.dll" target="lib\net40" />
<file src="..\bin\Release\net40\Dragablz.pdb" target="lib\net40" />
<file src="..\bin\Release\net40\Dragablz.xml" target="lib\net40" />
<file src="..\Dragablz\**\*.cs" target="src\netcoreapp3.0" />
<file src="..\Dragablz\**\*.cs" target="src\net45" />
<file src="..\Dragablz\**\*.cs" target="src\net40" />
</files>
</package>

View File

@@ -0,0 +1,74 @@
using System;
using System.Windows;
using System.Windows.Media;
using Dragablz.Core;
namespace Dragablz
{
/// <summary>
///
/// </summary>
/// <remarks>
/// In supporting .Net 4.0 we don't have access to SystemParameters.WindowGlassBrush, and even then
/// the opacity is not provided, so this class wraps up a few issues around here.
/// </remarks>
public static class DragablzColors
{
//TODO listen to changes from the OS to provide updates
public static Color WindowBaseColor = Color.FromRgb(217, 217, 217);
public static Brush WindowGlassBrush = GetWindowGlassBrush();
public static Brush WindowGlassBalancedBrush = GetBalancedWindowGlassBrush();
public static Brush WindowInactiveBrush = GetWindowInactiveBrush();
private static Brush GetWindowGlassBrush()
{
var colorizationParams = new Native.DWMCOLORIZATIONPARAMS();
Native.DwmGetColorizationParameters(ref colorizationParams);
var frameColor = ToColor(colorizationParams.ColorizationColor);
return new SolidColorBrush(frameColor);
}
private static Brush GetBalancedWindowGlassBrush()
{
var colorizationParams = new Native.DWMCOLORIZATIONPARAMS();
Native.DwmGetColorizationParameters(ref colorizationParams);
var frameColor = ToColor(colorizationParams.ColorizationColor);
var blendedColor = BlendColor(frameColor, WindowBaseColor, 100f - colorizationParams.ColorizationColorBalance);
return new SolidColorBrush(blendedColor);
}
private static Brush GetWindowInactiveBrush()
{
return new SolidColorBrush(SystemColors.MenuBarColor);
}
private static Color ToColor(UInt32 value)
{
return Color.FromArgb(255,
(byte)(value >> 16),
(byte)(value >> 8),
(byte)value
);
}
private static Color BlendColor(Color color1, Color color2, double percentage)
{
percentage = Math.Min(100, Math.Max(0, percentage));
return Color.FromRgb(
BlendColorChannel(color1.R, color2.R, percentage),
BlendColorChannel(color1.G, color2.G, percentage),
BlendColorChannel(color1.B, color2.B, percentage));
}
private static byte BlendColorChannel(double channel1, double channel2, double channel2Percentage)
{
var buff = channel1 + (channel2 - channel1) * channel2Percentage / 100D;
return Math.Min((byte)Math.Round(buff), (byte)255);
}
}
}

View File

@@ -0,0 +1,54 @@
using System;
using System.Windows;
using System.Windows.Controls.Primitives;
namespace Dragablz
{
public delegate void DragablzDragCompletedEventHandler(object sender, DragablzDragCompletedEventArgs e);
public class DragablzDragCompletedEventArgs : RoutedEventArgs
{
private readonly DragablzItem _dragablzItem;
private readonly bool _isDropTargetFound;
private readonly DragCompletedEventArgs _dragCompletedEventArgs;
public DragablzDragCompletedEventArgs(DragablzItem dragablzItem, DragCompletedEventArgs dragCompletedEventArgs)
{
if (dragablzItem == null) throw new ArgumentNullException("dragablzItem");
if (dragCompletedEventArgs == null) throw new ArgumentNullException("dragCompletedEventArgs");
_dragablzItem = dragablzItem;
_dragCompletedEventArgs = dragCompletedEventArgs;
}
public DragablzDragCompletedEventArgs(RoutedEvent routedEvent, DragablzItem dragablzItem, DragCompletedEventArgs dragCompletedEventArgs)
: base(routedEvent)
{
if (dragablzItem == null) throw new ArgumentNullException("dragablzItem");
if (dragCompletedEventArgs == null) throw new ArgumentNullException("dragCompletedEventArgs");
_dragablzItem = dragablzItem;
_dragCompletedEventArgs = dragCompletedEventArgs;
}
public DragablzDragCompletedEventArgs(RoutedEvent routedEvent, object source, DragablzItem dragablzItem, DragCompletedEventArgs dragCompletedEventArgs)
: base(routedEvent, source)
{
if (dragablzItem == null) throw new ArgumentNullException("dragablzItem");
if (dragCompletedEventArgs == null) throw new ArgumentNullException("dragCompletedEventArgs");
_dragablzItem = dragablzItem;
_dragCompletedEventArgs = dragCompletedEventArgs;
}
public DragablzItem DragablzItem
{
get { return _dragablzItem; }
}
public DragCompletedEventArgs DragCompletedEventArgs
{
get { return _dragCompletedEventArgs; }
}
}
}

View File

@@ -0,0 +1,44 @@
using System;
using System.Windows;
using System.Windows.Controls.Primitives;
namespace Dragablz
{
public delegate void DragablzDragDeltaEventHandler(object sender, DragablzDragDeltaEventArgs e);
public class DragablzDragDeltaEventArgs : DragablzItemEventArgs
{
private readonly DragDeltaEventArgs _dragDeltaEventArgs;
public DragablzDragDeltaEventArgs(DragablzItem dragablzItem, DragDeltaEventArgs dragDeltaEventArgs)
: base(dragablzItem)
{
if (dragDeltaEventArgs == null) throw new ArgumentNullException("dragDeltaEventArgs");
_dragDeltaEventArgs = dragDeltaEventArgs;
}
public DragablzDragDeltaEventArgs(RoutedEvent routedEvent, DragablzItem dragablzItem, DragDeltaEventArgs dragDeltaEventArgs)
: base(routedEvent, dragablzItem)
{
if (dragDeltaEventArgs == null) throw new ArgumentNullException("dragDeltaEventArgs");
_dragDeltaEventArgs = dragDeltaEventArgs;
}
public DragablzDragDeltaEventArgs(RoutedEvent routedEvent, object source, DragablzItem dragablzItem, DragDeltaEventArgs dragDeltaEventArgs)
: base(routedEvent, source, dragablzItem)
{
if (dragDeltaEventArgs == null) throw new ArgumentNullException("dragDeltaEventArgs");
_dragDeltaEventArgs = dragDeltaEventArgs;
}
public DragDeltaEventArgs DragDeltaEventArgs
{
get { return _dragDeltaEventArgs; }
}
public bool Cancel { get; set; }
}
}

View File

@@ -0,0 +1,38 @@
using System;
using System.Windows;
using System.Windows.Controls.Primitives;
namespace Dragablz
{
public delegate void DragablzDragStartedEventHandler(object sender, DragablzDragStartedEventArgs e);
public class DragablzDragStartedEventArgs : DragablzItemEventArgs
{
private readonly DragStartedEventArgs _dragStartedEventArgs;
public DragablzDragStartedEventArgs(DragablzItem dragablzItem, DragStartedEventArgs dragStartedEventArgs)
: base(dragablzItem)
{
if (dragStartedEventArgs == null) throw new ArgumentNullException("dragStartedEventArgs");
_dragStartedEventArgs = dragStartedEventArgs;
}
public DragablzDragStartedEventArgs(RoutedEvent routedEvent, DragablzItem dragablzItem, DragStartedEventArgs dragStartedEventArgs)
: base(routedEvent, dragablzItem)
{
_dragStartedEventArgs = dragStartedEventArgs;
}
public DragablzDragStartedEventArgs(RoutedEvent routedEvent, object source, DragablzItem dragablzItem, DragStartedEventArgs dragStartedEventArgs)
: base(routedEvent, source, dragablzItem)
{
_dragStartedEventArgs = dragStartedEventArgs;
}
public DragStartedEventArgs DragStartedEventArgs
{
get { return _dragStartedEventArgs; }
}
}
}

View File

@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Dragablz
{
public class DragablzIcon : Control
{
static DragablzIcon()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DragablzIcon), new FrameworkPropertyMetadata(typeof(DragablzIcon)));
}
}
}

View File

@@ -0,0 +1,616 @@
using System;
using System.Dynamic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Threading;
using Dragablz.Core;
using Dragablz.Referenceless;
namespace Dragablz
{
public enum SizeGrip
{
NotApplicable,
Left,
TopLeft,
Top,
TopRight,
Right,
BottomRight,
Bottom,
BottomLeft
}
[TemplatePart(Name = ThumbPartName, Type = typeof(Thumb))]
public class DragablzItem : ContentControl
{
public const string ThumbPartName = "PART_Thumb";
private readonly SerialDisposable _templateSubscriptions = new SerialDisposable();
private readonly SerialDisposable _rightMouseUpCleanUpDisposable = new SerialDisposable();
private Thumb _customThumb;
private Thumb _thumb;
private bool _seizeDragWithTemplate;
private Action<DragablzItem> _dragSeizedContinuation;
static DragablzItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DragablzItem), new FrameworkPropertyMetadata(typeof(DragablzItem)));
}
public DragablzItem()
{
AddHandler(MouseDownEvent, new RoutedEventHandler(MouseDownHandler), true);
}
public static readonly DependencyProperty XProperty = DependencyProperty.Register(
"X", typeof (double), typeof (DragablzItem), new PropertyMetadata(default(double), OnXChanged));
public double X
{
get { return (double) GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
public static readonly RoutedEvent XChangedEvent =
EventManager.RegisterRoutedEvent(
"XChanged",
RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<double>),
typeof(DragablzItem));
public event RoutedPropertyChangedEventHandler<double> XChanged
{
add { AddHandler(XChangedEvent, value); }
remove { RemoveHandler(IsDraggingChangedEvent, value); }
}
private static void OnXChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var instance = (DragablzItem)d;
var args = new RoutedPropertyChangedEventArgs<double>(
(double)e.OldValue,
(double)e.NewValue)
{
RoutedEvent = XChangedEvent
};
instance.RaiseEvent(args);
}
public static readonly DependencyProperty YProperty = DependencyProperty.Register(
"Y", typeof (double), typeof (DragablzItem), new PropertyMetadata(default(double), OnYChanged));
public double Y
{
get { return (double) GetValue(YProperty); }
set { SetValue(YProperty, value); }
}
public static readonly RoutedEvent YChangedEvent =
EventManager.RegisterRoutedEvent(
"YChanged",
RoutingStrategy.Bubble,
typeof(RoutedPropertyChangedEventHandler<double>),
typeof(DragablzItem));
public event RoutedPropertyChangedEventHandler<double> YChanged
{
add { AddHandler(YChangedEvent, value); }
remove { RemoveHandler(IsDraggingChangedEvent, value); }
}
private static void OnYChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var instance = (DragablzItem)d;
var args = new RoutedPropertyChangedEventArgs<double>(
(double)e.OldValue,
(double)e.NewValue)
{
RoutedEvent = YChangedEvent
};
instance.RaiseEvent(args);
}
private static readonly DependencyPropertyKey LogicalIndexPropertyKey =
DependencyProperty.RegisterReadOnly(
"LogicalIndex", typeof (int), typeof (DragablzItem),
new PropertyMetadata(default(int), OnLogicalIndexChanged));
public static readonly DependencyProperty LogicalIndexProperty =
LogicalIndexPropertyKey.DependencyProperty;
public int LogicalIndex
{
get { return (int) GetValue(LogicalIndexProperty); }
internal set { SetValue(LogicalIndexPropertyKey, value); }
}
public static readonly RoutedEvent LogicalIndexChangedEvent =
EventManager.RegisterRoutedEvent(
"LogicalIndexChanged",
RoutingStrategy.Bubble,
typeof (RoutedPropertyChangedEventHandler<int>),
typeof (DragablzItem));
public event RoutedPropertyChangedEventHandler<int> LogicalIndexChanged
{
add { AddHandler(LogicalIndexChangedEvent, value); }
remove { RemoveHandler(LogicalIndexChangedEvent, value); }
}
private static void OnLogicalIndexChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var instance = (DragablzItem) d;
var args = new RoutedPropertyChangedEventArgs<int>(
(int) e.OldValue,
(int) e.NewValue)
{
RoutedEvent = DragablzItem.LogicalIndexChangedEvent
};
instance.RaiseEvent(args);
}
public static readonly DependencyProperty SizeGripProperty = DependencyProperty.RegisterAttached(
"SizeGrip", typeof (SizeGrip), typeof (DragablzItem), new PropertyMetadata(default(SizeGrip), SizeGripPropertyChangedCallback));
private static void SizeGripPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var thumb = (dependencyObject as Thumb);
if (thumb == null) return;
thumb.DragDelta += SizeThumbOnDragDelta;
}
private static void SizeThumbOnDragDelta(object sender, DragDeltaEventArgs dragDeltaEventArgs)
{
var thumb = ((Thumb) sender);
var dragablzItem = thumb.VisualTreeAncestory().OfType<DragablzItem>().FirstOrDefault();
if (dragablzItem == null) return;
var sizeGrip = (SizeGrip) thumb.GetValue(SizeGripProperty);
var width = dragablzItem.ActualWidth;
var height = dragablzItem.ActualHeight;
var x = dragablzItem.X;
var y = dragablzItem.Y;
switch (sizeGrip)
{
case SizeGrip.NotApplicable:
break;
case SizeGrip.Left:
width += -dragDeltaEventArgs.HorizontalChange;
x += dragDeltaEventArgs.HorizontalChange;
break;
case SizeGrip.TopLeft:
width += -dragDeltaEventArgs.HorizontalChange;
height += -dragDeltaEventArgs.VerticalChange;
x += dragDeltaEventArgs.HorizontalChange;
y += dragDeltaEventArgs.VerticalChange;
break;
case SizeGrip.Top:
height += -dragDeltaEventArgs.VerticalChange;
y += dragDeltaEventArgs.VerticalChange;
break;
case SizeGrip.TopRight:
height += -dragDeltaEventArgs.VerticalChange;
width += dragDeltaEventArgs.HorizontalChange;
y += dragDeltaEventArgs.VerticalChange;
break;
case SizeGrip.Right:
width += dragDeltaEventArgs.HorizontalChange;
break;
case SizeGrip.BottomRight:
width += dragDeltaEventArgs.HorizontalChange;
height += dragDeltaEventArgs.VerticalChange;
break;
case SizeGrip.Bottom:
height += dragDeltaEventArgs.VerticalChange;
break;
case SizeGrip.BottomLeft:
height += dragDeltaEventArgs.VerticalChange;
width += -dragDeltaEventArgs.HorizontalChange;
x += dragDeltaEventArgs.HorizontalChange;
break;
default:
throw new ArgumentOutOfRangeException();
}
dragablzItem.SetCurrentValue(XProperty, x);
dragablzItem.SetCurrentValue(YProperty, y);
dragablzItem.SetCurrentValue(WidthProperty, Math.Max(width, thumb.DesiredSize.Width));
dragablzItem.SetCurrentValue(HeightProperty, Math.Max(height, thumb.DesiredSize.Height));
}
public static void SetSizeGrip(DependencyObject element, SizeGrip value)
{
element.SetValue(SizeGripProperty, value);
}
public static SizeGrip GetSizeGrip(DependencyObject element)
{
return (SizeGrip) element.GetValue(SizeGripProperty);
}
/// <summary>
/// Allows item content to be rotated (in suppported templates), typically for use in a vertical/side tab.
/// </summary>
public static readonly DependencyProperty ContentRotateTransformAngleProperty = DependencyProperty.RegisterAttached(
"ContentRotateTransformAngle", typeof (double), typeof (DragablzItem), new FrameworkPropertyMetadata(default(double), FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// Allows item content to be rotated (in suppported templates), typically for use in a vertical/side tab.
/// </summary>
/// <param name="element"></param>
/// <param name="value"></param>
public static void SetContentRotateTransformAngle(DependencyObject element, double value)
{
element.SetValue(ContentRotateTransformAngleProperty, value);
}
/// <summary>
/// Allows item content to be rotated (in suppported templates), typically for use in a vertical/side tab.
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
public static double GetContentRotateTransformAngle(DependencyObject element)
{
return (double) element.GetValue(ContentRotateTransformAngleProperty);
}
public static readonly DependencyProperty IsSelectedProperty = DependencyProperty.Register(
"IsSelected", typeof(bool), typeof(DragablzItem), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.AffectsParentMeasure));
public bool IsSelected
{
get { return (bool) GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
private static readonly DependencyPropertyKey IsDraggingPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsDragging", typeof (bool), typeof (DragablzItem),
new PropertyMetadata(default(bool), OnIsDraggingChanged));
public static readonly DependencyProperty IsDraggingProperty =
IsDraggingPropertyKey.DependencyProperty;
public bool IsDragging
{
get { return (bool) GetValue(IsDraggingProperty); }
internal set { SetValue(IsDraggingPropertyKey, value); }
}
public static readonly RoutedEvent IsDraggingChangedEvent =
EventManager.RegisterRoutedEvent(
"IsDraggingChanged",
RoutingStrategy.Bubble,
typeof (RoutedPropertyChangedEventHandler<bool>),
typeof (DragablzItem));
public event RoutedPropertyChangedEventHandler<bool> IsDraggingChanged
{
add { AddHandler(IsDraggingChangedEvent, value); }
remove { RemoveHandler(IsDraggingChangedEvent, value); }
}
internal object UnderlyingContent { get; set; }
private static void OnIsDraggingChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var instance = (DragablzItem)d;
var args = new RoutedPropertyChangedEventArgs<bool>(
(bool)e.OldValue,
(bool)e.NewValue) { RoutedEvent = IsDraggingChangedEvent };
instance.RaiseEvent(args);
}
public static readonly RoutedEvent MouseDownWithinEvent =
EventManager.RegisterRoutedEvent(
"MouseDownWithin",
RoutingStrategy.Bubble,
typeof(DragablzItemEventHandler),
typeof (DragablzItem));
private static void OnMouseDownWithin(DependencyObject d)
{
var instance = (DragablzItem)d;
instance.RaiseEvent(new DragablzItemEventArgs(MouseDownWithinEvent, instance));
}
private static readonly DependencyPropertyKey IsSiblingDraggingPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsSiblingDragging", typeof (bool), typeof (DragablzItem),
new PropertyMetadata(default(bool), OnIsSiblingDraggingChanged));
public static readonly DependencyProperty IsSiblingDraggingProperty =
IsSiblingDraggingPropertyKey.DependencyProperty;
public bool IsSiblingDragging
{
get { return (bool) GetValue(IsSiblingDraggingProperty); }
internal set { SetValue(IsSiblingDraggingPropertyKey, value); }
}
public static readonly RoutedEvent IsSiblingDraggingChangedEvent =
EventManager.RegisterRoutedEvent(
"IsSiblingDraggingChanged",
RoutingStrategy.Bubble,
typeof (RoutedPropertyChangedEventHandler<bool>),
typeof (DragablzItem));
public event RoutedPropertyChangedEventHandler<bool> IsSiblingDraggingChanged
{
add { AddHandler(IsSiblingDraggingChangedEvent, value); }
remove { RemoveHandler(IsSiblingDraggingChangedEvent, value); }
}
private static void OnIsSiblingDraggingChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var instance = (DragablzItem) d;
var args = new RoutedPropertyChangedEventArgs<bool>(
(bool) e.OldValue,
(bool) e.NewValue)
{
RoutedEvent = IsSiblingDraggingChangedEvent
};
instance.RaiseEvent(args);
}
public static readonly RoutedEvent DragStarted =
EventManager.RegisterRoutedEvent(
"DragStarted",
RoutingStrategy.Bubble,
typeof(DragablzDragStartedEventHandler),
typeof(DragablzItem));
protected void OnDragStarted(DragablzDragStartedEventArgs e)
{
RaiseEvent(e);
}
public static readonly RoutedEvent DragDelta =
EventManager.RegisterRoutedEvent(
"DragDelta",
RoutingStrategy.Bubble,
typeof (DragablzDragDeltaEventHandler),
typeof (DragablzItem));
protected void OnDragDelta(DragablzDragDeltaEventArgs e)
{
RaiseEvent(e);
}
public static readonly RoutedEvent PreviewDragDelta =
EventManager.RegisterRoutedEvent(
"PreviewDragDelta",
RoutingStrategy.Tunnel,
typeof(DragablzDragDeltaEventHandler),
typeof(DragablzItem));
protected void OnPreviewDragDelta(DragablzDragDeltaEventArgs e)
{
RaiseEvent(e);
}
public static readonly RoutedEvent DragCompleted =
EventManager.RegisterRoutedEvent(
"DragCompleted",
RoutingStrategy.Bubble,
typeof(DragablzDragCompletedEventHandler),
typeof(DragablzItem));
protected void OnDragCompleted(DragCompletedEventArgs e)
{
var args = new DragablzDragCompletedEventArgs(DragCompleted, this, e);
RaiseEvent(args);
//OK, this is a cheeky bit. A completed drag may have occured after a tab as been pushed
//intom a new window, which means we may have reverted to the template thumb. So, let's
//refresh the thumb in case the user has a custom one
_customThumb = FindCustomThumb();
_templateSubscriptions.Disposable = SelectAndSubscribeToThumb().Item2;
}
/// <summary>
/// <see cref="DragablzItem" /> templates contain a thumb, which is used to drag the item around.
/// For most scenarios this is fine, but by setting this flag to <value>true</value> you can define
/// a custom thumb in your content, without having to override the template. This can be useful if you
/// have extra content; such as a custom button that you want the user to be able to interact with (as usually
/// the default thumb will handle mouse interaction).
/// </summary>
public static readonly DependencyProperty IsCustomThumbProperty = DependencyProperty.RegisterAttached(
"IsCustomThumb", typeof (bool), typeof (DragablzItem), new PropertyMetadata(default(bool), IsCustomThumbPropertyChangedCallback));
private static void IsCustomThumbPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var thumb = dependencyObject as Thumb;
if (thumb == null) throw new ApplicationException("IsCustomThumb can only be applied to a thumb");
if (thumb.IsLoaded)
ApplyCustomThumbSetting(thumb);
else
thumb.Loaded += CustomThumbOnLoaded;
}
/// <summary>
/// <see cref="DragablzItem" /> templates contain a thumb, which is used to drag the item around.
/// For most scenarios this is fine, but by setting this flag to <value>true</value> you can define
/// a custom thumb in your content, without having to override the template. This can be useful if you
/// have extra content; such as a custom button that you want the user to be able to interact with (as usually
/// the default thumb will handle mouse interaction).
/// </summary>
public static void SetIsCustomThumb(Thumb element, bool value)
{
element.SetValue(IsCustomThumbProperty, value);
}
public static bool GetIsCustomThumb(Thumb element)
{
return (bool) element.GetValue(IsCustomThumbProperty);
}
private bool _isTemplateThumbWithMouseAfterSeize = false;
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var thumbAndSubscription = SelectAndSubscribeToThumb();
_templateSubscriptions.Disposable = thumbAndSubscription.Item2;
if (_seizeDragWithTemplate && thumbAndSubscription.Item1 != null)
{
_isTemplateThumbWithMouseAfterSeize = true;
Mouse.AddLostMouseCaptureHandler(this, LostMouseAfterSeizeHandler);
if (_dragSeizedContinuation != null)
_dragSeizedContinuation(this);
_dragSeizedContinuation = null;
Dispatcher.BeginInvoke(new Action(() => thumbAndSubscription.Item1.RaiseEvent(new MouseButtonEventArgs(InputManager.Current.PrimaryMouseDevice,
0,
MouseButton.Left) {RoutedEvent = MouseLeftButtonDownEvent})));
}
_seizeDragWithTemplate = false;
}
protected override void OnPreviewMouseRightButtonDown(MouseButtonEventArgs e)
{
if (_thumb != null)
{
var currentThumbIsHitTestVisible = _thumb.IsHitTestVisible;
_thumb.SetCurrentValue(IsHitTestVisibleProperty, false);
_rightMouseUpCleanUpDisposable.Disposable = Disposable.Create(() =>
{
_thumb.SetCurrentValue(IsHitTestVisibleProperty, currentThumbIsHitTestVisible);
});
}
else
{
_rightMouseUpCleanUpDisposable.Disposable = Disposable.Empty;
}
base.OnPreviewMouseRightButtonDown(e);
}
protected override void OnPreviewMouseRightButtonUp(MouseButtonEventArgs e)
{
_rightMouseUpCleanUpDisposable.Disposable = Disposable.Empty;
base.OnPreviewMouseRightButtonUp(e);
}
private void LostMouseAfterSeizeHandler(object sender, MouseEventArgs mouseEventArgs)
{
_isTemplateThumbWithMouseAfterSeize = false;
Mouse.RemoveLostMouseCaptureHandler(this, LostMouseAfterSeizeHandler);
}
internal void InstigateDrag(Action<DragablzItem> continuation)
{
_dragSeizedContinuation = continuation;
var thumb = GetTemplateChild(ThumbPartName) as Thumb;
if (thumb != null)
{
thumb.CaptureMouse();
}
else
_seizeDragWithTemplate = true;
}
internal Point MouseAtDragStart { get; set; }
internal string PartitionAtDragStart { get; set; }
internal bool IsDropTargetFound { get; set; }
private void ThumbOnDragCompleted(object sender, DragCompletedEventArgs dragCompletedEventArgs)
{
OnDragCompleted(dragCompletedEventArgs);
MouseAtDragStart = new Point();
}
private void ThumbOnDragDelta(object sender, DragDeltaEventArgs dragDeltaEventArgs)
{
var thumb = (Thumb) sender;
var previewEventArgs = new DragablzDragDeltaEventArgs(PreviewDragDelta, this, dragDeltaEventArgs);
OnPreviewDragDelta(previewEventArgs);
if (previewEventArgs.Cancel)
thumb.CancelDrag();
if (!previewEventArgs.Handled)
{
var eventArgs = new DragablzDragDeltaEventArgs(DragDelta, this, dragDeltaEventArgs);
OnDragDelta(eventArgs);
if (eventArgs.Cancel)
thumb.CancelDrag();
}
}
private void ThumbOnDragStarted(object sender, DragStartedEventArgs dragStartedEventArgs)
{
MouseAtDragStart = Mouse.GetPosition(this);
OnDragStarted(new DragablzDragStartedEventArgs(DragStarted, this, dragStartedEventArgs));
}
private void MouseDownHandler(object sender, RoutedEventArgs routedEventArgs)
{
OnMouseDownWithin(this);
}
private static void CustomThumbOnLoaded(object sender, RoutedEventArgs routedEventArgs)
{
var thumb = (Thumb)sender;
thumb.Loaded -= CustomThumbOnLoaded;
ApplyCustomThumbSetting(thumb);
}
private Thumb FindCustomThumb()
{
return this.VisualTreeDepthFirstTraversal().OfType<Thumb>().FirstOrDefault(GetIsCustomThumb);
}
private static void ApplyCustomThumbSetting(Thumb thumb)
{
var dragablzItem = thumb.VisualTreeAncestory().OfType<DragablzItem>().FirstOrDefault();
if (dragablzItem == null) throw new ApplicationException("Cannot find parent DragablzItem for custom thumb");
var enableCustomThumb = (bool)thumb.GetValue(IsCustomThumbProperty);
dragablzItem._customThumb = enableCustomThumb ? thumb : null;
dragablzItem._templateSubscriptions.Disposable = dragablzItem.SelectAndSubscribeToThumb().Item2;
if (dragablzItem._customThumb != null && dragablzItem._isTemplateThumbWithMouseAfterSeize)
dragablzItem.Dispatcher.BeginInvoke(new Action(() => dragablzItem._customThumb.RaiseEvent(new MouseButtonEventArgs(InputManager.Current.PrimaryMouseDevice,
0,
MouseButton.Left) { RoutedEvent = MouseLeftButtonDownEvent })));
}
private Tuple<Thumb, IDisposable> SelectAndSubscribeToThumb()
{
var templateThumb = GetTemplateChild(ThumbPartName) as Thumb;
templateThumb?.SetCurrentValue(IsHitTestVisibleProperty, _customThumb == null);
_thumb = _customThumb ?? templateThumb;
if (_thumb != null)
{
_thumb.DragStarted += ThumbOnDragStarted;
_thumb.DragDelta += ThumbOnDragDelta;
_thumb.DragCompleted += ThumbOnDragCompleted;
}
var tidyUpThumb = _thumb;
var disposable = Disposable.Create(() =>
{
if (tidyUpThumb == null) return;
tidyUpThumb.DragStarted -= ThumbOnDragStarted;
tidyUpThumb.DragDelta -= ThumbOnDragDelta;
tidyUpThumb.DragCompleted -= ThumbOnDragCompleted;
});
return new Tuple<Thumb, IDisposable>(_thumb, disposable);
}
}
}

View File

@@ -0,0 +1,37 @@
using System;
using System.Windows;
using System.Windows.Controls.Primitives;
namespace Dragablz
{
public delegate void DragablzItemEventHandler(object sender, DragablzItemEventArgs e);
public class DragablzItemEventArgs : RoutedEventArgs
{
private readonly DragablzItem _dragablzItem;
public DragablzItemEventArgs(DragablzItem dragablzItem)
{
if (dragablzItem == null) throw new ArgumentNullException("dragablzItem");
_dragablzItem = dragablzItem;
}
public DragablzItemEventArgs(RoutedEvent routedEvent, DragablzItem dragablzItem)
: base(routedEvent)
{
_dragablzItem = dragablzItem;
}
public DragablzItemEventArgs(RoutedEvent routedEvent, object source, DragablzItem dragablzItem)
: base(routedEvent, source)
{
_dragablzItem = dragablzItem;
}
public DragablzItem DragablzItem
{
get { return _dragablzItem; }
}
}
}

View File

@@ -0,0 +1,401 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Threading;
using Dragablz.Core;
namespace Dragablz
{
/// <summary>
/// Items control which typically uses a canvas and
/// </summary>
public class DragablzItemsControl : ItemsControl
{
private object[] _previousSortQueryResult;
static DragablzItemsControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DragablzItemsControl), new FrameworkPropertyMetadata(typeof(DragablzItemsControl)));
}
public DragablzItemsControl()
{
ItemContainerGenerator.StatusChanged += ItemContainerGeneratorOnStatusChanged;
ItemContainerGenerator.ItemsChanged += ItemContainerGeneratorOnItemsChanged;
AddHandler(DragablzItem.XChangedEvent, new RoutedPropertyChangedEventHandler<double>(ItemXChanged));
AddHandler(DragablzItem.YChangedEvent, new RoutedPropertyChangedEventHandler<double>(ItemYChanged));
AddHandler(DragablzItem.DragDelta, new DragablzDragDeltaEventHandler(ItemDragDelta));
AddHandler(DragablzItem.DragCompleted, new DragablzDragCompletedEventHandler(ItemDragCompleted));
AddHandler(DragablzItem.DragStarted, new DragablzDragStartedEventHandler(ItemDragStarted));
AddHandler(DragablzItem.MouseDownWithinEvent, new DragablzItemEventHandler(ItemMouseDownWithinHandlerTarget));
}
public static readonly DependencyProperty FixedItemCountProperty = DependencyProperty.Register(
"FixedItemCount", typeof (int), typeof (DragablzItemsControl), new PropertyMetadata(default(int)));
public int FixedItemCount
{
get { return (int) GetValue(FixedItemCountProperty); }
set { SetValue(FixedItemCountProperty, value); }
}
private void ItemContainerGeneratorOnItemsChanged(object sender, ItemsChangedEventArgs itemsChangedEventArgs)
{
//throw new NotImplementedException();
}
protected override void ClearContainerForItemOverride(DependencyObject element, object item)
{
if (ContainerCustomisations != null && ContainerCustomisations.ClearingContainerForItemOverride != null)
ContainerCustomisations.ClearingContainerForItemOverride(element, item);
base.ClearContainerForItemOverride(element, item);
((DragablzItem)element).SizeChanged -= ItemSizeChangedEventHandler;
Dispatcher.BeginInvoke(new Action(() =>
{
var dragablzItems = DragablzItems().ToList();
if (ItemsOrganiser == null) return;
ItemsOrganiser.Organise(this, new Size(ItemsPresenterWidth, ItemsPresenterHeight), dragablzItems);
var measure = ItemsOrganiser.Measure(this, new Size(ActualWidth, ActualHeight), dragablzItems);
ItemsPresenterWidth = measure.Width;
ItemsPresenterHeight = measure.Height;
}), DispatcherPriority.Input);
}
public static readonly DependencyProperty ItemsOrganiserProperty = DependencyProperty.Register(
"ItemsOrganiser", typeof (IItemsOrganiser), typeof (DragablzItemsControl), new PropertyMetadata(default(IItemsOrganiser)));
public IItemsOrganiser ItemsOrganiser
{
get { return (IItemsOrganiser) GetValue(ItemsOrganiserProperty); }
set { SetValue(ItemsOrganiserProperty, value); }
}
public static readonly DependencyProperty PositionMonitorProperty = DependencyProperty.Register(
"PositionMonitor", typeof (PositionMonitor), typeof (DragablzItemsControl), new PropertyMetadata(default(PositionMonitor)));
public PositionMonitor PositionMonitor
{
get { return (PositionMonitor) GetValue(PositionMonitorProperty); }
set { SetValue(PositionMonitorProperty, value); }
}
private static readonly DependencyPropertyKey ItemsPresenterWidthPropertyKey =
DependencyProperty.RegisterReadOnly(
"ItemsPresenterWidth", typeof(double), typeof (DragablzItemsControl),
new PropertyMetadata(default(double)));
public static readonly DependencyProperty ItemsPresenterWidthProperty =
ItemsPresenterWidthPropertyKey.DependencyProperty;
public double ItemsPresenterWidth
{
get { return (double) GetValue(ItemsPresenterWidthProperty); }
private set { SetValue(ItemsPresenterWidthPropertyKey, value); }
}
private static readonly DependencyPropertyKey ItemsPresenterHeightPropertyKey =
DependencyProperty.RegisterReadOnly(
"ItemsPresenterHeight", typeof (double), typeof (DragablzItemsControl),
new PropertyMetadata(default(double)));
public static readonly DependencyProperty ItemsPresenterHeightProperty =
ItemsPresenterHeightPropertyKey.DependencyProperty;
public double ItemsPresenterHeight
{
get { return (double) GetValue(ItemsPresenterHeightProperty); }
private set { SetValue(ItemsPresenterHeightPropertyKey, value); }
}
/// <summary>
/// Adds an item to the underlying source, displaying in a specific position in rendered control.
/// </summary>
/// <param name="item"></param>
/// <param name="addLocationHint"></param>
public void AddToSource(object item, AddLocationHint addLocationHint)
{
AddToSource(item, null, addLocationHint);
}
/// <summary>
/// Adds an item to the underlying source, displaying in a specific position in rendered control.
/// </summary>
/// <param name="item"></param>
/// <param name="nearItem"></param>
/// <param name="addLocationHint"></param>
public void AddToSource(object item, object nearItem, AddLocationHint addLocationHint)
{
CollectionTeaser collectionTeaser;
if (CollectionTeaser.TryCreate(ItemsSource, out collectionTeaser))
collectionTeaser.Add(item);
else
Items.Add(item);
MoveItem(new MoveItemRequest(item, nearItem, addLocationHint));
}
internal ContainerCustomisations ContainerCustomisations { get; set; }
private void ItemContainerGeneratorOnStatusChanged(object sender, EventArgs eventArgs)
{
if (ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated) return;
InvalidateMeasure();
//extra kick
Dispatcher.BeginInvoke(new Action(InvalidateMeasure), DispatcherPriority.Loaded);
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
var dragablzItem = item as DragablzItem;
if (dragablzItem == null) return false;
return true;
}
protected override DependencyObject GetContainerForItemOverride()
{
var result = ContainerCustomisations != null && ContainerCustomisations.GetContainerForItemOverride != null
? ContainerCustomisations.GetContainerForItemOverride()
: new DragablzItem();
result.SizeChanged += ItemSizeChangedEventHandler;
return result;
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
if (ContainerCustomisations != null && ContainerCustomisations.PrepareContainerForItemOverride != null)
ContainerCustomisations.PrepareContainerForItemOverride(element, item);
base.PrepareContainerForItemOverride(element, item);
}
protected override Size MeasureOverride(Size constraint)
{
if (ItemsOrganiser == null) return base.MeasureOverride(constraint);
if (LockedMeasure.HasValue)
{
ItemsPresenterWidth = LockedMeasure.Value.Width;
ItemsPresenterHeight = LockedMeasure.Value.Height;
return LockedMeasure.Value;
}
var dragablzItems = DragablzItems().ToList();
var maxConstraint = new Size(double.PositiveInfinity, double.PositiveInfinity);
ItemsOrganiser.Organise(this, maxConstraint, dragablzItems);
var measure = ItemsOrganiser.Measure(this, new Size(ActualWidth, ActualHeight), dragablzItems);
ItemsPresenterWidth = measure.Width;
ItemsPresenterHeight = measure.Height;
var width = double.IsInfinity(constraint.Width) ? measure.Width : constraint.Width;
var height = double.IsInfinity(constraint.Height) ? measure.Height : constraint.Height;
return new Size(width, height);
}
internal void InstigateDrag(object item, Action<DragablzItem> continuation)
{
var dragablzItem = (DragablzItem)ItemContainerGenerator.ContainerFromItem(item);
dragablzItem.InstigateDrag(continuation);
}
/// <summary>
/// Move an item in the rendered layout.
/// </summary>
/// <param name="moveItemRequest"></param>
public void MoveItem(MoveItemRequest moveItemRequest)
{
if (moveItemRequest == null) throw new ArgumentNullException("moveItemRequest");
if (ItemsOrganiser == null) return;
var dragablzItem = moveItemRequest.Item as DragablzItem ??
ItemContainerGenerator.ContainerFromItem(
moveItemRequest.Item) as DragablzItem;
var contextDragablzItem = moveItemRequest.Context as DragablzItem ??
ItemContainerGenerator.ContainerFromItem(
moveItemRequest.Context) as DragablzItem;
if (dragablzItem == null) return;
var sortedItems = DragablzItems().OrderBy(di => di.LogicalIndex).ToList();
sortedItems.Remove(dragablzItem);
switch (moveItemRequest.AddLocationHint)
{
case AddLocationHint.First:
sortedItems.Insert(0, dragablzItem);
break;
case AddLocationHint.Last:
sortedItems.Add(dragablzItem);
break;
case AddLocationHint.Prior:
case AddLocationHint.After:
if (contextDragablzItem == null)
return;
var contextIndex = sortedItems.IndexOf(contextDragablzItem);
sortedItems.Insert(moveItemRequest.AddLocationHint == AddLocationHint.Prior ? contextIndex : contextIndex + 1, dragablzItem);
break;
default:
throw new ArgumentOutOfRangeException();
}
//TODO might not be too great for perf on larger lists
var orderedEnumerable = sortedItems.OrderBy(di => sortedItems.IndexOf(di));
ItemsOrganiser.Organise(this, new Size(ItemsPresenterWidth, ItemsPresenterHeight), orderedEnumerable);
}
internal IEnumerable<DragablzItem> DragablzItems()
{
return this.Containers<DragablzItem>().ToList();
}
internal Size? LockedMeasure { get; set; }
private void ItemDragStarted(object sender, DragablzDragStartedEventArgs eventArgs)
{
if (ItemsOrganiser != null)
{
var bounds = new Size(ActualWidth, ActualHeight);
ItemsOrganiser.OrganiseOnDragStarted(this, bounds,
DragablzItems().Except(new[] { eventArgs.DragablzItem }).ToList(),
eventArgs.DragablzItem);
}
eventArgs.Handled = true;
Dispatcher.BeginInvoke(new Action(InvalidateMeasure), DispatcherPriority.Loaded);
}
private void ItemDragCompleted(object sender, DragablzDragCompletedEventArgs eventArgs)
{
var dragablzItems = DragablzItems()
.Select(i =>
{
i.IsDragging = false;
i.IsSiblingDragging = false;
return i;
})
.ToList();
if (ItemsOrganiser != null)
{
var bounds = new Size(ActualWidth, ActualHeight);
ItemsOrganiser.OrganiseOnDragCompleted(this, bounds,
dragablzItems.Except(eventArgs.DragablzItem),
eventArgs.DragablzItem);
}
eventArgs.Handled = true;
//wowsers
Dispatcher.BeginInvoke(new Action(InvalidateMeasure));
Dispatcher.BeginInvoke(new Action(InvalidateMeasure), DispatcherPriority.Loaded);
}
private void ItemDragDelta(object sender, DragablzDragDeltaEventArgs eventArgs)
{
var bounds = new Size(ItemsPresenterWidth, ItemsPresenterHeight);
var desiredLocation = new Point(
eventArgs.DragablzItem.X + eventArgs.DragDeltaEventArgs.HorizontalChange,
eventArgs.DragablzItem.Y + eventArgs.DragDeltaEventArgs.VerticalChange
);
if (ItemsOrganiser != null)
{
if (FixedItemCount > 0 &&
ItemsOrganiser.Sort(DragablzItems()).Take(FixedItemCount).Contains(eventArgs.DragablzItem))
{
eventArgs.Handled = true;
return;
}
desiredLocation = ItemsOrganiser.ConstrainLocation(this, bounds,
new Point(eventArgs.DragablzItem.X, eventArgs.DragablzItem.Y),
new Size(eventArgs.DragablzItem.ActualWidth, eventArgs.DragablzItem.ActualHeight),
desiredLocation, eventArgs.DragablzItem.DesiredSize);
}
foreach (var dragableItem in DragablzItems()
.Except(new[] { eventArgs.DragablzItem })) // how about Linq.Where() ?
{
dragableItem.IsSiblingDragging = true;
}
eventArgs.DragablzItem.IsDragging = true;
eventArgs.DragablzItem.X = desiredLocation.X;
eventArgs.DragablzItem.Y = desiredLocation.Y;
if (ItemsOrganiser != null)
ItemsOrganiser.OrganiseOnDrag(
this,
bounds,
DragablzItems().Except(new[] {eventArgs.DragablzItem}), eventArgs.DragablzItem);
eventArgs.DragablzItem.BringIntoView();
eventArgs.Handled = true;
}
private void ItemXChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
{
UpdateMonitor(routedPropertyChangedEventArgs);
}
private void ItemYChanged(object sender, RoutedPropertyChangedEventArgs<double> routedPropertyChangedEventArgs)
{
UpdateMonitor(routedPropertyChangedEventArgs);
}
private void UpdateMonitor(RoutedEventArgs routedPropertyChangedEventArgs)
{
if (PositionMonitor == null) return;
var dragablzItem = (DragablzItem) routedPropertyChangedEventArgs.OriginalSource;
if (!Equals(ItemsControlFromItemContainer(dragablzItem), this)) return;
PositionMonitor.OnLocationChanged(new LocationChangedEventArgs(dragablzItem.Content, new Point(dragablzItem.X, dragablzItem.Y)));
var linearPositionMonitor = PositionMonitor as StackPositionMonitor;
if (linearPositionMonitor == null) return;
var sortedItems = linearPositionMonitor.Sort(this.Containers<DragablzItem>()).Select(di => di.Content).ToArray();
if (_previousSortQueryResult == null || !_previousSortQueryResult.SequenceEqual(sortedItems))
linearPositionMonitor.OnOrderChanged(new OrderChangedEventArgs(_previousSortQueryResult, sortedItems));
_previousSortQueryResult = sortedItems;
}
private void ItemMouseDownWithinHandlerTarget(object sender, DragablzItemEventArgs e)
{
if (ItemsOrganiser == null) return;
var bounds = new Size(ActualWidth, ActualHeight);
ItemsOrganiser.OrganiseOnMouseDownWithin(this, bounds,
DragablzItems().Except(e.DragablzItem).ToList(),
e.DragablzItem);
}
private void ItemSizeChangedEventHandler(object sender, SizeChangedEventArgs e)
{
InvalidateMeasure();
//extra kick
Dispatcher.BeginInvoke(new Action(InvalidateMeasure), DispatcherPriority.Loaded);
}
}
}

View File

@@ -0,0 +1,370 @@
using System;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Threading;
using Dragablz.Core;
using Dragablz.Dockablz;
using Dragablz.Referenceless;
namespace Dragablz
{
/// <summary>
/// It is not necessary to use a <see cref="DragablzWindow"/> to gain tab dragging features.
/// What this Window does is allow a quick way to remove the Window border, and support transparency whilst
/// dragging.
/// </summary>
[TemplatePart(Name = WindowSurfaceGridPartName, Type = typeof(Grid))]
[TemplatePart(Name = WindowRestoreThumbPartName, Type = typeof(Thumb))]
[TemplatePart(Name = WindowResizeThumbPartName, Type = typeof(Thumb))]
public class DragablzWindow : Window
{
public const string WindowSurfaceGridPartName = "PART_WindowSurface";
public const string WindowRestoreThumbPartName = "PART_WindowRestoreThumb";
public const string WindowResizeThumbPartName = "PART_WindowResizeThumb";
private readonly SerialDisposable _templateSubscription = new SerialDisposable();
public static RoutedCommand CloseWindowCommand = new RoutedCommand();
public static RoutedCommand RestoreWindowCommand = new RoutedCommand();
public static RoutedCommand MaximizeWindowCommand = new RoutedCommand();
public static RoutedCommand MinimizeWindowCommand = new RoutedCommand();
private const int ResizeMargin = 4;
private Size _sizeWhenResizeBegan;
private Point _screenMousePointWhenResizeBegan;
private Point _windowLocationPointWhenResizeBegan;
private SizeGrip _resizeType;
private static SizeGrip[] _leftMode = new[] { SizeGrip.TopLeft, SizeGrip.Left, SizeGrip.BottomLeft };
private static SizeGrip[] _rightMode = new[] { SizeGrip.TopRight, SizeGrip.Right, SizeGrip.BottomRight };
private static SizeGrip[] _topMode = new[] { SizeGrip.TopLeft, SizeGrip.Top, SizeGrip.TopRight };
private static SizeGrip[] _bottomMode = new[] { SizeGrip.BottomLeft, SizeGrip.Bottom, SizeGrip.BottomRight };
private static double _xScale = 1;
private static double _yScale = 1;
private static bool _dpiInitialized = false;
static DragablzWindow()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DragablzWindow), new FrameworkPropertyMetadata(typeof(DragablzWindow)));
}
public DragablzWindow()
{
AddHandler(DragablzItem.DragStarted, new DragablzDragStartedEventHandler(ItemDragStarted), true);
AddHandler(DragablzItem.DragCompleted, new DragablzDragCompletedEventHandler(ItemDragCompleted), true);
CommandBindings.Add(new CommandBinding(CloseWindowCommand, CloseWindowExecuted));
CommandBindings.Add(new CommandBinding(MaximizeWindowCommand, MaximizeWindowExecuted));
CommandBindings.Add(new CommandBinding(MinimizeWindowCommand, MinimizeWindowExecuted));
CommandBindings.Add(new CommandBinding(RestoreWindowCommand, RestoreWindowExecuted));
}
private static readonly DependencyPropertyKey IsWindowBeingDraggedByTabPropertyKey =
DependencyProperty.RegisterReadOnly(
"IsBeingDraggedByTab", typeof (bool), typeof (DragablzWindow),
new PropertyMetadata(default(bool)));
public static readonly DependencyProperty IsBeingDraggedByTabProperty =
IsWindowBeingDraggedByTabPropertyKey.DependencyProperty;
public bool IsBeingDraggedByTab
{
get { return (bool) GetValue(IsBeingDraggedByTabProperty); }
private set { SetValue(IsWindowBeingDraggedByTabPropertyKey, value); }
}
private void ItemDragCompleted(object sender, DragablzDragCompletedEventArgs e)
{
IsBeingDraggedByTab = false;
}
private void ItemDragStarted(object sender, DragablzDragStartedEventArgs e)
{
var sourceOfDragItemsControl = ItemsControl.ItemsControlFromItemContainer(e.DragablzItem) as DragablzItemsControl;
if (sourceOfDragItemsControl == null) return;
var sourceTab = TabablzControl.GetOwnerOfHeaderItems(sourceOfDragItemsControl);
if (sourceTab == null) return;
if (sourceOfDragItemsControl.Items.Count != 1
|| (sourceTab.InterTabController != null && !sourceTab.InterTabController.MoveWindowWithSolitaryTabs)
|| Layout.IsContainedWithinBranch(sourceOfDragItemsControl))
return;
IsBeingDraggedByTab = true;
}
public override void OnApplyTemplate()
{
var windowSurfaceGrid = GetTemplateChild(WindowSurfaceGridPartName) as Grid;
var windowRestoreThumb = GetTemplateChild(WindowRestoreThumbPartName) as Thumb;
var windowResizeThumb = GetTemplateChild(WindowResizeThumbPartName) as Thumb;
_templateSubscription.Disposable = Disposable.Create(() =>
{
if (windowSurfaceGrid != null)
{
windowSurfaceGrid.MouseLeftButtonDown -= WindowSurfaceGridOnMouseLeftButtonDown;
}
if (windowRestoreThumb != null)
{
windowRestoreThumb.DragDelta -= WindowMoveThumbOnDragDelta;
windowRestoreThumb.MouseDoubleClick -= WindowRestoreThumbOnMouseDoubleClick;
}
if (windowResizeThumb == null) return;
windowResizeThumb.MouseMove -= WindowResizeThumbOnMouseMove;
windowResizeThumb.DragStarted -= WindowResizeThumbOnDragStarted;
windowResizeThumb.DragDelta -= WindowResizeThumbOnDragDelta;
windowResizeThumb.DragCompleted -= WindowResizeThumbOnDragCompleted;
});
base.OnApplyTemplate();
if (windowSurfaceGrid != null)
{
windowSurfaceGrid.MouseLeftButtonDown += WindowSurfaceGridOnMouseLeftButtonDown;
}
if (windowRestoreThumb != null)
{
windowRestoreThumb.DragDelta += WindowMoveThumbOnDragDelta;
windowRestoreThumb.MouseDoubleClick += WindowRestoreThumbOnMouseDoubleClick;
}
if (windowResizeThumb == null) return;
windowResizeThumb.MouseMove += WindowResizeThumbOnMouseMove;
windowResizeThumb.DragStarted += WindowResizeThumbOnDragStarted;
windowResizeThumb.DragDelta += WindowResizeThumbOnDragDelta;
windowResizeThumb.DragCompleted += WindowResizeThumbOnDragCompleted;
}
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
var resizeThumb = GetTemplateChild(WindowResizeThumbPartName) as Thumb;
if (resizeThumb != null)
{
var outerRectangleGeometry = new RectangleGeometry(new Rect(sizeInfo.NewSize));
var innerRectangleGeometry =
new RectangleGeometry(new Rect(ResizeMargin, ResizeMargin, sizeInfo.NewSize.Width - ResizeMargin * 2, sizeInfo.NewSize.Height - ResizeMargin*2));
resizeThumb.Clip = new CombinedGeometry(GeometryCombineMode.Exclude, outerRectangleGeometry,
innerRectangleGeometry);
}
base.OnRenderSizeChanged(sizeInfo);
}
protected IntPtr CriticalHandle
{
get
{
var value = typeof (Window).GetProperty("CriticalHandle", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(this, new object[0]);
return (IntPtr) value;
}
}
private void WindowSurfaceGridOnMouseLeftButtonDown(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
if (mouseButtonEventArgs.ChangedButton != MouseButton.Left) return;
if (mouseButtonEventArgs.ClickCount == 1)
DragMove();
if (mouseButtonEventArgs.ClickCount == 2)
WindowState = WindowState.Maximized;
}
private static void WindowResizeThumbOnMouseMove(object sender, MouseEventArgs mouseEventArgs)
{
var thumb = (Thumb)sender;
var mousePositionInThumb = Mouse.GetPosition(thumb);
thumb.Cursor = SelectCursor(SelectSizingMode(mousePositionInThumb, thumb.RenderSize));
}
private void WindowRestoreThumbOnMouseDoubleClick(object sender, MouseButtonEventArgs mouseButtonEventArgs)
{
WindowState = WindowState.Normal;
}
private void WindowResizeThumbOnDragCompleted(object sender, DragCompletedEventArgs dragCompletedEventArgs)
{
Cursor = Cursors.Arrow;
}
private void WindowResizeThumbOnDragDelta(object sender, DragDeltaEventArgs dragDeltaEventArgs)
{
var mousePositionInWindow = Mouse.GetPosition(this);
var currentScreenMousePoint = PointToScreen(mousePositionInWindow);
var width = _sizeWhenResizeBegan.Width;
var height = _sizeWhenResizeBegan.Height;
var left = _windowLocationPointWhenResizeBegan.X;
var top = _windowLocationPointWhenResizeBegan.Y;
if (_leftMode.Contains(_resizeType))
{
var diff = currentScreenMousePoint.X - _screenMousePointWhenResizeBegan.X;
diff /= _xScale;
var suggestedWidth = width + -diff;
left += diff;
width = suggestedWidth;
}
if (_rightMode.Contains(_resizeType))
{
var diff = currentScreenMousePoint.X - _screenMousePointWhenResizeBegan.X;
diff /= _xScale;
width += diff;
}
if (_topMode.Contains(_resizeType))
{
var diff = currentScreenMousePoint.Y - _screenMousePointWhenResizeBegan.Y;
diff /= _yScale;
height += -diff;
top += diff;
}
if (_bottomMode.Contains(_resizeType))
{
var diff = currentScreenMousePoint.Y - _screenMousePointWhenResizeBegan.Y;
diff /= _yScale;
height += diff;
}
width = Math.Max(MinWidth, width);
height = Math.Max(MinHeight, height);
//TODO must try harder.
left = Math.Min(left, _windowLocationPointWhenResizeBegan.X + _sizeWhenResizeBegan.Width - ResizeMargin*4);
//TODO must try harder.
top = Math.Min(top, _windowLocationPointWhenResizeBegan.Y + _sizeWhenResizeBegan.Height - ResizeMargin * 4);
SetCurrentValue(WidthProperty, width);
SetCurrentValue(HeightProperty, height);
SetCurrentValue(LeftProperty, left);
SetCurrentValue(TopProperty, top);
}
private void GetDPI()
{
if (_dpiInitialized)
{
return;
}
Matrix m = PresentationSource.FromVisual(this).CompositionTarget.TransformToDevice;
_xScale = m.M11;
_yScale = m.M22;
_dpiInitialized = true;
}
private void WindowResizeThumbOnDragStarted(object sender, DragStartedEventArgs dragStartedEventArgs)
{
_sizeWhenResizeBegan = new Size(ActualWidth, ActualHeight);
_windowLocationPointWhenResizeBegan = new Point(Left, Top);
var mousePositionInWindow = Mouse.GetPosition(this);
_screenMousePointWhenResizeBegan = PointToScreen(mousePositionInWindow);
var thumb = (Thumb)sender;
var mousePositionInThumb = Mouse.GetPosition(thumb);
_resizeType = SelectSizingMode(mousePositionInThumb, thumb.RenderSize);
GetDPI();
}
private static SizeGrip SelectSizingMode(Point mousePositionInThumb, Size thumbSize)
{
if (mousePositionInThumb.X <= ResizeMargin)
{
if (mousePositionInThumb.Y <= ResizeMargin)
return SizeGrip.TopLeft;
if (mousePositionInThumb.Y >= thumbSize.Height - ResizeMargin)
return SizeGrip.BottomLeft;
return SizeGrip.Left;
}
if (mousePositionInThumb.X >= thumbSize.Width - ResizeMargin)
{
if (mousePositionInThumb.Y <= ResizeMargin)
return SizeGrip.TopRight;
if (mousePositionInThumb.Y >= thumbSize.Height - ResizeMargin)
return SizeGrip.BottomRight;
return SizeGrip.Right;
}
if (mousePositionInThumb.Y <= ResizeMargin)
return SizeGrip.Top;
return SizeGrip.Bottom;
}
private static Cursor SelectCursor(SizeGrip sizeGrip)
{
switch (sizeGrip)
{
case SizeGrip.Left:
return Cursors.SizeWE;
case SizeGrip.TopLeft:
return Cursors.SizeNWSE;
case SizeGrip.Top:
return Cursors.SizeNS;
case SizeGrip.TopRight:
return Cursors.SizeNESW;
case SizeGrip.Right:
return Cursors.SizeWE;
case SizeGrip.BottomRight:
return Cursors.SizeNWSE;
case SizeGrip.Bottom:
return Cursors.SizeNS;
case SizeGrip.BottomLeft:
return Cursors.SizeNESW;
default:
return Cursors.Arrow;
}
}
private void WindowMoveThumbOnDragDelta(object sender, DragDeltaEventArgs dragDeltaEventArgs)
{
if (WindowState != WindowState.Maximized ||
(!(Math.Abs(dragDeltaEventArgs.HorizontalChange) > 2) &&
!(Math.Abs(dragDeltaEventArgs.VerticalChange) > 2))) return;
var cursorPos = Native.GetRawCursorPos();
WindowState = WindowState.Normal;
GetDPI();
Top = cursorPos.Y / _yScale - 2;
Left = cursorPos.X / _xScale - RestoreBounds.Width / 2;
var lParam = (int)(uint)cursorPos.X | (cursorPos.Y << 16);
Native.SendMessage(CriticalHandle, WindowMessage.WM_LBUTTONUP, (IntPtr)HitTest.HT_CAPTION,
(IntPtr)lParam);
Native.SendMessage(CriticalHandle, WindowMessage.WM_SYSCOMMAND, (IntPtr)SystemCommand.SC_MOUSEMOVE,
IntPtr.Zero);
}
private void RestoreWindowExecuted(object sender, ExecutedRoutedEventArgs e)
{
Native.PostMessage(new WindowInteropHelper(this).Handle, WindowMessage.WM_SYSCOMMAND, (IntPtr)SystemCommand.SC_RESTORE, IntPtr.Zero);
}
private void MinimizeWindowExecuted(object sender, ExecutedRoutedEventArgs e)
{
Native.PostMessage(new WindowInteropHelper(this).Handle, WindowMessage.WM_SYSCOMMAND, (IntPtr)SystemCommand.SC_MINIMIZE, IntPtr.Zero);
}
private void MaximizeWindowExecuted(object sender, ExecutedRoutedEventArgs e)
{
Native.PostMessage(new WindowInteropHelper(this).Handle, WindowMessage.WM_SYSCOMMAND, (IntPtr)SystemCommand.SC_MAXIMIZE, IntPtr.Zero);
}
private void CloseWindowExecuted(object sender, ExecutedRoutedEventArgs executedRoutedEventArgs)
{
Native.PostMessage(new WindowInteropHelper(this).Handle, WindowMessage.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
}
}

View File

@@ -0,0 +1,22 @@
namespace Dragablz
{
/// <summary>
/// Provide a hint for how the header should size itself if there are no tabs left (and a Window is still open).
/// </summary>
public enum EmptyHeaderSizingHint
{
/// <summary>
/// The header size collapses to zero along the correct axis.
/// </summary>
Collapse,
/// <summary>
/// The header size remains that of the last tab prior to the tab header becoming empty.
/// </summary>
PreviousTab,
//TODO implement EmptyHeaderSizingHint.Stretch
/// <summary>
/// The header stretches along the according axis.
/// </summary>
//Stretch
}
}

View File

@@ -0,0 +1,49 @@
using System.Windows;
using System.Windows.Controls;
namespace Dragablz
{
public class HeaderedDragablzItem : DragablzItem
{
static HeaderedDragablzItem()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(HeaderedDragablzItem), new FrameworkPropertyMetadata(typeof(HeaderedDragablzItem)));
}
public static readonly DependencyProperty HeaderContentProperty = DependencyProperty.Register(
"HeaderContent", typeof (object), typeof (HeaderedDragablzItem), new PropertyMetadata(default(object)));
public object HeaderContent
{
get { return (object) GetValue(HeaderContentProperty); }
set { SetValue(HeaderContentProperty, value); }
}
public static readonly DependencyProperty HeaderContentStringFormatProperty = DependencyProperty.Register(
"HeaderContentStringFormat", typeof (string), typeof (HeaderedDragablzItem), new PropertyMetadata(default(string)));
public string HeaderContentStringFormat
{
get { return (string) GetValue(HeaderContentStringFormatProperty); }
set { SetValue(HeaderContentStringFormatProperty, value); }
}
public static readonly DependencyProperty HeaderContentTemplateProperty = DependencyProperty.Register(
"HeaderContentTemplate", typeof (DataTemplate), typeof (HeaderedDragablzItem), new PropertyMetadata(default(DataTemplate)));
public DataTemplate HeaderContentTemplate
{
get { return (DataTemplate) GetValue(HeaderContentTemplateProperty); }
set { SetValue(HeaderContentTemplateProperty, value); }
}
public static readonly DependencyProperty HeaderContentTemplateSelectorProperty = DependencyProperty.Register(
"HeaderContentTemplateSelector", typeof (DataTemplateSelector), typeof (HeaderedDragablzItem), new PropertyMetadata(default(DataTemplateSelector)));
public DataTemplateSelector HeaderContentTemplateSelector
{
get { return (DataTemplateSelector) GetValue(HeaderContentTemplateSelectorProperty); }
set { SetValue(HeaderContentTemplateSelectorProperty, value); }
}
}
}

View File

@@ -0,0 +1,83 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Dragablz
{
/// <summary>
/// Helper class to create view models, particularly for tool/MDI windows.
/// </summary>
public class HeaderedItemViewModel : INotifyPropertyChanged
{
private bool _isSelected;
private object _header;
private object _content;
public HeaderedItemViewModel()
{
}
public HeaderedItemViewModel(object header, object content, bool isSelected = false)
{
_header = header;
_content = content;
_isSelected = isSelected;
}
public object Header
{
get { return _header; }
set
{
if (_header == value) return;
_header = value;
#if NET40
OnPropertyChanged("Header");
#else
OnPropertyChanged();
#endif
}
}
public object Content
{
get { return _content; }
set
{
if (_content == value) return;
_content = value;
#if NET40
OnPropertyChanged("Content");
#else
OnPropertyChanged();
#endif
}
}
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected == value) return;
_isSelected = value;
#if NET40
OnPropertyChanged("IsSelected");
#else
OnPropertyChanged();
#endif
}
}
public event PropertyChangedEventHandler PropertyChanged;
#if NET40
protected virtual void OnPropertyChanged(string propertyName)
#else
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
#endif
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
namespace Dragablz
{
public class HorizontalOrganiser : StackOrganiser
{
public HorizontalOrganiser() : base(Orientation.Horizontal)
{ }
public HorizontalOrganiser(double itemOffset) : base(Orientation.Horizontal, itemOffset)
{ }
}
}

View File

@@ -0,0 +1,11 @@
using System.Windows.Controls;
namespace Dragablz
{
public class HorizontalPositionMonitor : StackPositionMonitor
{
public HorizontalPositionMonitor() : base(Orientation.Horizontal)
{
}
}
}

View File

@@ -0,0 +1,19 @@
using System.Windows;
namespace Dragablz
{
/// <summary>
/// Implementors should provide a mechanism to provide a new host control which contains a new <see cref="TabablzControl"/>.
/// </summary>
public interface IInterLayoutClient
{
/// <summary>
/// Provide a new host control and tab into which will be placed into a newly created layout branch.
/// </summary>
/// <param name="partition">Provides the partition where the drag operation was initiated.</param>
/// <param name="source">The source control where a dragging operation was initiated.</param>
/// <returns></returns>
INewTabHost<UIElement> GetNewHost(object partition, TabablzControl source);
}
}

View File

@@ -0,0 +1,27 @@
using System.Threading;
using System.Windows;
namespace Dragablz
{
/// <summary>
/// Implementors should provide mechanisms for providing new windows and closing old windows.
/// </summary>
public interface IInterTabClient
{
/// <summary>
/// Provide a new host window so a tab can be teared from an existing window into a new window.
/// </summary>
/// <param name="interTabClient"></param>
/// <param name="partition">Provides the partition where the drag operation was initiated.</param>
/// <param name="source">The source control where a dragging operation was initiated.</param>
/// <returns></returns>
INewTabHost<Window> GetNewHost(IInterTabClient interTabClient, object partition, TabablzControl source);
/// <summary>
/// Called when a tab has been emptied, and thus typically a window needs closing.
/// </summary>
/// <param name="tabControl"></param>
/// <param name="window"></param>
/// <returns></returns>
TabEmptiedResponse TabEmptiedHandler(TabablzControl tabControl, Window window);
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
namespace Dragablz
{
public interface IItemsOrganiser
{
void Organise(DragablzItemsControl requestor, Size measureBounds, IEnumerable<DragablzItem> items);
void Organise(DragablzItemsControl requestor, Size measureBounds, IOrderedEnumerable<DragablzItem> items);
void OrganiseOnMouseDownWithin(DragablzItemsControl requestor, Size measureBounds, List<DragablzItem> siblingItems, DragablzItem dragablzItem);
void OrganiseOnDragStarted(DragablzItemsControl requestor, Size measureBounds, IEnumerable<DragablzItem> siblingItems, DragablzItem dragItem);
void OrganiseOnDrag(DragablzItemsControl requestor, Size measureBounds, IEnumerable<DragablzItem> siblingItems, DragablzItem dragItem);
void OrganiseOnDragCompleted(DragablzItemsControl requestor, Size measureBounds, IEnumerable<DragablzItem> siblingItems, DragablzItem dragItem);
Point ConstrainLocation(DragablzItemsControl requestor, Size measureBounds, Point itemCurrentLocation, Size itemCurrentSize, Point itemDesiredLocation, Size itemDesiredSize);
Size Measure(DragablzItemsControl requestor, Size availableSize, IEnumerable<DragablzItem> items);
IEnumerable<DragablzItem> Sort(IEnumerable<DragablzItem> items);
}
}

View File

@@ -0,0 +1,8 @@
namespace Dragablz
{
public interface IManualInterTabClient : IInterTabClient
{
void Add(object item);
void Remove(object item);
}
}

View File

@@ -0,0 +1,10 @@
using System.Windows;
namespace Dragablz
{
public interface INewTabHost<out TElement> where TElement : UIElement
{
TElement Container { get; }
TabablzControl TabablzControl { get; }
}
}

View File

@@ -0,0 +1,73 @@
using System.Dynamic;
using System.Windows;
namespace Dragablz
{
public class InterTabController : FrameworkElement
{
public InterTabController()
{
HorizontalPopoutGrace = 8;
VerticalPopoutGrace = 8;
MoveWindowWithSolitaryTabs = true;
}
public static readonly DependencyProperty HorizontalPopoutGraceProperty = DependencyProperty.Register(
"HorizontalPopoutGrace", typeof (double), typeof (InterTabController), new PropertyMetadata(8.0));
public double HorizontalPopoutGrace
{
get { return (double) GetValue(HorizontalPopoutGraceProperty); }
set { SetValue(HorizontalPopoutGraceProperty, value); }
}
public static readonly DependencyProperty VerticalPopoutGraceProperty = DependencyProperty.Register(
"VerticalPopoutGrace", typeof (double), typeof (InterTabController), new PropertyMetadata(8.0));
public double VerticalPopoutGrace
{
get { return (double) GetValue(VerticalPopoutGraceProperty); }
set { SetValue(VerticalPopoutGraceProperty, value); }
}
public static readonly DependencyProperty MoveWindowWithSolitaryTabsProperty = DependencyProperty.Register(
"MoveWindowWithSolitaryTabs", typeof (bool), typeof (InterTabController), new PropertyMetadata(true));
public bool MoveWindowWithSolitaryTabs
{
get { return (bool) GetValue(MoveWindowWithSolitaryTabsProperty); }
set { SetValue(MoveWindowWithSolitaryTabsProperty, value); }
}
public static readonly DependencyProperty InterTabClientProperty = DependencyProperty.Register(
"InterTabClient", typeof (IInterTabClient), typeof (InterTabController),
new PropertyMetadata(new DefaultInterTabClient()));
public IInterTabClient InterTabClient
{
get { return (IInterTabClient) GetValue(InterTabClientProperty); }
set { SetValue(InterTabClientProperty, value); }
}
/*
public static readonly DependencyProperty PartitionProperty = DependencyProperty.Register(
"Partition", typeof (object), typeof (InterTabController), new PropertyMetadata(default(object)));
/// <summary>
/// The partition allows on or more tab environments in a single application. Only tabs which have a tab controller
/// with a common partition will be allowed to have tabs dragged between them. <c>null</c> is a valid partition (i.e global).
/// </summary>
public object Partition
{
get { return (object) GetValue(PartitionProperty); }
set { SetValue(PartitionProperty, value); }
}
*/
/// <summary>
/// The partition allows on or more tab environments in a single application. Only tabs which have a tab controller
/// with a common partition will be allowed to have tabs dragged between them. <c>null</c> is a valid partition (i.e global).
/// </summary>
public string Partition { get; set; }
}
}

View File

@@ -0,0 +1,47 @@
using System;
using System.Windows;
namespace Dragablz
{
public delegate void ItemActionCallback(ItemActionCallbackArgs<TabablzControl> args);
public class ItemActionCallbackArgs<TOwner> where TOwner : FrameworkElement
{
private readonly Window _window;
private readonly TOwner _owner;
private readonly DragablzItem _dragablzItem;
public ItemActionCallbackArgs(Window window, TOwner owner, DragablzItem dragablzItem)
{
if (window == null) throw new ArgumentNullException("window");
if (owner == null) throw new ArgumentNullException("owner");
if (dragablzItem == null) throw new ArgumentNullException("dragablzItem");
_window = window;
_owner = owner;
_dragablzItem = dragablzItem;
}
public Window Window
{
get { return _window; }
}
public TOwner Owner
{
get { return _owner; }
}
public DragablzItem DragablzItem
{
get { return _dragablzItem; }
}
public bool IsCancelled { get; private set; }
public void Cancel()
{
IsCancelled = true;
}
}
}

View File

@@ -0,0 +1,29 @@
using System;
using System.Windows;
namespace Dragablz
{
public class LocationChangedEventArgs : EventArgs
{
private readonly object _item;
private readonly Point _location;
public LocationChangedEventArgs(object item, Point location)
{
if (item == null) throw new ArgumentNullException("item");
_item = item;
_location = location;
}
public object Item
{
get { return _item; }
}
public Point Location
{
get { return _location; }
}
}
}

View File

@@ -0,0 +1,26 @@
namespace Dragablz
{
/// <summary>
/// Specifies where an item should appear when added to tab control, as the headers order do not
/// specifically correlate to the order of the the source items.
/// </summary>
public enum AddLocationHint
{
/// <summary>
/// Display item in the first position.
/// </summary>
First,
/// <summary>
/// Display item in the first position.
/// </summary>
Last,
/// <summary>
/// Display an item prior to the selected, or specified item.
/// </summary>
Prior,
/// <summary>
/// Display an item after the selected, or specified item.
/// </summary>
After
}
}

View File

@@ -0,0 +1,31 @@
namespace Dragablz
{
public class MoveItemRequest
{
private readonly object _item;
private readonly object _context;
private readonly AddLocationHint _addLocationHint;
public MoveItemRequest(object item, object context, AddLocationHint addLocationHint)
{
_item = item;
_context = context;
_addLocationHint = addLocationHint;
}
public object Item
{
get { return _item; }
}
public object Context
{
get { return _context; }
}
public AddLocationHint AddLocationHint
{
get { return _addLocationHint; }
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Windows;
namespace Dragablz
{
public class NewTabHost<TElement> : INewTabHost<TElement> where TElement : UIElement
{
private readonly TElement _container;
private readonly TabablzControl _tabablzControl;
public NewTabHost(TElement container, TabablzControl tabablzControl)
{
if (container == null) throw new ArgumentNullException("container");
if (tabablzControl == null) throw new ArgumentNullException("tabablzControl");
_container = container;
_tabablzControl = tabablzControl;
}
public TElement Container
{
get { return _container; }
}
public TabablzControl TabablzControl
{
get { return _tabablzControl; }
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
namespace Dragablz
{
public class OrderChangedEventArgs : EventArgs
{
private readonly object[] _previousOrder;
private readonly object[] _newOrder;
public OrderChangedEventArgs(object[] previousOrder, object[] newOrder)
{
if (newOrder == null) throw new ArgumentNullException("newOrder");
_previousOrder = previousOrder;
_newOrder = newOrder;
}
public object[] PreviousOrder
{
get { return _previousOrder; }
}
public object[] NewOrder
{
get { return _newOrder; }
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Text;
using System.Threading.Tasks;
namespace Dragablz
{
/// <summary>
/// Consumers can provide a position monitor to receive updates regarding the location of an item.
/// </summary>
/// <remarks>
/// A <see cref="PositionMonitor"/> can be used to listen to changes
/// instead of routed events, which can be easier in a MVVM scenario.
/// </remarks>
public class PositionMonitor
{
/// <summary>
/// Raised when the X,Y coordinate of a <see cref="DragablzItem"/> changes.
/// </summary>
public event EventHandler<LocationChangedEventArgs> LocationChanged;
internal virtual void OnLocationChanged(LocationChangedEventArgs e)
{
if (e == null) throw new ArgumentNullException("e");
var handler = LocationChanged;
handler?.Invoke(this, e);
}
internal virtual void ItemsChanged() { }
}
}

View File

@@ -0,0 +1,64 @@
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Markup;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Dragablz")]
[assembly: AssemblyDescription("Dragable tabable")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Mulholland Software Ltd/James Willock")]
[assembly: AssemblyProduct("Dragablz")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: XmlnsPrefix("http://dragablz.net/winfx/xaml/dragablz", "dragablz")]
[assembly: XmlnsDefinition("http://dragablz.net/winfx/xaml/dragablz",
"Dragablz")]
[assembly: XmlnsPrefix("http://dragablz.net/winfx/xaml/dockablz", "dockablz")]
[assembly: XmlnsDefinition("http://dragablz.net/winfx/xaml/dockablz",
"Dragablz.Dockablz")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
//In order to begin building localizable applications, set
//<UICulture>CultureYouAreCodingWith</UICulture> in your .csproj file
//inside a <PropertyGroup>. For example, if you are using US english
//in your source files, set the <UICulture> to en-US. Then uncomment
//the NeutralResourceLanguage attribute below. Update the "en-US" in
//the line below to match the UICulture setting in the project file.
//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.0.4.0")]
[assembly: AssemblyFileVersion("0.0.4.0")]
[assembly: InternalsVisibleTo("Dragablz.Test")]

View File

@@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34014
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Dragablz.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Dragablz.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.34014
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Dragablz.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@@ -0,0 +1,31 @@
using System;
using System.Threading;
namespace Dragablz.Referenceless
{
internal sealed class AnonymousDisposable : ICancelable, IDisposable
{
private volatile Action _dispose;
public bool IsDisposed
{
get
{
return this._dispose == null;
}
}
public AnonymousDisposable(Action dispose)
{
this._dispose = dispose;
}
public void Dispose()
{
var action = Interlocked.Exchange<Action>(ref _dispose, (Action)null);
if (action == null)
return;
action();
}
}
}

View File

@@ -0,0 +1,21 @@
using System;
namespace Dragablz.Referenceless
{
internal sealed class DefaultDisposable : IDisposable
{
public static readonly DefaultDisposable Instance = new DefaultDisposable();
static DefaultDisposable()
{
}
private DefaultDisposable()
{
}
public void Dispose()
{
}
}
}

View File

@@ -0,0 +1,23 @@
using System;
namespace Dragablz.Referenceless
{
internal static class Disposable
{
public static IDisposable Empty
{
get
{
return (IDisposable)DefaultDisposable.Instance;
}
}
public static IDisposable Create(Action dispose)
{
if (dispose == null)
throw new ArgumentNullException("dispose");
else
return (IDisposable)new AnonymousDisposable(dispose);
}
}
}

View File

@@ -0,0 +1,9 @@
using System;
namespace Dragablz.Referenceless
{
internal interface ICancelable : IDisposable
{
bool IsDisposed { get; }
}
}

View File

@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Dragablz.Referenceless
{
internal sealed class SerialDisposable : ICancelable, IDisposable
{
private readonly object _gate = new object();
private IDisposable _current;
private bool _disposed;
/// <summary>
/// Gets a value that indicates whether the object is disposed.
///
/// </summary>
public bool IsDisposed
{
get
{
lock (this._gate)
return this._disposed;
}
}
/// <summary>
/// Gets or sets the underlying disposable.
///
/// </summary>
///
/// <remarks>
/// If the SerialDisposable has already been disposed, assignment to this property causes immediate disposal of the given disposable object. Assigning this property disposes the previous disposable object.
/// </remarks>
public IDisposable Disposable
{
get
{
return this._current;
}
set
{
bool flag = false;
IDisposable disposable = (IDisposable)null;
lock (this._gate)
{
flag = this._disposed;
if (!flag)
{
disposable = this._current;
this._current = value;
}
}
if (disposable != null)
disposable.Dispose();
if (!flag || value == null)
return;
value.Dispose();
}
}
/// <summary>
/// Disposes the underlying disposable as well as all future replacements.
///
/// </summary>
public void Dispose()
{
IDisposable disposable = (IDisposable)null;
lock (this._gate)
{
if (!this._disposed)
{
this._disposed = true;
disposable = this._current;
this._current = (IDisposable)null;
}
}
if (disposable == null)
return;
disposable.Dispose();
}
}
}

View File

@@ -0,0 +1,309 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;
using Dragablz.Core;
namespace Dragablz
{
public abstract class StackOrganiser : IItemsOrganiser
{
private readonly Orientation _orientation;
private readonly double _itemOffset;
private readonly Func<DragablzItem, double> _getDesiredSize;
private readonly Func<DragablzItem, double> _getLocation;
private readonly DependencyProperty _canvasDependencyProperty;
private readonly Action<DragablzItem, double> _setLocation;
private readonly Dictionary<DragablzItem, double> _activeStoryboardTargetLocations =
new Dictionary<DragablzItem, double>();
protected StackOrganiser(Orientation orientation, double itemOffset = 0)
{
_orientation = orientation;
_itemOffset = itemOffset;
switch (orientation)
{
case Orientation.Horizontal:
_getDesiredSize = item => item.DesiredSize.Width;
_getLocation = item => item.X;
_setLocation = (item, coord) => item.SetCurrentValue(DragablzItem.XProperty, coord);
_canvasDependencyProperty = Canvas.LeftProperty;
break;
case Orientation.Vertical:
_getDesiredSize = item => item.DesiredSize.Height;
_getLocation = item => item.Y;
_setLocation = (item, coord) => item.SetCurrentValue(DragablzItem.YProperty, coord);
_canvasDependencyProperty = Canvas.TopProperty;
break;
default:
throw new ArgumentOutOfRangeException("orientation");
}
}
#region LocationInfo
private class LocationInfo
{
private readonly DragablzItem _item;
private readonly double _start;
private readonly double _mid;
private readonly double _end;
public LocationInfo(DragablzItem item, double start, double mid, double end)
{
_item = item;
_start = start;
_mid = mid;
_end = end;
}
public double Start
{
get { return _start; }
}
public double Mid
{
get { return _mid; }
}
public double End
{
get { return _end; }
}
public DragablzItem Item
{
get { return _item; }
}
}
#endregion
public virtual Orientation Orientation
{
get { return _orientation; }
}
public virtual void Organise(DragablzItemsControl requestor, Size measureBounds, IEnumerable<DragablzItem> items)
{
if (items == null) throw new ArgumentNullException("items");
OrganiseInternal(
requestor,
measureBounds,
items.Select((di, idx) => new Tuple<int, DragablzItem>(idx, di))
.OrderBy(tuple => tuple,
MultiComparer<Tuple<int, DragablzItem>>.Ascending(tuple => _getLocation(tuple.Item2))
.ThenAscending(tuple => tuple.Item1))
.Select(tuple => tuple.Item2));
}
public virtual void Organise(DragablzItemsControl requestor, Size measureBounds, IOrderedEnumerable<DragablzItem> items)
{
if (items == null) throw new ArgumentNullException("items");
OrganiseInternal(
requestor,
measureBounds,
items);
}
private void OrganiseInternal(DragablzItemsControl requestor, Size measureBounds,
IEnumerable<DragablzItem> items)
{
var currentCoord = 0.0;
var z = int.MaxValue;
var logicalIndex = 0;
foreach (var newItem in items)
{
Panel.SetZIndex(newItem, newItem.IsSelected ? int.MaxValue : --z);
SetLocation(newItem, currentCoord);
newItem.LogicalIndex = logicalIndex++;
newItem.Measure(measureBounds);
var desiredSize = _getDesiredSize(newItem);
if (desiredSize == 0.0) desiredSize = 1.0; //no measure? create something to help sorting
currentCoord += desiredSize + _itemOffset;
}
}
public virtual void OrganiseOnMouseDownWithin(DragablzItemsControl requestor, Size measureBounds,
List<DragablzItem> siblingItems, DragablzItem dragablzItem)
{
}
private IDictionary<DragablzItem, LocationInfo> _siblingItemLocationOnDragStart;
public virtual void OrganiseOnDragStarted(DragablzItemsControl requestor, Size measureBounds,
IEnumerable<DragablzItem> siblingItems, DragablzItem dragItem)
{
if (siblingItems == null) throw new ArgumentNullException("siblingItems");
if (dragItem == null) throw new ArgumentNullException("dragItem");
_siblingItemLocationOnDragStart = siblingItems.Select(GetLocationInfo).ToDictionary(loc => loc.Item);
}
public virtual void OrganiseOnDrag(DragablzItemsControl requestor, Size measureBounds,
IEnumerable<DragablzItem> siblingItems, DragablzItem dragItem)
{
if (siblingItems == null) throw new ArgumentNullException("siblingItems");
if (dragItem == null) throw new ArgumentNullException("dragItem");
var currentLocations = siblingItems
.Select(GetLocationInfo)
.Union(new[] {GetLocationInfo(dragItem)})
.OrderBy(loc => loc.Item == dragItem ? loc.Start : _siblingItemLocationOnDragStart[loc.Item].Start);
var currentCoord = 0.0;
var zIndex = int.MaxValue;
foreach (var location in currentLocations)
{
if (!Equals(location.Item, dragItem))
{
SendToLocation(location.Item, currentCoord);
Panel.SetZIndex(location.Item, --zIndex);
}
currentCoord += _getDesiredSize(location.Item) + _itemOffset;
}
Panel.SetZIndex(dragItem, int.MaxValue);
}
public virtual void OrganiseOnDragCompleted(DragablzItemsControl requestor, Size measureBounds,
IEnumerable<DragablzItem> siblingItems, DragablzItem dragItem)
{
if (siblingItems == null) throw new ArgumentNullException("siblingItems");
var currentLocations = siblingItems
.Select(GetLocationInfo)
.Union(new[] {GetLocationInfo(dragItem)})
.OrderBy(loc => loc.Item == dragItem ? loc.Start : _siblingItemLocationOnDragStart[loc.Item].Start);
var currentCoord = 0.0;
var z = int.MaxValue;
var logicalIndex = 0;
foreach (var location in currentLocations)
{
SetLocation(location.Item, currentCoord);
currentCoord += _getDesiredSize(location.Item) + _itemOffset;
Panel.SetZIndex(location.Item, --z);
location.Item.LogicalIndex = logicalIndex++;
}
Panel.SetZIndex(dragItem, int.MaxValue);
}
public virtual Point ConstrainLocation(DragablzItemsControl requestor, Size measureBounds, Point itemCurrentLocation,
Size itemCurrentSize, Point itemDesiredLocation, Size itemDesiredSize)
{
var fixedItems = requestor.FixedItemCount;
var lowerBound = fixedItems == 0
? -1d
: GetLocationInfo(requestor.DragablzItems()
.Take(fixedItems)
.Last()).End + _itemOffset - 1;
return new Point(
_orientation == Orientation.Vertical
? 0
: Math.Min(Math.Max(lowerBound, itemDesiredLocation.X), (measureBounds.Width) + 1),
_orientation == Orientation.Horizontal
? 0
: Math.Min(Math.Max(lowerBound, itemDesiredLocation.Y), (measureBounds.Height) + 1)
);
}
public virtual Size Measure(DragablzItemsControl requestor, Size availableSize, IEnumerable<DragablzItem> items)
{
if (items == null) throw new ArgumentNullException("items");
var size = new Size(double.PositiveInfinity, double.PositiveInfinity);
double width = 0, height = 0;
var isFirst = true;
foreach (var dragablzItem in items)
{
dragablzItem.Measure(size);
if (_orientation == Orientation.Horizontal)
{
width += !dragablzItem.IsLoaded ? dragablzItem.DesiredSize.Width : dragablzItem.ActualWidth;
if (!isFirst)
width += _itemOffset;
height = Math.Max(height,
!dragablzItem.IsLoaded ? dragablzItem.DesiredSize.Height : dragablzItem.ActualHeight);
}
else
{
width = Math.Max(width,
!dragablzItem.IsLoaded ? dragablzItem.DesiredSize.Width : dragablzItem.ActualWidth);
height += !dragablzItem.IsLoaded ? dragablzItem.DesiredSize.Height : dragablzItem.ActualHeight;
if (!isFirst)
height += _itemOffset;
}
isFirst = false;
}
return new Size(Math.Max(width, 0), Math.Max(height, 0));
}
public virtual IEnumerable<DragablzItem> Sort(IEnumerable<DragablzItem> items)
{
if (items == null) throw new ArgumentNullException("items");
return items.OrderBy(i => GetLocationInfo(i).Start);
}
private void SetLocation(DragablzItem dragablzItem, double location)
{
_setLocation(dragablzItem, location);
}
private void SendToLocation(DragablzItem dragablzItem, double location)
{
double activeTarget;
if (Math.Abs(_getLocation(dragablzItem) - location) < 1.0
||
_activeStoryboardTargetLocations.TryGetValue(dragablzItem, out activeTarget)
&& Math.Abs(activeTarget - location) < 1.0)
{
return;
}
_activeStoryboardTargetLocations[dragablzItem] = location;
var storyboard = new Storyboard {FillBehavior = FillBehavior.Stop};
storyboard.WhenComplete(sb =>
{
_setLocation(dragablzItem, location);
sb.Remove(dragablzItem);
_activeStoryboardTargetLocations.Remove(dragablzItem);
});
var timeline = new DoubleAnimationUsingKeyFrames();
timeline.SetValue(Storyboard.TargetPropertyProperty, new PropertyPath(_canvasDependencyProperty));
timeline.KeyFrames.Add(
new EasingDoubleKeyFrame(location, KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(200)))
{
EasingFunction = new CubicEase() { EasingMode = EasingMode.EaseOut }
});
storyboard.Children.Add(timeline);
storyboard.Begin(dragablzItem, true);
}
private LocationInfo GetLocationInfo(DragablzItem item)
{
var size = _getDesiredSize(item);
double startLocation;
if (!_activeStoryboardTargetLocations.TryGetValue(item, out startLocation))
startLocation = _getLocation(item);
var midLocation = startLocation + size / 2;
var endLocation = startLocation + size;
return new LocationInfo(item, startLocation, midLocation, endLocation);
}
}
}

View File

@@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Controls;
namespace Dragablz
{
/// <summary>
/// A linear position monitor simplifies the montoring of the order of items, where they are laid out
/// horizontally or vertically (typically via a <see cref="StackOrganiser"/>.
/// </summary>
public abstract class StackPositionMonitor : PositionMonitor
{
private readonly Func<DragablzItem, double> _getLocation;
protected StackPositionMonitor(Orientation orientation)
{
switch (orientation)
{
case Orientation.Horizontal:
_getLocation = item => item.X;
break;
case Orientation.Vertical:
_getLocation = item => item.Y;
break;
default:
throw new ArgumentOutOfRangeException("orientation");
}
}
public event EventHandler<OrderChangedEventArgs> OrderChanged;
internal virtual void OnOrderChanged(OrderChangedEventArgs e)
{
var handler = OrderChanged;
if (handler != null) handler(this, e);
}
internal IEnumerable<DragablzItem> Sort(IEnumerable<DragablzItem> items)
{
if (items == null) throw new ArgumentNullException("items");
return items.OrderBy(i => _getLocation(i));
}
}
}

View File

@@ -0,0 +1,41 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows.Media.Animation;
namespace Dragablz
{
internal class StoryboardCompletionListener
{
private readonly Storyboard _storyboard;
private readonly Action<Storyboard> _continuation;
public StoryboardCompletionListener(Storyboard storyboard, Action<Storyboard> continuation)
{
if (storyboard == null) throw new ArgumentNullException("storyboard");
if (continuation == null) throw new ArgumentNullException("continuation");
_storyboard = storyboard;
_continuation = continuation;
_storyboard.Completed += StoryboardOnCompleted;
}
private void StoryboardOnCompleted(object sender, EventArgs eventArgs)
{
_storyboard.Completed -= StoryboardOnCompleted;
_continuation(_storyboard);
}
}
internal static class StoryboardCompletionListenerExtension
{
private static readonly IDictionary<Storyboard, Action<Storyboard>> ContinuationIndex = new Dictionary<Storyboard, Action<Storyboard>>();
public static void WhenComplete(this Storyboard storyboard, Action<Storyboard> continuation)
{
// ReSharper disable once ObjectCreationAsStatement
new StoryboardCompletionListener(storyboard, continuation);
}
}
}

View File

@@ -0,0 +1,14 @@
namespace Dragablz
{
public enum TabEmptiedResponse
{
/// <summary>
/// Allow the Window to be closed automatically.
/// </summary>
CloseWindowOrLayoutBranch,
/// <summary>
/// The window will not be closed by the <see cref="TabablzControl"/>, probably meaning the implementor will close the window manually
/// </summary>
DoNothing
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,50 @@
using System;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
namespace Dragablz
{
/// <summary>
/// Provides a little help for sizing the header panel in the tab control
/// </summary>
public class TabablzHeaderSizeConverter : IMultiValueConverter
{
public Orientation Orientation { get; set; }
/// <summary>
/// The first value should be the total size available size, typically the parent control size.
/// The second value should be from <see cref="DragablzItemsControl.ItemsPresenterWidthProperty"/> or (height equivalent)
/// All additional values should be siblings sizes (width or height) which will affect (reduce) the available size.
/// </summary>
/// <param name="values"></param>
/// <param name="targetType"></param>
/// <param name="parameter"></param>
/// <param name="culture"></param>
/// <returns></returns>
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values == null) throw new ArgumentNullException("values");
if (values.Length < 2) return Binding.DoNothing;
var val = values
.Skip(2)
.OfType<double>()
.Where(d => !double.IsInfinity(d) && !double.IsNaN(d))
.Aggregate(values.OfType<double>().First(), (current, diminish) => current - diminish);
var maxWidth = values.Take(2).OfType<double>().Min();
return Math.Min(Math.Max(val, 0), maxWidth);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,27 @@
using System.Windows;
using System.Windows.Controls;
namespace Dragablz
{
/// <summary>
/// Selects style to apply to a <see cref="DragablzItem"/> according to the tab item content itself.
/// </summary>
public class TabablzItemStyleSelector : StyleSelector
{
private readonly Style _defaultHeaderItemStyle;
private readonly Style _customHeaderItemStyle;
public TabablzItemStyleSelector(Style defaultHeaderItemStyle, Style customHeaderItemStyle)
{
_defaultHeaderItemStyle = defaultHeaderItemStyle;
_customHeaderItemStyle = customHeaderItemStyle;
}
public override Style SelectStyle(object item, DependencyObject container)
{
if (item is TabItem) return _defaultHeaderItemStyle;
return _customHeaderItemStyle;
}
}
}

View File

@@ -0,0 +1,31 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
namespace Dragablz.Themes
{
public class BrushToRadialGradientBrushConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var solidColorBrush = value as SolidColorBrush;
if (solidColorBrush == null) return Binding.DoNothing;
return new RadialGradientBrush(solidColorBrush.Color, Colors.Transparent)
{
Center = new Point(.5, .5),
GradientOrigin = new Point(.5, .5),
RadiusX = .5,
RadiusY = .5,
Opacity = .39
};
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
}

View File

@@ -0,0 +1,8 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dockablz="clr-namespace:Dragablz.Dockablz"
xmlns:converters="clr-namespace:Dragablz.Converters">
</ResourceDictionary>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,443 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dragablz="clr-namespace:Dragablz"
xmlns:dockablz="clr-namespace:Dragablz.Dockablz"
xmlns:converters="clr-namespace:Dragablz.Converters">
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<converters:EqualityToVisibilityConverter x:Key="EqualityToVisibilityConverter" />
<converters:BooleanAndToVisibilityConverter x:Key="BooleanAndToVisibilityConverter" />
<converters:EqualityToBooleanConverter x:Key="EqualityToBooleanConverter" />
<converters:ShowDefaultCloseButtonConverter x:Key="ShowDefaultCloseButtonConverter" />
<Style x:Key="MahAppsFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Thumb}" x:Key="MahAppsInvisibleThumbStyle">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Background="{TemplateBinding Background}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Button}" x:Key="MahAppsMenuCommandButtonStyle">
<Setter Property="FocusVisualStyle" Value="{StaticResource MahAppsFocusVisual}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Opacity" Value=".8"/>
<Setter Property="Width" Value="24"/>
<Setter Property="Height" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value=".5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{x:Static dragablz:DragablzColors.WindowGlassBrush}"/>
<Setter Property="Foreground" Value="{x:Static SystemColors.HighlightTextBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Opacity" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}" x:Key="MahAppsCloseItemCommandButtonStyle" BasedOn="{StaticResource MahAppsMenuCommandButtonStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Fill="{TemplateBinding Background}"/>
<Path x:Name="Path"
Data="M0.5,71 L70,0.5 M0.5,1 L70,70.5"
Width="6" Height="6"
Stretch="Uniform"
Stroke="{TemplateBinding Foreground}"
StrokeThickness="1.5"
StrokeStartLineCap="Square"
StrokeEndLineCap="Square"
SnapsToDevicePixels="True" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="{x:Static SystemColors.ActiveBorderColor}" />
</Setter.Value>
</Setter>
<Setter Property="Width" Value="12" />
<Setter Property="Height" Value="12" />
<Setter Property="Margin" Value="4 0 2 0" />
<Setter Property="Opacity" Value=".75" />
<Setter Property="Foreground" Value="{x:Static SystemColors.HighlightTextBrush}" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Opacity" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}" x:Key="AddItemCommandButtonStyle" BasedOn="{StaticResource MahAppsMenuCommandButtonStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Fill="{TemplateBinding Background}"/>
<Path x:Name="Path"
Data="M38,6L38.0003451911513,70.6666666666666 M70.3336667356886,38L5.50002465137562,38"
Width="6" Height="6"
Stretch="Uniform"
Stroke="{TemplateBinding Foreground}"
StrokeThickness="1.5"
StrokeStartLineCap="Square"
StrokeEndLineCap="Square"
SnapsToDevicePixels="True" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background">
<Setter.Value>
<SolidColorBrush Color="{x:Static SystemColors.ActiveBorderColor}" />
</Setter.Value>
</Setter>
<Setter Property="Width" Value="12" />
<Setter Property="Height" Value="12" />
<Setter Property="Margin" Value="4 0 2 0" />
<Setter Property="Foreground" Value="{x:Static SystemColors.HighlightTextBrush}" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{x:Static dragablz:DragablzColors.WindowGlassBrush}"/>
<Setter Property="Foreground" Value="{x:Static SystemColors.HighlightTextBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Opacity" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="MahAppsTrapezoidDragableTabItemStyle" TargetType="{x:Type dragablz:DragablzItem}">
<Setter Property="FocusVisualStyle" Value="{StaticResource MahAppsFocusVisual}"/>
<Setter Property="Foreground" Value="{DynamicResource BlackBrush}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Margin" Value="0 0 0 0"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dragablz:DragablzItem}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true" Margin="0 1 0 -1">
<dragablz:Trapezoid x:Name="Trapezoid"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
LongBasePenBrush="{TemplateBinding Background}"
PenThickness="0"
Margin="0 0 0 0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
Margin="{TemplateBinding Control.Padding }"
x:Name="contentPresenter" />
<Thumb Grid.Column="0" HorizontalAlignment="Stretch" VerticalContentAlignment="Stretch"
x:Name="PART_Thumb"
Style="{StaticResource MahAppsInvisibleThumbStyle}" />
<Button Grid.Column="1"
Style="{StaticResource MahAppsCloseItemCommandButtonStyle}"
Command="{x:Static dragablz:TabablzControl.CloseItemCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<Button.Visibility>
<MultiBinding Converter="{StaticResource ShowDefaultCloseButtonConverter}">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type dragablz:TabablzControl}}" Path="ShowDefaultCloseButton" />
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type dragablz:TabablzControl}}" Path="FixedHeaderCount" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="LogicalIndex" />
</MultiBinding>
</Button.Visibility>
</Button>
</Grid>
</dragablz:Trapezoid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="dragablz:TabablzControl.IsWrappingTabItem" Value="True">
<Setter TargetName="contentPresenter" Property="Content" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.Header}" />
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource AccentColorBrush3}" TargetName="Trapezoid"/>
<Setter Property="LongBasePenBrush" Value="{DynamicResource AccentColorBrush3}" TargetName="Trapezoid"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{DynamicResource AccentColorBrush}" TargetName="Trapezoid"/>
<Setter Property="LongBasePenBrush" Value="{DynamicResource AccentColorBrush}" TargetName="Trapezoid"/>
<Setter Property="Foreground" Value="{DynamicResource WhiteBrush}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MahAppsMetroBaseWindowButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="{DynamicResource TransparentWhiteBrush}" />
<Setter Property="Foreground" Value="{DynamicResource WhiteColorBrush}" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Padding" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter x:Name="contentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
RecognizesAccessKey="True"
Opacity="0.75" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="contentPresenter" Property="Opacity" Value="1" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter TargetName="contentPresenter" Property="Opacity" Value=".5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource SemiTransparentWhiteBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{DynamicResource HighlightBrush}" />
<Setter Property="Foreground" Value="White" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#ADADAD" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="MahAppsToolWindowButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource MahAppsMetroBaseWindowButtonStyle}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Width" Value="30" />
<Setter Property="MaxHeight" Value="30" />
<Setter Property="Padding" Value="0" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type dragablz:HeaderedDragablzItem}" x:Key="MahAppsToolDragablzItemStyle">
<Style.Setters>
<Setter Property="BorderBrush" Value="{DynamicResource AccentColorBrush2}" />
<Setter Property="BorderThickness" Value="4" />
<Setter Property="Canvas.Left" Value="{Binding X, RelativeSource={RelativeSource Self}}" />
<Setter Property="Canvas.Top" Value="{Binding Y, RelativeSource={RelativeSource Self}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dragablz:HeaderedDragablzItem}">
<Grid Margin="{TemplateBinding Margin}">
<Border Background="{DynamicResource WhiteColorBrush}">
<Border.Effect>
<DropShadowEffect BlurRadius="10" ShadowDepth="5" Direction="315" Color="{DynamicResource AccentColor2}" Opacity=".5" />
</Border.Effect>
</Border>
<Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="{TemplateBinding Control.Padding}" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" SnapsToDevicePixels="True">
<DockPanel>
<Grid DockPanel.Dock="Top" Background="{TemplateBinding BorderBrush}">
<Thumb HorizontalAlignment="Stretch" VerticalContentAlignment="Stretch" x:Name="PART_Thumb"
Style="{StaticResource MahAppsInvisibleThumbStyle}"
/>
<DockPanel Margin="{TemplateBinding Control.Padding }">
<Button DockPanel.Dock="Right"
Command="{x:Static dockablz:Layout.CloseFloatingItem}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Margin="1 0 0 0"
Style="{DynamicResource MahAppsToolWindowButtonStyle}"
Focusable="False"
RenderOptions.EdgeMode="Aliased"
>
<Path Data="M 10.009,1.704 L 8.331,0.026 5.03,3.327 1.703,0 0,1.704 3.326,5.031 0.025,8.332 1.703,10.009 5.004,6.708 8.305,10.009 10.009,8.305 6.708,5.005"
SnapsToDevicePixels="True"
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}" />
</Button>
<Button DockPanel.Dock="Right"
Command="{x:Static dockablz:Layout.UnfloatItemCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Margin="1 0 0 0"
Style="{DynamicResource MahAppsToolWindowButtonStyle}"
>
<Path Width="12" Height="12" Stretch="UniformToFill" Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}" Data="M 19 19 H 5 V 5 h 7 V 3 H 5 C 3.89 3 3 3.9 3 5 v 14 c 0 1.1 0.89 2 2 2 h 14 c 1.1 0 2 -0.9 2 -2 v -7 h -2 v 7 z M 14 3 v 2 h 3.59 L 7.76 14.83 9.17 16.24 19 6.41 V 10 h 2 V 3 h -7 z" />
</Button>
<Button DockPanel.Dock="Right"
Command="{x:Static dockablz:Layout.MaximiseFloatingItem}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Margin="1 0 0 0"
Style="{DynamicResource MahAppsToolWindowButtonStyle}"
>
<Path Width="10"
Height="10"
Data="F1M0,0L0,9 9,9 9,0 0,0 0,3 8,3 8,8 1,8 1,3z"
SnapsToDevicePixels="True"
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}" />
</Button>
<Button DockPanel.Dock="Right"
Command="{x:Static dockablz:Layout.RestoreFloatingItem}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Margin="1 0 0 0"
Style="{DynamicResource MahAppsToolWindowButtonStyle}"
>
<Path Width="10"
Height="10"
Data="F1M0,10L0,3 3,3 3,0 10,0 10,2 4,2 4,3 7,3 7,6 6,6 6,5 1,5 1,10z M1,10L7,10 7,7 10,7 10,2 9,2 9,6 6,6 6,9 1,9z"
SnapsToDevicePixels="True"
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}" />
</Button>
<ContentPresenter Content="{TemplateBinding HeaderContent}"
ContentTemplate="{TemplateBinding HeaderContentTemplate}"
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
ContentTemplateSelector="{TemplateBinding HeaderContentTemplateSelector}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
IsHitTestVisible="False"
/>
</DockPanel>
</Grid>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
Margin="{TemplateBinding Control.Padding }"/>
</DockPanel>
</Border>
<Thumb Style="{StaticResource MahAppsInvisibleThumbStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Left}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="Left"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
Cursor="SizeWE"
/>
<Thumb Style="{StaticResource MahAppsInvisibleThumbStyle}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Top}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="Top"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Cursor="SizeNS"
/>
<Thumb Style="{StaticResource MahAppsInvisibleThumbStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Right}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="Right"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Cursor="SizeWE"
/>
<Thumb Style="{StaticResource MahAppsInvisibleThumbStyle}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Bottom}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="Bottom"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Cursor="SizeNS"
/>
<Thumb Style="{StaticResource MahAppsInvisibleThumbStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Right}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Bottom}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="TopLeft"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Cursor="SizeNWSE"
/>
<Thumb Style="{StaticResource MahAppsInvisibleThumbStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Right}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Bottom}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="TopRight"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Cursor="SizeNESW"
/>
<Thumb Style="{StaticResource MahAppsInvisibleThumbStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Right}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Bottom}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="BottomRight"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Cursor="SizeNWSE"
/>
<Thumb Style="{StaticResource MahAppsInvisibleThumbStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Right}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Bottom}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="BottomLeft"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Cursor="SizeNESW"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<Trigger Property="dockablz:Layout.FloatingItemState" Value="Maximized">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Canvas.Left" Value="0" />
<Setter Property="Canvas.Top" Value="0" />
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type dockablz:Layout}}, Path=ActualWidth}" />
<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type dockablz:Layout}}, Path=ActualHeight}" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type dragablz:TabablzControl}" x:Key="MahAppsTabablzControlStyle">
<Setter Property="Background" Value="{DynamicResource WhiteColorBrush}"/>
<Setter Property="BorderBrush" Value="{DynamicResource AccentColorBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource BlackColorBrush}"/>
<Setter Property="AdjacentHeaderItemOffset" Value="-12" />
<Setter Property="ItemContainerStyle" Value="{StaticResource MahAppsTrapezoidDragableTabItemStyle}" />
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,803 @@
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dragablz="clr-namespace:Dragablz"
xmlns:dockablz="clr-namespace:Dragablz.Dockablz"
xmlns:converters="clr-namespace:Dragablz.Converters"
xmlns:local="clr-namespace:Dragablz.Themes">
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
<converters:EqualityToVisibilityConverter x:Key="EqualityToVisibilityConverter" />
<converters:BooleanAndToVisibilityConverter x:Key="BooleanAndToVisibilityConverter" />
<converters:EqualityToBooleanConverter x:Key="EqualityToBooleanConverter" />
<converters:ShowDefaultCloseButtonConverter x:Key="ShowDefaultCloseButtonConverter" />
<local:BrushToRadialGradientBrushConverter x:Key="BrushToRadialGradientBrushConverter" />
<Style x:Key="MaterialDesignFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Thumb}" x:Key="MaterialDesignInvisibleThumbStyle">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid Background="{TemplateBinding Background}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Button}" x:Key="MaterialDesignMenuCommandButtonStyle">
<Setter Property="FocusVisualStyle" Value="{StaticResource MaterialDesignFocusVisual}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Opacity" Value=".8"/>
<Setter Property="Width" Value="24"/>
<Setter Property="Height" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value=".5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{x:Static dragablz:DragablzColors.WindowGlassBrush}"/>
<Setter Property="Foreground" Value="{x:Static SystemColors.HighlightTextBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Opacity" Value="1"/>
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}" x:Key="MaterialDesignCloseItemCommandButtonStyle" BasedOn="{StaticResource MaterialDesignMenuCommandButtonStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Fill="{TemplateBinding Background}"/>
<Path x:Name="Path"
Data="M0.5,71 L70,0.5 M0.5,1 L70,70.5"
Width="6" Height="6"
Stretch="Uniform"
Stroke="{TemplateBinding Foreground}"
StrokeThickness="1.5"
StrokeStartLineCap="Square"
StrokeEndLineCap="Square"
SnapsToDevicePixels="True" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="{DynamicResource PrimaryHueMidBrush}" />
<Setter Property="Width" Value="12" />
<Setter Property="Height" Value="12" />
<Setter Property="Margin" Value="4 0 2 0" />
<Setter Property="Opacity" Value=".75" />
<Setter Property="Foreground" Value="{DynamicResource PrimaryHueMidForegroundBrush}" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{DynamicResource PrimaryHueLightBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryHueLightForegroundBrush}" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type Button}" x:Key="MaterialDesignAddItemCommandButtonStyle" BasedOn="{StaticResource MaterialDesignMenuCommandButtonStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Ellipse Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Fill="{TemplateBinding Background}"/>
<Path x:Name="Path"
Data="M38,6L38.0003451911513,70.6666666666666 M70.3336667356886,38L5.50002465137562,38"
Width="6" Height="6"
Stretch="Uniform"
Stroke="{TemplateBinding Foreground}"
StrokeThickness="1.5"
StrokeStartLineCap="Square"
StrokeEndLineCap="Square"
SnapsToDevicePixels="True" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="{DynamicResource PrimaryHueMidBrush}" />
<Setter Property="Width" Value="12" />
<Setter Property="Height" Value="12" />
<Setter Property="Margin" Value="4 0 2 0" />
<Setter Property="Foreground" Value="{DynamicResource PrimaryHueMidForegroundBrush}" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" Value="{DynamicResource PrimaryHueLightBrush}"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryHueLightForegroundBrush}" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="MaterialDesignDragableTabItemStyle" TargetType="{x:Type dragablz:DragablzItem}">
<Setter Property="FocusVisualStyle" Value="{StaticResource MaterialDesignFocusVisual}"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryHueMidForegroundBrush}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="local:MaterialDesignAssist.IndicatorBrush" Value="{DynamicResource SecondaryAccentBrush}" />
<Setter Property="Margin" Value="0 0 0 0"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="MinWidth" Value="80" />
<Setter Property="Height" Value="40" />
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="TextBlock.FontWeight" Value="Medium" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dragablz:DragablzItem}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
Margin="0 0 0 0">
<local:Ripple HorizontalContentAlignment="Stretch" VerticalAlignment="Stretch">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
x:Name="contentPresenter"
Margin="{TemplateBinding Control.Padding}"
Opacity=".87"/>
<Thumb Grid.Column="0" HorizontalAlignment="Stretch" VerticalContentAlignment="Stretch"
x:Name="PART_Thumb"
Style="{StaticResource MaterialDesignInvisibleThumbStyle}" />
<Button Grid.Column="1"
Style="{StaticResource MaterialDesignCloseItemCommandButtonStyle}"
Command="{x:Static dragablz:TabablzControl.CloseItemCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<Button.Visibility>
<MultiBinding Converter="{StaticResource ShowDefaultCloseButtonConverter}">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type dragablz:TabablzControl}}" Path="ShowDefaultCloseButton" />
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type dragablz:TabablzControl}}" Path="FixedHeaderCount" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="LogicalIndex" />
</MultiBinding>
</Button.Visibility>
</Button>
</Grid>
</local:Ripple>
</Border>
<Border x:Name="SelectionHighlightBorder" Background="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(local:MaterialDesignAssist.IndicatorBrush)}" Height="2"
Grid.Row="1"
Visibility="Hidden"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="dragablz:TabablzControl.IsWrappingTabItem" Value="True">
<Setter TargetName="contentPresenter" Property="Content" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.Header}" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="contentPresenter" Property="Opacity" Value="1"/>
<Setter TargetName="SelectionHighlightBorder" Property="Visibility" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MaterialDesignDragableTabItemVerticalStyle" TargetType="{x:Type dragablz:DragablzItem}">
<Setter Property="FocusVisualStyle" Value="{StaticResource MaterialDesignFocusVisual}"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryHueMidForegroundBrush}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Margin" Value="0 0 0 0"/>
<Setter Property="Width" Value="80" />
<Setter Property="MinHeight" Value="40" />
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="TextBlock.FontWeight" Value="Medium" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dragablz:DragablzItem}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Border BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
Margin="0 0 0 0">
<local:Ripple HorizontalContentAlignment="Stretch" VerticalAlignment="Stretch">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
x:Name="contentPresenter"
RenderTransformOrigin=".5, .5">
<ContentPresenter.RenderTransform>
<RotateTransform Angle="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(dragablz:DragablzItem.ContentRotateTransformAngle)}" />
</ContentPresenter.RenderTransform>
</ContentPresenter>
<Thumb Grid.Row="0" HorizontalAlignment="Stretch" VerticalContentAlignment="Stretch"
x:Name="PART_Thumb"
Style="{StaticResource MaterialDesignInvisibleThumbStyle}" />
<Button Grid.Row="1"
Style="{StaticResource MaterialDesignCloseItemCommandButtonStyle}"
Command="{x:Static dragablz:TabablzControl.CloseItemCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}">
<Button.Visibility>
<MultiBinding Converter="{StaticResource ShowDefaultCloseButtonConverter}">
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type dragablz:TabablzControl}}" Path="ShowDefaultCloseButton" />
<Binding RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type dragablz:TabablzControl}}" Path="FixedHeaderCount" />
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="LogicalIndex" />
</MultiBinding>
</Button.Visibility>
</Button>
</Grid>
</local:Ripple>
</Border>
<Border x:Name="SelectionHighlightBorder" Background="{DynamicResource SecondaryAccentBrush}" Width="2"
Grid.Column="1"
Visibility="Hidden"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="dragablz:TabablzControl.IsWrappingTabItem" Value="True">
<Setter TargetName="contentPresenter" Property="Content" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DataContext.Header}" />
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="contentPresenter" Property="Opacity" Value="1"/>
<Setter TargetName="SelectionHighlightBorder" Property="Visibility" Value="Visible" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MaterialDesignMetroBaseWindowButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="{DynamicResource TransparentWhiteBrush}" />
<Setter Property="Foreground" Value="{DynamicResource WhiteColorBrush}" />
<Setter Property="HorizontalContentAlignment" Value="Center" />
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Padding" Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter x:Name="contentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
RecognizesAccessKey="True"
Opacity="0.75" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="contentPresenter" Property="Opacity" Value="1" />
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter TargetName="contentPresenter" Property="Opacity" Value=".5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{DynamicResource SemiTransparentWhiteBrush}" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{DynamicResource HighlightBrush}" />
<Setter Property="Foreground" Value="White" />
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#ADADAD" />
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="MaterialDesignToolWindowButtonStyle" TargetType="{x:Type Button}" BasedOn="{StaticResource MaterialDesignMetroBaseWindowButtonStyle}">
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Width" Value="30" />
<Setter Property="MaxHeight" Value="30" />
<Setter Property="Padding" Value="0" />
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type dragablz:HeaderedDragablzItem}" x:Key="MaterialDesignToolDragablzItemStyle">
<Style.Setters>
<Setter Property="BorderBrush" Value="{DynamicResource AccentColorBrush2}" />
<Setter Property="BorderThickness" Value="4" />
<Setter Property="Canvas.Left" Value="{Binding X, RelativeSource={RelativeSource Self}}" />
<Setter Property="Canvas.Top" Value="{Binding Y, RelativeSource={RelativeSource Self}}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dragablz:HeaderedDragablzItem}">
<Grid Margin="{TemplateBinding Margin}">
<Border Background="{DynamicResource WhiteColorBrush}">
<Border.Effect>
<DropShadowEffect BlurRadius="10" ShadowDepth="5" Direction="315" Color="{DynamicResource AccentColor2}" Opacity=".5" />
</Border.Effect>
</Border>
<Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="{TemplateBinding Control.Padding}" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" SnapsToDevicePixels="True">
<DockPanel>
<Grid DockPanel.Dock="Top" Background="{TemplateBinding BorderBrush}">
<Thumb HorizontalAlignment="Stretch" VerticalContentAlignment="Stretch" x:Name="PART_Thumb"
Style="{StaticResource MaterialDesignInvisibleThumbStyle}"
/>
<DockPanel Margin="{TemplateBinding Control.Padding }">
<Button DockPanel.Dock="Right"
Command="{x:Static dockablz:Layout.CloseFloatingItem}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Margin="1 0 0 0"
Style="{DynamicResource MaterialDesignToolWindowButtonStyle}"
Focusable="False"
RenderOptions.EdgeMode="Aliased"
>
<Path Data="M 10.009,1.704 L 8.331,0.026 5.03,3.327 1.703,0 0,1.704 3.326,5.031 0.025,8.332 1.703,10.009 5.004,6.708 8.305,10.009 10.009,8.305 6.708,5.005"
SnapsToDevicePixels="True"
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}" />
</Button>
<Button DockPanel.Dock="Right"
Command="{x:Static dockablz:Layout.UnfloatItemCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Margin="1 0 0 0"
Style="{DynamicResource MaterialDesignToolWindowButtonStyle}"
>
<Path Width="12" Height="12" Stretch="UniformToFill" Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}" Data="M 19 19 H 5 V 5 h 7 V 3 H 5 C 3.89 3 3 3.9 3 5 v 14 c 0 1.1 0.89 2 2 2 h 14 c 1.1 0 2 -0.9 2 -2 v -7 h -2 v 7 z M 14 3 v 2 h 3.59 L 7.76 14.83 9.17 16.24 19 6.41 V 10 h 2 V 3 h -7 z" />
</Button>
<Button DockPanel.Dock="Right"
Command="{x:Static dockablz:Layout.MaximiseFloatingItem}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Margin="1 0 0 0"
Style="{DynamicResource MaterialDesignToolWindowButtonStyle}"
>
<Path Width="10"
Height="10"
Data="F1M0,0L0,9 9,9 9,0 0,0 0,3 8,3 8,8 1,8 1,3z"
SnapsToDevicePixels="True"
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}" />
</Button>
<Button DockPanel.Dock="Right"
Command="{x:Static dockablz:Layout.RestoreFloatingItem}"
CommandParameter="{Binding RelativeSource={RelativeSource TemplatedParent}}"
Margin="1 0 0 0"
Style="{DynamicResource MaterialDesignToolWindowButtonStyle}"
>
<Path Width="10"
Height="10"
Data="F1M0,10L0,3 3,3 3,0 10,0 10,2 4,2 4,3 7,3 7,6 6,6 6,5 1,5 1,10z M1,10L7,10 7,7 10,7 10,2 9,2 9,6 6,6 6,9 1,9z"
SnapsToDevicePixels="True"
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType=Button}}" />
</Button>
<ContentPresenter Content="{TemplateBinding HeaderContent}"
ContentTemplate="{TemplateBinding HeaderContentTemplate}"
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
ContentTemplateSelector="{TemplateBinding HeaderContentTemplateSelector}"
SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
IsHitTestVisible="False"
/>
</DockPanel>
</Grid>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}" HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
Margin="{TemplateBinding Control.Padding }"/>
</DockPanel>
</Border>
<Thumb Style="{StaticResource MaterialDesignInvisibleThumbStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Left}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="Left"
HorizontalAlignment="Left"
VerticalAlignment="Stretch"
Cursor="SizeWE"
/>
<Thumb Style="{StaticResource MaterialDesignInvisibleThumbStyle}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Top}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="Top"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Cursor="SizeNS"
/>
<Thumb Style="{StaticResource MaterialDesignInvisibleThumbStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Right}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="Right"
HorizontalAlignment="Right"
VerticalAlignment="Stretch"
Cursor="SizeWE"
/>
<Thumb Style="{StaticResource MaterialDesignInvisibleThumbStyle}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Bottom}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="Bottom"
HorizontalAlignment="Stretch"
VerticalAlignment="Bottom"
Cursor="SizeNS"
/>
<Thumb Style="{StaticResource MaterialDesignInvisibleThumbStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Right}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Bottom}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="TopLeft"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Cursor="SizeNWSE"
/>
<Thumb Style="{StaticResource MaterialDesignInvisibleThumbStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Right}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Bottom}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="TopRight"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Cursor="SizeNESW"
/>
<Thumb Style="{StaticResource MaterialDesignInvisibleThumbStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Right}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Bottom}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="BottomRight"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
Cursor="SizeNWSE"
/>
<Thumb Style="{StaticResource MaterialDesignInvisibleThumbStyle}"
Width="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Right}"
Height="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=BorderThickness.Bottom}"
IsEnabled="{Binding Path=(dockablz:Layout.FloatingItemState), RelativeSource={RelativeSource TemplatedParent}, Converter={StaticResource EqualityToBooleanConverter}, ConverterParameter={x:Static WindowState.Normal}}"
dragablz:DragablzItem.SizeGrip="BottomLeft"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
Cursor="SizeNESW"
/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<Trigger Property="dockablz:Layout.FloatingItemState" Value="Maximized">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Canvas.Left" Value="0" />
<Setter Property="Canvas.Top" Value="0" />
<Setter Property="Width" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type dockablz:Layout}}, Path=ActualWidth}" />
<Setter Property="Height" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type dockablz:Layout}}, Path=ActualHeight}" />
</Trigger>
</Style.Triggers>
</Style>
<ControlTemplate x:Key="TabablzScrollViewerControlTemplate" TargetType="{x:Type ScrollViewer}">
<Grid x:Name="Grid" Background="{TemplateBinding Background}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
<ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="1" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}"
Margin="0 1 0 0" />
<ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="0" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"
Margin="1 0 0 0" />
</Grid>
</ControlTemplate>
<Style TargetType="{x:Type dragablz:DragablzItemsControl}" x:Key="TabablzDragablzItemsControlStyle">
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="VerticalAlignment" Value="Top" />
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dragablz:DragablzItemsControl}">
<Border BorderThickness="{TemplateBinding Border.BorderThickness}" Padding="{TemplateBinding Control.Padding}" BorderBrush="{TemplateBinding Border.BorderBrush}" Background="{TemplateBinding Panel.Background}" SnapsToDevicePixels="True">
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
Width="{TemplateBinding ActualWidth}"
Height="{TemplateBinding ActualHeight}"
Template="{StaticResource TabablzScrollViewerControlTemplate}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="{TemplateBinding ItemsPresenterWidth}"
Height="{TemplateBinding ItemsPresenterHeight}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="StandardEmbeddedButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type dragablz:TabablzControl}" x:Key="MaterialDesignTabablzControlStyle">
<Setter Property="Background" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=Background}" />
<Setter Property="BorderBrush" Value="{DynamicResource PrimaryHueMidBrush}" />
<Setter Property="TextElement.Foreground" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=Window}, Path=(TextElement.Foreground)}" />
<Setter Property="ItemContainerStyle" Value="{StaticResource MaterialDesignDragableTabItemStyle}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dragablz:TabablzControl}">
<Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
<Grid.RowDefinitions>
<RowDefinition x:Name="RowDefinition0" Height="Auto"/>
<RowDefinition x:Name="RowDefinition1" Height="*"/>
</Grid.RowDefinitions>
<Border x:Name="contentPanel" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="0" KeyboardNavigation.DirectionalNavigation="Contained" Grid.Row="1" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local"
Margin="0 -1 0 0">
<Grid x:Name="PART_ItemsHolder" />
</Border>
<Grid Grid.Column="0" Grid.Row="0" x:Name="HeaderContainerGrid" Visibility="{TemplateBinding IsHeaderPanelVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource StandardEmbeddedButtonStyle}"></Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Background="{TemplateBinding BorderBrush}" Grid.ColumnSpan="4">
<Border.Effect>
<DropShadowEffect BlurRadius="5" ShadowDepth="2" Direction="270" Opacity=".5" />
</Border.Effect>
</Border>
<ContentControl Grid.Column="0" x:Name="PrefixContentControl"
Content="{TemplateBinding HeaderPrefixContent}"
ContentStringFormat="{TemplateBinding HeaderPrefixContentStringFormat}"
ContentTemplate="{TemplateBinding HeaderPrefixContentStringFormat}"
ContentTemplateSelector="{TemplateBinding HeaderPrefixContentTemplateSelector}"/>
<dragablz:DragablzItemsControl x:Name="PART_HeaderItemsControl"
Grid.Column="1"
FixedItemCount="{TemplateBinding FixedHeaderCount}"
ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Items}"
ItemContainerStyle="{TemplateBinding ItemContainerStyle}"
ItemsOrganiser="{TemplateBinding HeaderItemsOrganiser}"
KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"
ItemTemplate="{TemplateBinding HeaderItemTemplate}"
Style="{StaticResource TabablzDragablzItemsControlStyle}">
<dragablz:DragablzItemsControl.MaxWidth>
<MultiBinding>
<MultiBinding.Converter>
<dragablz:TabablzHeaderSizeConverter Orientation="Horizontal"/>
</MultiBinding.Converter>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ActualWidth" />
<Binding RelativeSource="{RelativeSource Self}" Path="ItemsPresenterWidth" />
<Binding ElementName="PrefixContentControl" Path="ActualWidth" />
<Binding ElementName="DefaultAddButton" Path="DesiredSize.Width" />
<Binding ElementName="SuffixContentControl" Path="DesiredSize.Width" />
</MultiBinding>
</dragablz:DragablzItemsControl.MaxWidth>
</dragablz:DragablzItemsControl>
<Button Style="{StaticResource MaterialDesignAddItemCommandButtonStyle}"
x:Name="DefaultAddButton"
Grid.Column="2"
Command="{x:Static dragablz:TabablzControl.AddItemCommand}"
Visibility="{TemplateBinding ShowDefaultAddButton, Converter={StaticResource BooleanToVisibilityConverter}}"
/>
<ContentControl Grid.Column="3" x:Name="SuffixContentControl"
Content="{TemplateBinding HeaderSuffixContent}"
ContentStringFormat="{TemplateBinding HeaderSuffixContentStringFormat}"
ContentTemplate="{TemplateBinding HeaderSuffixContentStringFormat}"
ContentTemplateSelector="{TemplateBinding HeaderSuffixContentTemplateSelector}"
HorizontalAlignment="Stretch"
/>
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="TabStripPlacement" Value="Bottom">
<Setter Property="Grid.Row" TargetName="PART_HeaderItemsControl" Value="1"/>
<Setter Property="Grid.Row" TargetName="contentPanel" Value="0"/>
<Setter Property="Height" TargetName="RowDefinition0" Value="*"/>
<Setter Property="Height" TargetName="RowDefinition1" Value="Auto"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
<Trigger Property="IsDraggingWindow" Value="True">
<Setter TargetName="DefaultAddButton" Property="Visibility" Value="Hidden" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="TabStripPlacement" Value="Left">
<Setter Property="HeaderItemsOrganiser">
<Setter.Value>
<dragablz:VerticalOrganiser />
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle" Value="{StaticResource MaterialDesignDragableTabItemVerticalStyle}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type dragablz:TabablzControl}">
<Grid x:Name="templateRoot" ClipToBounds="true" SnapsToDevicePixels="true" KeyboardNavigation.TabNavigation="Local">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="ColumnDefinition0" Width="Auto"/>
<ColumnDefinition x:Name="ColumnDefinition1" Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="contentPanel" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.Column="1" KeyboardNavigation.DirectionalNavigation="Contained" KeyboardNavigation.TabIndex="2" KeyboardNavigation.TabNavigation="Local" Margin="-1 0 0 0">
<Grid x:Name="PART_ItemsHolder" />
</Border>
<Grid Grid.Column="0" x:Name="HeaderContainerGrid" Visibility="{TemplateBinding IsHeaderPanelVisible, Converter={StaticResource BooleanToVisibilityConverter}}">
<Grid.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource StandardEmbeddedButtonStyle}" />
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Background="{TemplateBinding BorderBrush}" Grid.RowSpan="4">
<Border.Effect>
<DropShadowEffect BlurRadius="5" ShadowDepth="2" Direction="0" Opacity=".5" />
</Border.Effect>
</Border>
<ContentControl Grid.Row="0" x:Name="PrefixContentControl"
Content="{TemplateBinding HeaderPrefixContent}"
ContentStringFormat="{TemplateBinding HeaderPrefixContentStringFormat}"
ContentTemplate="{TemplateBinding HeaderPrefixContentStringFormat}"
ContentTemplateSelector="{TemplateBinding HeaderPrefixContentTemplateSelector}"/>
<dragablz:DragablzItemsControl x:Name="PART_HeaderItemsControl"
Grid.Row="1"
ItemsSource="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Items}"
ItemContainerStyle="{TemplateBinding ItemContainerStyle}"
FixedItemCount="{TemplateBinding FixedHeaderCount}"
ItemsOrganiser="{TemplateBinding HeaderItemsOrganiser}"
KeyboardNavigation.TabIndex="1" Panel.ZIndex="1"
ItemTemplate="{TemplateBinding HeaderItemTemplate}"
Style="{StaticResource TabablzDragablzItemsControlStyle}">
<dragablz:DragablzItemsControl.MaxHeight>
<MultiBinding>
<MultiBinding.Converter>
<dragablz:TabablzHeaderSizeConverter Orientation="Vertical"/>
</MultiBinding.Converter>
<Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ActualHeight" />
<Binding RelativeSource="{RelativeSource Self}" Path="ItemsPresenterHeight" />
<Binding ElementName="PrefixContentControl" Path="ActualHeight" />
<Binding ElementName="DefaultAddButton" Path="DesiredSize.Height" />
<Binding ElementName="SuffixContentControl" Path="DesiredSize.Height" />
</MultiBinding>
</dragablz:DragablzItemsControl.MaxHeight>
</dragablz:DragablzItemsControl>
<Button Style="{StaticResource MaterialDesignAddItemCommandButtonStyle}"
x:Name="DefaultAddButton"
Grid.Row="2"
Command="{x:Static dragablz:TabablzControl.AddItemCommand}"
Visibility="{TemplateBinding ShowDefaultAddButton, Converter={StaticResource BooleanToVisibilityConverter}}" />
<ContentControl Grid.Row="3" x:Name="SuffixContentControl"
Content="{TemplateBinding HeaderSuffixContent}"
ContentStringFormat="{TemplateBinding HeaderSuffixContentStringFormat}"
ContentTemplate="{TemplateBinding HeaderSuffixContentStringFormat}"
ContentTemplateSelector="{TemplateBinding HeaderSuffixContentTemplateSelector}"
VerticalAlignment="Top" />
</Grid>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="TextElement.Foreground" TargetName="templateRoot" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
<Trigger Property="IsDraggingWindow" Value="True">
<Setter TargetName="DefaultAddButton" Property="Visibility" Value="Hidden" />
</Trigger>
<Trigger Property="TabStripPlacement" Value="Right">
<!-- TODO -->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
<Style x:Key="MaterialDesignAlternateDragableTabItemStyle" TargetType="{x:Type dragablz:DragablzItem}" BasedOn="{StaticResource MaterialDesignDragableTabItemStyle}">
<Setter Property="Foreground" Value="{DynamicResource MaterialDesignColumnHeader}"/>
<Setter Property="local:MaterialDesignAssist.IndicatorBrush" Value="{DynamicResource PrimaryHueMidBrush}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="{DynamicResource PrimaryHueMidBrush}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="False" />
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Foreground" Value="{DynamicResource MaterialDesignBody}" />
</MultiTrigger>
</Style.Triggers>
</Style>
<Style x:Key="MaterialDesignAlternateDragableTabItemVerticalStyle" TargetType="{x:Type dragablz:DragablzItem}" BasedOn="{StaticResource MaterialDesignDragableTabItemVerticalStyle}">
<Setter Property="Foreground" Value="{DynamicResource MaterialDesignColumnHeader}"/>
<Setter Property="local:MaterialDesignAssist.IndicatorBrush" Value="{DynamicResource PrimaryHueMidBrush}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="{DynamicResource PrimaryHueMidBrush}" />
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="False" />
<Condition Property="IsMouseOver" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Foreground" Value="{DynamicResource MaterialDesignBody}" />
</MultiTrigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type dragablz:TabablzControl}" x:Key="MaterialDesignAlternateTabablzControlStyle" BasedOn="{StaticResource MaterialDesignTabablzControlStyle}">
<Setter Property="BorderBrush" Value="{DynamicResource MaterialDesignPaper}" />
<Setter Property="ItemContainerStyle" Value="{StaticResource MaterialDesignAlternateDragableTabItemStyle}" />
<Style.Triggers>
<Trigger Property="TabStripPlacement" Value="Left">
<Setter Property="ItemContainerStyle" Value="{StaticResource MaterialDesignAlternateDragableTabItemVerticalStyle}" />
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
namespace Dragablz.Themes
{
/// <summary>
/// Helper propries for configuring the material design style.
/// </summary>
public static class MaterialDesignAssist
{
/// <summary>
/// Framework use only.
/// </summary>
public static readonly DependencyProperty IndicatorBrushProperty = DependencyProperty.RegisterAttached(
"IndicatorBrush", typeof (Brush), typeof (MaterialDesignAssist), new PropertyMetadata(default(Brush)));
/// <summary>
/// The indicator (underline) brush.
/// </summary>
/// <param name="element"></param>
/// <param name="value"></param>
public static void SetIndicatorBrush(DependencyObject element, Brush value)
{
element.SetValue(IndicatorBrushProperty, value);
}
/// <summary>
/// The indicator (underline) brush.
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
public static Brush GetIndicatorBrush(DependencyObject element)
{
return (Brush) element.GetValue(IndicatorBrushProperty);
}
}
}

View File

@@ -0,0 +1,218 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
namespace Dragablz.Themes
{
[TemplateVisualState(GroupName = "CommonStates", Name = TemplateStateNormal)]
[TemplateVisualState(GroupName = "CommonStates", Name = TemplateStateMousePressed)]
[TemplateVisualState(GroupName = "CommonStates", Name = TemplateStateMouseOut)]
public class Ripple : ContentControl
{
public const string TemplateStateNormal = "Normal";
public const string TemplateStateMousePressed = "MousePressed";
public const string TemplateStateMouseOut = "MouseOut";
private static readonly HashSet<Ripple> PressedInstances = new HashSet<Ripple>();
static Ripple()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(Ripple), new FrameworkPropertyMetadata(typeof(Ripple)));
EventManager.RegisterClassHandler(typeof(Window), Mouse.PreviewMouseUpEvent, new MouseButtonEventHandler(MouseButtonEventHandler), true);
EventManager.RegisterClassHandler(typeof(Window), Mouse.MouseMoveEvent, new MouseEventHandler(MouseMouveEventHandler), true);
EventManager.RegisterClassHandler(typeof(UserControl), Mouse.PreviewMouseUpEvent, new MouseButtonEventHandler(MouseButtonEventHandler), true);
EventManager.RegisterClassHandler(typeof(UserControl), Mouse.MouseMoveEvent, new MouseEventHandler(MouseMouveEventHandler), true);
}
public Ripple()
{
SizeChanged += OnSizeChanged;
}
private static void MouseButtonEventHandler(object sender, MouseButtonEventArgs e)
{
foreach (var ripple in PressedInstances)
{
// adjust the transition scale time according to the current animated scale
var scaleTrans = ripple.Template.FindName("ScaleTransform", ripple) as ScaleTransform;
if (scaleTrans != null)
{
double currentScale = scaleTrans.ScaleX;
var newTime = TimeSpan.FromMilliseconds(300 * (1.0 - currentScale));
// change the scale animation according to the current scale
var scaleXKeyFrame = ripple.Template.FindName("MousePressedToNormalScaleXKeyFrame", ripple) as EasingDoubleKeyFrame;
if (scaleXKeyFrame != null)
{
scaleXKeyFrame.KeyTime = KeyTime.FromTimeSpan(newTime);
}
var scaleYKeyFrame = ripple.Template.FindName("MousePressedToNormalScaleYKeyFrame", ripple) as EasingDoubleKeyFrame;
if (scaleYKeyFrame != null)
{
scaleYKeyFrame.KeyTime = KeyTime.FromTimeSpan(newTime);
}
}
VisualStateManager.GoToState(ripple, TemplateStateNormal, true);
}
PressedInstances.Clear();
}
private static void MouseMouveEventHandler(object sender, MouseEventArgs e)
{
foreach (var ripple in PressedInstances.ToList())
{
var relativePosition = Mouse.GetPosition(ripple);
if (relativePosition.X < 0
|| relativePosition.Y < 0
|| relativePosition.X >= ripple.ActualWidth
|| relativePosition.Y >= ripple.ActualHeight)
{
VisualStateManager.GoToState(ripple, TemplateStateMouseOut, true);
PressedInstances.Remove(ripple);
}
}
}
public static readonly DependencyProperty FeedbackProperty = DependencyProperty.Register(
"Feedback", typeof(Brush), typeof(Ripple), new PropertyMetadata(default(Brush)));
public Brush Feedback
{
get { return (Brush)GetValue(FeedbackProperty); }
set { SetValue(FeedbackProperty, value); }
}
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
var point = e.GetPosition(this);
if (RippleAssist.GetIsCentered(this))
{
var innerContent = (Content as FrameworkElement);
if (innerContent != null)
{
var position = innerContent.TransformToAncestor(this)
.Transform(new Point(0, 0));
RippleX = position.X + innerContent.ActualWidth / 2 - RippleSize / 2;
RippleY = position.Y + innerContent.ActualHeight / 2 - RippleSize / 2;
}
else
{
RippleX = ActualWidth / 2 - RippleSize / 2;
RippleY = ActualHeight / 2 - RippleSize / 2;
}
}
else
{
RippleX = point.X - RippleSize / 2;
RippleY = point.Y - RippleSize / 2;
}
VisualStateManager.GoToState(this, TemplateStateNormal, false);
VisualStateManager.GoToState(this, TemplateStateMousePressed, true);
PressedInstances.Add(this);
base.OnPreviewMouseLeftButtonDown(e);
}
private static readonly DependencyPropertyKey RippleSizePropertyKey =
DependencyProperty.RegisterReadOnly(
"RippleSize", typeof(double), typeof(Ripple),
new PropertyMetadata(default(double)));
public static readonly DependencyProperty RippleSizeProperty =
RippleSizePropertyKey.DependencyProperty;
public double RippleSize
{
get { return (double)GetValue(RippleSizeProperty); }
private set { SetValue(RippleSizePropertyKey, value); }
}
private static readonly DependencyPropertyKey RippleXPropertyKey =
DependencyProperty.RegisterReadOnly(
"RippleX", typeof(double), typeof(Ripple),
new PropertyMetadata(default(double)));
public static readonly DependencyProperty RippleXProperty =
RippleXPropertyKey.DependencyProperty;
public double RippleX
{
get { return (double)GetValue(RippleXProperty); }
private set { SetValue(RippleXPropertyKey, value); }
}
private static readonly DependencyPropertyKey RippleYPropertyKey =
DependencyProperty.RegisterReadOnly(
"RippleY", typeof(double), typeof(Ripple),
new PropertyMetadata(default(double)));
public static readonly DependencyProperty RippleYProperty =
RippleYPropertyKey.DependencyProperty;
public double RippleY
{
get { return (double)GetValue(RippleYProperty); }
private set { SetValue(RippleYPropertyKey, value); }
}
/// <summary>
/// The DependencyProperty for the RecognizesAccessKey property.
/// Default Value: false
/// </summary>
public static readonly DependencyProperty RecognizesAccessKeyProperty =
DependencyProperty.Register(
"RecognizesAccessKey", typeof(bool), typeof(Ripple),
new PropertyMetadata(default(bool)));
/// <summary>
/// Determine if Ripple should use AccessText in its style
/// </summary>
public bool RecognizesAccessKey
{
get { return (bool)GetValue(RecognizesAccessKeyProperty); }
set { SetValue(RecognizesAccessKeyProperty, value); }
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
VisualStateManager.GoToState(this, TemplateStateNormal, false);
}
private void OnSizeChanged(object sender, SizeChangedEventArgs sizeChangedEventArgs)
{
var innerContent = (Content as FrameworkElement);
double width, height;
if (RippleAssist.GetIsCentered(this) && innerContent != null)
{
width = innerContent.ActualWidth;
height = innerContent.ActualHeight;
}
else
{
width = sizeChangedEventArgs.NewSize.Width;
height = sizeChangedEventArgs.NewSize.Height;
}
var radius = Math.Sqrt(Math.Pow(width, 2) + Math.Pow(height, 2));
RippleSize = 2 * radius * RippleAssist.GetRippleSizeMultiplier(this);
}
}
}

View File

@@ -0,0 +1,74 @@
using System.Windows;
namespace Dragablz.Themes
{
public static class RippleAssist
{
#region ClipToBound
public static readonly DependencyProperty ClipToBoundsProperty = DependencyProperty.RegisterAttached(
"ClipToBounds", typeof(bool), typeof(RippleAssist), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits));
public static void SetClipToBounds(DependencyObject element, bool value)
{
element.SetValue(ClipToBoundsProperty, value);
}
public static bool GetClipToBounds(DependencyObject element)
{
return (bool)element.GetValue(ClipToBoundsProperty);
}
#endregion
#region StayOnCenter
/// <summary>
/// Set to <c>true</c> to cause the ripple to originate from the centre of the
/// content. Otherwise the effect will originate from the mouse down position.
/// </summary>
public static readonly DependencyProperty IsCenteredProperty = DependencyProperty.RegisterAttached(
"IsCentered", typeof(bool), typeof(RippleAssist), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.Inherits));
/// <summary>
/// Set to <c>true</c> to cause the ripple to originate from the centre of the
/// content. Otherwise the effect will originate from the mouse down position.
/// </summary>
/// <param name="element"></param>
/// <param name="value"></param>
public static void SetIsCentered(DependencyObject element, bool value)
{
element.SetValue(IsCenteredProperty, value);
}
/// <summary>
/// Set to <c>true</c> to cause the ripple to originate from the centre of the
/// content. Otherwise the effect will originate from the mouse down position.
/// </summary>
/// <param name="element"></param>
public static bool GetIsCentered(DependencyObject element)
{
return (bool)element.GetValue(IsCenteredProperty);
}
#endregion
#region RippleSizeMultiplier
public static readonly DependencyProperty RippleSizeMultiplierProperty = DependencyProperty.RegisterAttached(
"RippleSizeMultiplier", typeof(double), typeof(RippleAssist), new FrameworkPropertyMetadata(1.0, FrameworkPropertyMetadataOptions.Inherits));
public static void SetRippleSizeMultiplier(DependencyObject element, double value)
{
element.SetValue(RippleSizeMultiplierProperty, value);
}
public static double GetRippleSizeMultiplier(DependencyObject element)
{
return (double)element.GetValue(RippleSizeMultiplierProperty);
}
#endregion
}
}

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
namespace Dragablz.Themes
{
public enum SystemCommandType
{
CloseWindow,
MaximizeWindow,
MinimzeWindow,
RestoreWindow
}
public class SystemCommandIcon : Control
{
static SystemCommandIcon()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SystemCommandIcon), new FrameworkPropertyMetadata(typeof(SystemCommandIcon)));
}
public static readonly DependencyProperty SystemCommandTypeProperty = DependencyProperty.Register(
"SystemCommandType", typeof (SystemCommandType), typeof (SystemCommandIcon), new PropertyMetadata(default(SystemCommandType)));
public SystemCommandType SystemCommandType
{
get { return (SystemCommandType) GetValue(SystemCommandTypeProperty); }
set { SetValue(SystemCommandTypeProperty, value); }
}
}
}

Some files were not shown because too many files have changed in this diff Show More