diff --git a/ExtendedWPFToolkitSolution/ExtendedWPFToolkit.sln b/ExtendedWPFToolkitSolution/ExtendedWPFToolkit.sln
index 8b521022..a1c6307a 100644
--- a/ExtendedWPFToolkitSolution/ExtendedWPFToolkit.sln
+++ b/ExtendedWPFToolkitSolution/ExtendedWPFToolkit.sln
@@ -30,6 +30,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.Modules.ChildWindow
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.Modules.Color", "Src\Samples\Modules\Samples.Modules.Color\Samples.Modules.Color.csproj", "{A36AB9C9-D813-4018-B341-8729A9B5BD7D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.Modules.DataGrid", "Src\Samples\Modules\Samples.Modules.DataGrid\Samples.Modules.DataGrid.csproj", "{129477D9-F413-495F-BE05-D049575BC16C}"
+EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.Modules.DateTime", "Src\Samples\Modules\Samples.Modules.DateTime\Samples.Modules.DateTime.csproj", "{CAB850B9-1D47-4F45-A6A5-07B48599B5D7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.Modules.Numeric", "Src\Samples\Modules\Samples.Modules.Numeric\Samples.Modules.Numeric.csproj", "{CBED4977-79ED-4E46-AA00-0A4D83D333A6}"
@@ -54,6 +56,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.Modules.Magnifier",
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Samples.Modules.MessageBox", "Src\Samples\Modules\Samples.Modules.MessageBox\Samples.Modules.MessageBox.csproj", "{C324395B-C1D1-407B-8745-4AAC39B8BFD9}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Xceed.Wpf.DataGrid", "Src\Xceed.Wpf.DataGrid\Xceed.Wpf.DataGrid.csproj", "{63648392-6CE9-4A60-96D4-F9FD718D29B0}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -254,6 +258,26 @@ Global
{C324395B-C1D1-407B-8745-4AAC39B8BFD9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{C324395B-C1D1-407B-8745-4AAC39B8BFD9}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{C324395B-C1D1-407B-8745-4AAC39B8BFD9}.Release|x86.ActiveCfg = Release|Any CPU
+ {63648392-6CE9-4A60-96D4-F9FD718D29B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {63648392-6CE9-4A60-96D4-F9FD718D29B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {63648392-6CE9-4A60-96D4-F9FD718D29B0}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {63648392-6CE9-4A60-96D4-F9FD718D29B0}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {63648392-6CE9-4A60-96D4-F9FD718D29B0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {63648392-6CE9-4A60-96D4-F9FD718D29B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {63648392-6CE9-4A60-96D4-F9FD718D29B0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {63648392-6CE9-4A60-96D4-F9FD718D29B0}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {63648392-6CE9-4A60-96D4-F9FD718D29B0}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {63648392-6CE9-4A60-96D4-F9FD718D29B0}.Release|x86.ActiveCfg = Release|Any CPU
+ {129477D9-F413-495F-BE05-D049575BC16C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {129477D9-F413-495F-BE05-D049575BC16C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {129477D9-F413-495F-BE05-D049575BC16C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
+ {129477D9-F413-495F-BE05-D049575BC16C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
+ {129477D9-F413-495F-BE05-D049575BC16C}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {129477D9-F413-495F-BE05-D049575BC16C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {129477D9-F413-495F-BE05-D049575BC16C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {129477D9-F413-495F-BE05-D049575BC16C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
+ {129477D9-F413-495F-BE05-D049575BC16C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
+ {129477D9-F413-495F-BE05-D049575BC16C}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -278,5 +302,6 @@ Global
{910A7A19-DD9F-4989-A001-BD7E5BAD2FE8} = {F927B3FE-820C-4EE1-921F-D10D3AE287AE}
{C3D3FFAD-2DA3-4142-8DE8-7045E347B63A} = {F927B3FE-820C-4EE1-921F-D10D3AE287AE}
{C324395B-C1D1-407B-8745-4AAC39B8BFD9} = {F927B3FE-820C-4EE1-921F-D10D3AE287AE}
+ {129477D9-F413-495F-BE05-D049575BC16C} = {F927B3FE-820C-4EE1-921F-D10D3AE287AE}
EndGlobalSection
EndGlobal
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.CheckLists/Views/HomeView.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.CheckLists/Views/HomeView.xaml
index 8ce4243c..61eb908b 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.CheckLists/Views/HomeView.xaml
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.CheckLists/Views/HomeView.xaml
@@ -129,7 +129,6 @@
-
-
+ IsDropDownOpen="{Binding ElementName=_isDropDownOpen, Path=IsChecked, Mode=TwoWay}" />
-
-
-
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/DataGridModule.cs b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/DataGridModule.cs
new file mode 100644
index 00000000..739d1bd7
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/DataGridModule.cs
@@ -0,0 +1,46 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using Microsoft.Practices.Prism.Regions;
+using Microsoft.Practices.Unity;
+using Samples.Infrastructure;
+using Samples.Infrastructure.Extensions;
+using Samples.Modules.DataGrid.Views;
+
+namespace Samples.Modules.DataGrid
+{
+ public class DataGridModule : ModuleBase
+ {
+ public DataGridModule( IUnityContainer container, IRegionManager regionManager )
+ : base( container, regionManager )
+ {
+ }
+
+ protected override void InitializeModule()
+ {
+ RegionManager.RegisterViewWithRegion( RegionNames.NavigationRegion, typeof( NavigationView ) );
+ }
+
+ protected override void RegisterViewsAndTypes()
+ {
+ Container.RegisterNavigationType( typeof( HomeView ) );
+ Container.RegisterNavigationType( typeof( FullVersion ) );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Properties/AssemblyInfo.cs b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..b629e124
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Properties/AssemblyInfo.cs
@@ -0,0 +1,72 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// 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( "Extended WPF Toolkit DataGrid Sample" )]
+[assembly: AssemblyDescription( "" )]
+[assembly: AssemblyConfiguration( "" )]
+[assembly: AssemblyCompany( "Xceed Software Inc." )]
+[assembly: AssemblyProduct( "Extended WPF Toolkit DataGrid Sample" )]
+[assembly: AssemblyCopyright( "Copyright © Xceed Software Inc. 2010-2012" )]
+[assembly: AssemblyTrademark( "" )]
+[assembly: AssemblyCulture( "" )]
+
+// 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
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the 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( "1.0.0.0" )]
+[assembly: AssemblyFileVersion( "1.0.0.0" )]
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Samples.Modules.DataGrid.csproj b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Samples.Modules.DataGrid.csproj
new file mode 100644
index 00000000..bdaad47d
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Samples.Modules.DataGrid.csproj
@@ -0,0 +1,116 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {129477D9-F413-495F-BE05-D049575BC16C}
+ library
+ Properties
+ Samples.Modules.DataGrid
+ Samples.Modules.DataGrid
+ v4.0
+ Client
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\..\..\..\Libs\Prism\Microsoft.Practices.Prism.dll
+
+
+ ..\..\..\..\Libs\Prism\Microsoft.Practices.Unity.dll
+
+
+
+
+
+
+
+
+
+ 4.0
+
+
+
+
+
+ ..\..\..\..\Libs\Xceed.Wpf.DataGrid.Samples.SampleData.dll
+
+
+
+
+ Code
+
+
+ FullVersion.xaml
+
+
+ HomeView.xaml
+
+
+ NavigationView.xaml
+
+
+
+
+ Code
+
+
+
+
+
+ {63648392-6CE9-4A60-96D4-F9FD718D29B0}
+ Xceed.Wpf.DataGrid
+
+
+ {A4A049A4-665A-4651-9046-7D06E9D0CCDC}
+ Samples.Infrastructure
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+
+ xcopy "$(TargetDir)*.*" "$(SolutionDir)Src\Samples\Samples\bin\$(ConfigurationName)\" /Y
+xcopy "$(ProjectDir)Views" "$(SolutionDir)Src\Samples\Samples\bin\$(ConfigurationName)\Samples\$(ProjectName)\" /s /Y /I
+
+
+
\ No newline at end of file
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/FullVersion.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/FullVersion.xaml
new file mode 100644
index 00000000..e2703f39
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/FullVersion.xaml
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+ The full featured version of the DataGrid available commercially will provide
+ you with extended features like:
+
+
+
+ Master Details
+
+
+ Filter Row
+
+
+ Insertion Row
+
+
+ Excel Auto-Filtering
+
+
+ Column Chooser
+
+
+ CardView, CompactCardView
+
+
+ 3DView, MultiSurfaceView
+
+
+ Statistical Functions and Summary row
+
+
+ Print, Print Preview
+
+
+ Export to Excel
+
+
+ Fixed Column Splitter UI element
+
+
+ Persist User Settings
+
+
+ Design time support in Visual Studio and Expression Blend
+
+
+ Comprehensive documentation fully integrated into Visual Studio
+
+
+ Includes a variety of VB.NET and C# sample applications to get you started
+
+
+ Office 2007 Themes: Blue, Black, and Silver
+
+
+ The Xceed Live Explorer theme
+
+
+ Includes Xceed 3D Views for WPF
+
+
+ And many more....
+
+
+
+
+
+
+
+ Click here for more details about the full featured DataGrid control.
+
+
+
+
+
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/FullVersion.xaml.cs b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/FullVersion.xaml.cs
new file mode 100644
index 00000000..06fcdf64
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/FullVersion.xaml.cs
@@ -0,0 +1,46 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using Microsoft.Practices.Prism.Regions;
+using Samples.Infrastructure.Controls;
+using Xceed.Wpf.DataGrid.Samples.SampleData;
+using System.Data;
+using Xceed.Wpf.DataGrid;
+using System.Diagnostics;
+
+namespace Samples.Modules.DataGrid.Views
+{
+ ///
+ /// Interaction logic for FullVersion.xaml
+ ///
+ [RegionMemberLifetime( KeepAlive = false )]
+ public partial class FullVersion : DemoView
+ {
+ public FullVersion()
+ {
+ InitializeComponent();
+ }
+
+ private void Hyperlink_RequestNavigate( object sender, System.Windows.Navigation.RequestNavigateEventArgs e )
+ {
+ Process.Start( new ProcessStartInfo( e.Uri.AbsoluteUri ) );
+ e.Handled = true;
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/HomeView.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/HomeView.xaml
new file mode 100644
index 00000000..61fb391e
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/HomeView.xaml
@@ -0,0 +1,54 @@
+
+
+
+The experience Xceed DataGrid for WPF provides centers on its Tableflow view, which lets you take advantage of a stunning, shaded appearance and capabilities such as inertial smooth scrolling and animated full-column reordering—which mimic the physics of real-life movement. Add to that the datagrid’s zero-lag data virtualization, and you have the fastest WPF datagrid around—in performance and feel.
+
+It's rock-solid and time-tested, so you can trust it in your most important applications. Constantly evolving—no other datagrid is updated as often—it has more features than any other offering and a flexible, extensible object model. It also provides unbeatable performance by handling millions of rows and thousands of columns, and integrates easily into any WPF app. It’s easy to understand why it’s the most-adopted WPF datagrid available and used by Microsoft in Visual Studio 2010 and by IBM U2 in SystemBuilder 4GL (SB+)!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/HomeView.xaml.cs b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/HomeView.xaml.cs
new file mode 100644
index 00000000..4c56b5e3
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/HomeView.xaml.cs
@@ -0,0 +1,47 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using Microsoft.Practices.Prism.Regions;
+using Samples.Infrastructure.Controls;
+using Xceed.Wpf.DataGrid.Samples.SampleData;
+using System.Data;
+using Xceed.Wpf.DataGrid;
+
+namespace Samples.Modules.DataGrid.Views
+{
+ ///
+ /// Interaction logic for HomeView.xaml
+ ///
+ [RegionMemberLifetime( KeepAlive = false )]
+ public partial class HomeView : DemoView
+ {
+ public HomeView()
+ {
+ this.Orders = DataProvider.GetNorthwindDataSet().Tables[ "Orders" ];
+ InitializeComponent();
+ }
+
+ public DataTable Orders
+ {
+ get;
+ private set;
+ }
+
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/NavigationView.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/NavigationView.xaml
new file mode 100644
index 00000000..ae1a7d56
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/NavigationView.xaml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/PropertyGrid/Implementation/Editors/DoubleUpDownEditor.cs b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/NavigationView.xaml.cs
similarity index 68%
rename from ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/PropertyGrid/Implementation/Editors/DoubleUpDownEditor.cs
rename to ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/NavigationView.xaml.cs
index ce2b6ded..42995634 100644
--- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/PropertyGrid/Implementation/Editors/DoubleUpDownEditor.cs
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DataGrid/Views/NavigationView.xaml.cs
@@ -17,18 +17,18 @@
**********************************************************************/
-namespace Xceed.Wpf.Toolkit.PropertyGrid.Editors
+using System.Windows.Controls;
+
+namespace Samples.Modules.DataGrid.Views
{
- public class DoubleUpDownEditor : TypeEditor
+ ///
+ /// Interaction logic for NavigationView.xaml
+ ///
+ public partial class NavigationView : TreeViewItem
{
- protected override void SetControlProperties()
- {
- Editor.BorderThickness = new System.Windows.Thickness( 0 );
- }
-
- protected override void SetValueDependencyProperty()
+ public NavigationView()
{
- ValueProperty = DoubleUpDown.ValueProperty;
+ InitializeComponent();
}
}
}
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DateTime/Views/HomeView.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DateTime/Views/HomeView.xaml
index 4f195aec..e54d3bf2 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DateTime/Views/HomeView.xaml
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.DateTime/Views/HomeView.xaml
@@ -16,7 +16,7 @@
Visit http://xceed.com and follow @datagrid on Twitter.
********************************************************************-->
-
+
+ Description="The DateTimePicker and TimePicker controls let you select the date and the time in several ways. The TimePicker is used in the DateTimePicker.">
@@ -45,7 +45,6 @@
-
@@ -67,20 +66,17 @@
-
-
@@ -102,7 +98,6 @@
-
-
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+ Content="Styled MessageBox"
+ Click="StyledMessageBoxButton_Click"/>
+
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.MessageBox/Views/HomeView.xaml.cs b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.MessageBox/Views/HomeView.xaml.cs
index 7141903e..3597e60f 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.MessageBox/Views/HomeView.xaml.cs
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.MessageBox/Views/HomeView.xaml.cs
@@ -32,16 +32,25 @@ namespace Samples.Modules.MessageBox.Views
[RegionMemberLifetime( KeepAlive = false )]
public partial class HomeView : DemoView
{
+ private string _standardTitle = "Standard MessageBox";
+ private string _styledTitle = "Extended WPF Toolkit MessageBox";
+ private string _standardMessage = "The standard system MessageBox will always have this look. No styling is possible.";
+ private string _styledMessage = "The Toolkit MessageBox allows you to style it in order to integrate it into your application colors and styles.";
+
public HomeView()
{
InitializeComponent();
}
- private void Button_Click( object sender, System.Windows.RoutedEventArgs e )
+ private void StandardMessageBoxButton_Click( object sender, System.Windows.RoutedEventArgs e )
{
- System.Windows.Style style = ( System.Windows.Style )this.Resources[ "messageBoxStyle" ];
- Xceed.Wpf.Toolkit.MessageBox.Show( null, "Hello World!", "Extended WPF Toolkit MessageBox", style );
+ System.Windows.MessageBox.Show( _standardMessage, _standardTitle );
}
+ private void StyledMessageBoxButton_Click( object sender, System.Windows.RoutedEventArgs e )
+ {
+ System.Windows.Style style = ( System.Windows.Style )this.Resources[ "messageBoxStyle" ];
+ Xceed.Wpf.Toolkit.MessageBox.Show( null, _styledMessage, _styledTitle, style );
+ }
}
}
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Numeric/Views/HomeView.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Numeric/Views/HomeView.xaml
index 5e88b543..961ffb26 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Numeric/Views/HomeView.xaml
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Numeric/Views/HomeView.xaml
@@ -21,58 +21,71 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sample="clr-namespace:Samples.Infrastructure.Controls;assembly=Samples.Infrastructure"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
- Title="Numeric Controls"
- Description="The DecimalUpDown, DoubleUpDown and IntegerUpDown provides a TextBox with button spinners that allow incrementing and decrementing numeric values by using the spinner buttons, keyboard up/down arrows, or mouse wheel.">
-
-
-
-
-
+ Title="Numeric Controls">
+
+
+
+
+ Numeric up-down controls provides a TextBox with button spinners that allow incrementing
+ and decrementing numeric values by using the spinner buttons, keyboard up/down arrows, or mouse wheel
+
+
+ The following controls are present to support various native numeric types:
+ ByteUpDown, ShortUpDown, IntegerUpDown, LongUpDown, SingleUpDown, DoubleUpDown, DecimalUpDown.
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Currency
- Fixed Point
- General
- Number
- Percent
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Currency
+ Fixed Point
+ General
+ Number
+ Percent
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
+
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Pie/Views/PieChart.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Pie/Views/PieChart.xaml
index e8f7a093..3763b572 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Pie/Views/PieChart.xaml
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Pie/Views/PieChart.xaml
@@ -24,7 +24,6 @@
xmlns:local="clr-namespace:Samples.Modules.Pie.Views"
Title="Chart"
Description="Obviously, the Pie control can be used to create a pie chart.">
-
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Pie/Views/PieProgress.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Pie/Views/PieProgress.xaml
index 5fcc9dbd..7c597723 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Pie/Views/PieProgress.xaml
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Pie/Views/PieProgress.xaml
@@ -24,7 +24,6 @@
xmlns:local="clr-namespace:Samples.Modules.Pie.Views"
Title="Progress"
Description="The Pie control properties can be animated to create various effects and controls like this circular progress bar">
-
DefaultEditors.xaml
+
+ True
+ True
+ DisplayLocalizationRes.resx
+
ExpandableProperties.xaml
@@ -136,9 +141,21 @@
MSBuild:Compile
+
+
+ ResXFileCodeGenerator
+ DisplayLocalizationRes.Designer.cs
+ Designer
+
+
+
+
+ Designer
+
+
- xcopy "$(TargetDir)*.*" "$(SolutionDir)Src\Samples\Samples\bin\$(ConfigurationName)\" /Y
+ xcopy "$(TargetDir)*.*" "$(SolutionDir)Src\Samples\Samples\bin\$(ConfigurationName)\" /Y /S
xcopy "$(ProjectDir)Views" "$(SolutionDir)Src\Samples\Samples\bin\$(ConfigurationName)\Samples\$(ProjectName)\" /s /Y /I
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Détails
+
+
+ Acteur Favori 1 (Aucun DisplayName)
+
+
+ Le type de cette propriété est un enum où les valeurs n'ont aucun attribut "DisplayName". Les valeur affiché sont retourné par la méthode ToString()
+
+
+ Acteur Favori 2 (Avec DisplayName)
+
+
+ Le type de cette propriété est un enum où les valeurs sont décoré avec l'attribut "DisplayName". Les valeurs affichées sont celles spécifié par l'attribut ExtendedDisplayName
+
+
+ Acteur Favori 3
+
+
+ La valeur "(Non proposé)" démontre que les valeur des enum peuvent aussi être localisée
+
+
+ Prénom
+
+
+ Prénom de la personne
+
+
+ Information
+
+
+ Nom de famille
+
+
+ Le nom de famille de la personne
+
+
+ (Non proposé)
+
+
\ No newline at end of file
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.PropertyGrid/Views/DisplayLocalizationRes.resx b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.PropertyGrid/Views/DisplayLocalizationRes.resx
new file mode 100644
index 00000000..6462554d
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.PropertyGrid/Views/DisplayLocalizationRes.resx
@@ -0,0 +1,159 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Details
+
+
+ Favorite Actor 1 (No DisplayName)
+
+
+ This property type is an enum where values has no DisplayName attributes. Theses values display are provided by ToString() method
+
+
+ Favorite Actor 2 (With DisplayName)
+
+
+ This property type is an enum where values are decorated with the ExtendedDisplayName attribute. Combined with the EnumDisplayNameConverter, this allow to customize display values
+
+
+ Favorite Actor 3
+
+
+ "(Not listed)" enum value show that enum values can also be localized
+
+
+ First Name
+
+
+ First name of that person
+
+
+ Information
+
+
+ Last Name
+
+
+ Usualy the Familly name of the person
+
+
+ (No listed)
+
+
\ No newline at end of file
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.PropertyGrid/Views/HomeView.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.PropertyGrid/Views/HomeView.xaml
index 5ebd63ec..9023a747 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.PropertyGrid/Views/HomeView.xaml
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.PropertyGrid/Views/HomeView.xaml
@@ -34,6 +34,7 @@
+
@@ -57,6 +58,8 @@
+
+
@@ -70,14 +73,18 @@
- Inspect Me
+
+ Inspect Me
+ Inspect That
+
+ ShowAdvancedOptions="{Binding IsChecked, ElementName=_showAdvancedOptions}"
+ IsReadOnly="{Binding IsChecked, ElementName=_isReadOnly}"/>
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.PropertyGrid/Views/HomeView.xaml.cs b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.PropertyGrid/Views/HomeView.xaml.cs
index 3745fa8f..a0a8d253 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.PropertyGrid/Views/HomeView.xaml.cs
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.PropertyGrid/Views/HomeView.xaml.cs
@@ -19,6 +19,7 @@
using Microsoft.Practices.Prism.Regions;
using Samples.Infrastructure.Controls;
+using System;
namespace Samples.Modules.PropertyGrid.Views
{
@@ -32,5 +33,10 @@ namespace Samples.Modules.PropertyGrid.Views
{
InitializeComponent();
}
+
+ private void OnButtonClick( object sender, EventArgs e )
+ {
+ _propertyGrid.SelectedObject = sender;
+ }
}
}
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Samples.Modules.Text.csproj b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Samples.Modules.Text.csproj
index 33091a37..6ce027d8 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Samples.Modules.Text.csproj
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Samples.Modules.Text.csproj
@@ -69,6 +69,9 @@
Code
+
+ AutoSelectTextBoxView.xaml
+
HomeView.xaml
@@ -90,6 +93,10 @@
+
+ MSBuild:Compile
+ Designer
+
MSBuild:Compile
Designer
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/TextModule.cs b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/TextModule.cs
index 5f056680..1f0db4f1 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/TextModule.cs
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/TextModule.cs
@@ -44,6 +44,7 @@ namespace Samples.Modules.Text
Container.RegisterNavigationType( typeof( MultiLineTextEditorView ) );
Container.RegisterNavigationType( typeof( RichTextBoxView ) );
Container.RegisterNavigationType( typeof( WatermarkTextBoxView ) );
+ Container.RegisterNavigationType( typeof( AutoSelectTextBoxView ) );
}
}
}
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/AutoSelectTextBoxView.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/AutoSelectTextBoxView.xaml
new file mode 100644
index 00000000..c6b4a4bb
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/AutoSelectTextBoxView.xaml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ AutoSelectBehavior:
+
+ Controled by the "AutoSelectBehavior" property, the content of the AutoSelectTextBox will be selected or not when the control get the focus.
+
+
+ AutoMoveFocus:
+
+ Effect with "MaxLength" property:
+
+
+ Setting the "MaxLenght" of the text box allow the focus to move from the AutoSelectTextBox once the max lenght has been reached.
+ In the following "Telephone Number" fields, the "MaxLenght" property of the control has been set to 3, 3 and 4, respectively
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Effect with Arrow keys
+
+
+ The "AutoMoveFocus" at true also allow to navigate the focus thru the controls using the arrow keys to move the focus up, down, left or right.
+ You are no longer limited to the "left-right" scenario of the "Tab, Shift-Tab" keys.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/PropertyGrid/Implementation/Editors/DecimalUpDownEditor.cs b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/AutoSelectTextBoxView.xaml.cs
similarity index 66%
rename from ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/PropertyGrid/Implementation/Editors/DecimalUpDownEditor.cs
rename to ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/AutoSelectTextBoxView.xaml.cs
index b778bcfc..6a7dc340 100644
--- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/PropertyGrid/Implementation/Editors/DecimalUpDownEditor.cs
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/AutoSelectTextBoxView.xaml.cs
@@ -17,18 +17,20 @@
**********************************************************************/
-namespace Xceed.Wpf.Toolkit.PropertyGrid.Editors
+using Microsoft.Practices.Prism.Regions;
+using Samples.Infrastructure.Controls;
+
+namespace Samples.Modules.Text.Views
{
- public class DecimalUpDownEditor : TypeEditor
+ ///
+ /// Interaction logic for WatermarkTextBoxView.xaml
+ ///
+ [RegionMemberLifetime( KeepAlive = false )]
+ public partial class AutoSelectTextBoxView : DemoView
{
- protected override void SetControlProperties()
- {
- Editor.BorderThickness = new System.Windows.Thickness( 0 );
- }
-
- protected override void SetValueDependencyProperty()
+ public AutoSelectTextBoxView()
{
- ValueProperty = DecimalUpDown.ValueProperty;
+ InitializeComponent();
}
}
}
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/MaskedTextBoxView.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/MaskedTextBoxView.xaml
index b1171869..6aba50d4 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/MaskedTextBoxView.xaml
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/MaskedTextBoxView.xaml
@@ -21,6 +21,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:sample="clr-namespace:Samples.Infrastructure.Controls;assembly=Samples.Infrastructure"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
+ xmlns:osb="clr-namespace:Xceed.Wpf.Toolkit.Obselete;assembly=WPFToolkit.Extended"
+ xmlns:s="clr-namespace:System;assembly=mscorlib"
Title="MaskedTextBox"
Description="The MaskedTextBox control lets you display and edit values based on a mask.">
@@ -43,10 +45,12 @@
-
+
-
+
@@ -60,23 +64,25 @@
-
+
-
+
-
+
-
+
+
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/MaskedTextBoxView.xaml.cs b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/MaskedTextBoxView.xaml.cs
index 8663bd8e..6d9ff25b 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/MaskedTextBoxView.xaml.cs
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/MaskedTextBoxView.xaml.cs
@@ -31,6 +31,12 @@ namespace Samples.Modules.Text.Views
public MaskedTextBoxView()
{
InitializeComponent();
+ _mask.TextChanged += new System.Windows.Controls.TextChangedEventHandler( _mask_TextChanged );
+ }
+
+ void _mask_TextChanged( object sender, System.Windows.Controls.TextChangedEventArgs e )
+ {
+ _maskedTextBox.Value = null;
}
}
}
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/MultiLineTextEditor.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/MultiLineTextEditor.xaml
index 3ef1a469..70dc3365 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/MultiLineTextEditor.xaml
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/MultiLineTextEditor.xaml
@@ -16,6 +16,7 @@
Visit http://xceed.com and follow @datagrid on Twitter.
********************************************************************-->
+
+ Description="The MultiLineTextEditor is a TextBox that allows you to edit text that is too long to display in a regular TextBox. The popup is resizable to accommodate any text.">
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/NavigationView.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/NavigationView.xaml
index 37538b50..e18d7a31 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/NavigationView.xaml
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/NavigationView.xaml
@@ -20,12 +20,14 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:views="clr-namespace:Samples.Modules.Text.Views"
- Header="Text" Tag="{x:Type views:HomeView}">
+ Header="Text" Tag="{x:Type views:HomeView}"
+ Style="{StaticResource newFeature}" >
+
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/RichTextBoxView.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/RichTextBoxView.xaml
index aaca4a79..7ca77786 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/RichTextBoxView.xaml
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Modules/Samples.Modules.Text/Views/RichTextBoxView.xaml
@@ -16,6 +16,7 @@
Visit http://xceed.com and follow @datagrid on Twitter.
********************************************************************-->
+
+ Description="The RichTextBox is a TextBox that allows you to edit formatted text based on TextFormatters. The RichTextBoxFormatBar is a contextual formatting toolbar that mimics the behavior of the Microsoft Office 2010 formatting bar on selection.">
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Samples/App.config b/ExtendedWPFToolkitSolution/Src/Samples/Samples/App.config
index 69603068..68914990 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Samples/App.config
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Samples/App.config
@@ -10,6 +10,7 @@
+
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Samples/Resources/TreeViewStyles.xaml b/ExtendedWPFToolkitSolution/Src/Samples/Samples/Resources/TreeViewStyles.xaml
index f9e870fe..74e50651 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Samples/Resources/TreeViewStyles.xaml
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Samples/Resources/TreeViewStyles.xaml
@@ -246,4 +246,18 @@
+
+
+
diff --git a/ExtendedWPFToolkitSolution/Src/Samples/Samples/Samples.csproj b/ExtendedWPFToolkitSolution/Src/Samples/Samples/Samples.csproj
index 21cf80f7..b37c4d80 100644
--- a/ExtendedWPFToolkitSolution/Src/Samples/Samples/Samples.csproj
+++ b/ExtendedWPFToolkitSolution/Src/Samples/Samples/Samples.csproj
@@ -87,6 +87,10 @@
+
+ {72E591D6-8F83-4D8C-8F67-9C325E623234}
+ WPFToolkit.Extended
+
{A4A049A4-665A-4651-9046-7D06E9D0CCDC}
Samples.Infrastructure
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AssemblyVersionInfo.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AssemblyVersionInfo.cs
index 74011485..45843ae6 100644
--- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AssemblyVersionInfo.cs
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AssemblyVersionInfo.cs
@@ -22,7 +22,7 @@
internal static class _XceedVersionInfo
{
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields" )]
- public const string BaseVersion = "1.7";
+ public const string BaseVersion = "1.8";
[System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields" )]
public const string Version = BaseVersion +
_XceedVersionInfoCommon.Build;
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AutoSelectTextBox/Implementation/AutoSelectBehaviorEnum.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AutoSelectTextBox/Implementation/AutoSelectBehaviorEnum.cs
new file mode 100644
index 00000000..2340995d
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AutoSelectTextBox/Implementation/AutoSelectBehaviorEnum.cs
@@ -0,0 +1,31 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Xceed.Wpf.Toolkit
+{
+ public enum AutoSelectBehavior
+ {
+ Never,
+ OnFocus
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AutoSelectTextBox/Implementation/AutoSelectTextBox.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AutoSelectTextBox/Implementation/AutoSelectTextBox.cs
new file mode 100644
index 00000000..87aa6d0b
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AutoSelectTextBox/Implementation/AutoSelectTextBox.cs
@@ -0,0 +1,301 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows;
+using System.Windows.Automation;
+using Xceed.Wpf.Toolkit.Core.Utilities;
+
+namespace Xceed.Wpf.Toolkit
+{
+ public class AutoSelectTextBox : TextBox
+ {
+ static AutoSelectTextBox()
+ {
+ AutomationProperties.AutomationIdProperty.OverrideMetadata( typeof( AutoSelectTextBox ), new UIPropertyMetadata( "AutoSelectTextBox" ) );
+ }
+
+ #region AutoSelectBehavior PROPERTY
+
+ public AutoSelectBehavior AutoSelectBehavior
+ {
+ get
+ {
+ return ( AutoSelectBehavior )GetValue( AutoSelectBehaviorProperty );
+ }
+ set
+ {
+ SetValue( AutoSelectBehaviorProperty, value );
+ }
+ }
+
+ public static readonly DependencyProperty AutoSelectBehaviorProperty =
+ DependencyProperty.Register( "AutoSelectBehavior", typeof( AutoSelectBehavior ), typeof( AutoSelectTextBox ),
+ new UIPropertyMetadata( AutoSelectBehavior.Never ) );
+
+ #endregion AutoSelectBehavior PROPERTY
+
+ #region AutoMoveFocus PROPERTY
+
+ public bool AutoMoveFocus
+ {
+ get
+ {
+ return ( bool )GetValue( AutoMoveFocusProperty );
+ }
+ set
+ {
+ SetValue( AutoMoveFocusProperty, value );
+ }
+ }
+
+ public static readonly DependencyProperty AutoMoveFocusProperty =
+ DependencyProperty.Register( "AutoMoveFocus", typeof( bool ), typeof( AutoSelectTextBox ), new UIPropertyMetadata( false ) );
+
+ #endregion AutoMoveFocus PROPERTY
+
+ #region QueryMoveFocus EVENT
+
+ public static readonly RoutedEvent QueryMoveFocusEvent = EventManager.RegisterRoutedEvent( "QueryMoveFocus",
+ RoutingStrategy.Bubble,
+ typeof( QueryMoveFocusEventHandler ),
+ typeof( AutoSelectTextBox ) );
+ #endregion QueryMoveFocus EVENT
+
+ protected override void OnPreviewKeyDown( KeyEventArgs e )
+ {
+ if( !this.AutoMoveFocus )
+ {
+ base.OnPreviewKeyDown( e );
+ return;
+ }
+
+ if( ( e.Key == Key.Left )
+ && ( ( Keyboard.Modifiers == ModifierKeys.None )
+ || ( Keyboard.Modifiers == ModifierKeys.Control ) ) )
+ {
+ e.Handled = this.MoveFocusLeft();
+ }
+
+ if( ( e.Key == Key.Right )
+ && ( ( Keyboard.Modifiers == ModifierKeys.None )
+ || ( Keyboard.Modifiers == ModifierKeys.Control ) ) )
+ {
+ e.Handled = this.MoveFocusRight();
+ }
+
+ if( ( ( e.Key == Key.Up ) || ( e.Key == Key.PageUp ) )
+ && ( ( Keyboard.Modifiers == ModifierKeys.None )
+ || ( Keyboard.Modifiers == ModifierKeys.Control ) ) )
+ {
+ e.Handled = this.MoveFocusUp();
+ }
+
+ if( ( ( e.Key == Key.Down ) || ( e.Key == Key.PageDown ) )
+ && ( ( Keyboard.Modifiers == ModifierKeys.None )
+ || ( Keyboard.Modifiers == ModifierKeys.Control ) ) )
+ {
+ e.Handled = this.MoveFocusDown();
+ }
+
+ base.OnPreviewKeyDown( e );
+ }
+
+ protected override void OnPreviewGotKeyboardFocus( KeyboardFocusChangedEventArgs e )
+ {
+ base.OnPreviewGotKeyboardFocus( e );
+
+ if( this.AutoSelectBehavior == AutoSelectBehavior.OnFocus )
+ {
+ // If the focus was not in one of our child ( or popup ), we select all the text.
+ if( !TreeHelper.IsDescendantOf( e.OldFocus as DependencyObject, this ) )
+ this.SelectAll();
+ }
+ }
+
+ protected override void OnPreviewMouseLeftButtonDown( MouseButtonEventArgs e )
+ {
+ base.OnPreviewMouseLeftButtonDown( e );
+
+ if( this.AutoSelectBehavior == AutoSelectBehavior.Never )
+ return;
+
+ if( this.IsKeyboardFocusWithin == false )
+ {
+ this.Focus();
+ e.Handled = true;
+ }
+ }
+
+ protected override void OnTextChanged( TextChangedEventArgs e )
+ {
+ base.OnTextChanged( e );
+
+ if( !this.AutoMoveFocus )
+ return;
+
+ if( ( this.Text.Length != 0 )
+ && ( this.Text.Length == this.MaxLength )
+ && ( this.CaretIndex == this.MaxLength ) )
+ {
+ if( this.CanMoveFocus( FocusNavigationDirection.Right, true ) == true )
+ {
+ FocusNavigationDirection direction = ( this.FlowDirection == FlowDirection.LeftToRight )
+ ? FocusNavigationDirection.Right
+ : FocusNavigationDirection.Left;
+
+ this.MoveFocus( new TraversalRequest( direction ) );
+ }
+ }
+ }
+
+ private bool CanMoveFocus( FocusNavigationDirection direction, bool reachedMax )
+ {
+ QueryMoveFocusEventArgs e = new QueryMoveFocusEventArgs( direction, reachedMax );
+ this.RaiseEvent( e );
+ return e.CanMoveFocus;
+ }
+
+ private bool MoveFocusLeft()
+ {
+ if( this.FlowDirection == FlowDirection.LeftToRight )
+ {
+ //occurs only if the cursor is at the beginning of the text
+ if( ( this.CaretIndex == 0 ) && ( this.SelectionLength == 0 ) )
+ {
+ if( ComponentCommands.MoveFocusBack.CanExecute( null, this ) )
+ {
+ ComponentCommands.MoveFocusBack.Execute( null, this );
+ return true;
+ }
+ else if( this.CanMoveFocus( FocusNavigationDirection.Left, false ) )
+ {
+ this.MoveFocus( new TraversalRequest( FocusNavigationDirection.Left ) );
+ return true;
+ }
+ }
+ }
+ else
+ {
+ //occurs only if the cursor is at the end of the text
+ if( ( this.CaretIndex == this.Text.Length ) && ( this.SelectionLength == 0 ) )
+ {
+ if( ComponentCommands.MoveFocusBack.CanExecute( null, this ) )
+ {
+ ComponentCommands.MoveFocusBack.Execute( null, this );
+ return true;
+ }
+ else if( this.CanMoveFocus( FocusNavigationDirection.Left, false ) )
+ {
+ this.MoveFocus( new TraversalRequest( FocusNavigationDirection.Left ) );
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private bool MoveFocusRight()
+ {
+ if( this.FlowDirection == FlowDirection.LeftToRight )
+ {
+ //occurs only if the cursor is at the beginning of the text
+ if( ( this.CaretIndex == this.Text.Length ) && ( this.SelectionLength == 0 ) )
+ {
+ if( ComponentCommands.MoveFocusForward.CanExecute( null, this ) )
+ {
+ ComponentCommands.MoveFocusForward.Execute( null, this );
+ return true;
+ }
+ else if( this.CanMoveFocus( FocusNavigationDirection.Right, false ) )
+ {
+ this.MoveFocus( new TraversalRequest( FocusNavigationDirection.Right ) );
+ return true;
+ }
+ }
+ }
+ else
+ {
+ //occurs only if the cursor is at the end of the text
+ if( ( this.CaretIndex == 0 ) && ( this.SelectionLength == 0 ) )
+ {
+ if( ComponentCommands.MoveFocusForward.CanExecute( null, this ) )
+ {
+ ComponentCommands.MoveFocusForward.Execute( null, this );
+ return true;
+ }
+ else if( this.CanMoveFocus( FocusNavigationDirection.Right, false ) )
+ {
+ this.MoveFocus( new TraversalRequest( FocusNavigationDirection.Right ) );
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ private bool MoveFocusUp()
+ {
+ int lineNumber = this.GetLineIndexFromCharacterIndex( this.SelectionStart );
+
+ //occurs only if the cursor is on the first line
+ if( lineNumber == 0 )
+ {
+ if( ComponentCommands.MoveFocusUp.CanExecute( null, this ) )
+ {
+ ComponentCommands.MoveFocusUp.Execute( null, this );
+ return true;
+ }
+ else if( this.CanMoveFocus( FocusNavigationDirection.Up, false ) )
+ {
+ this.MoveFocus( new TraversalRequest( FocusNavigationDirection.Up ) );
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private bool MoveFocusDown()
+ {
+ int lineNumber = this.GetLineIndexFromCharacterIndex( this.SelectionStart );
+
+ //occurs only if the cursor is on the first line
+ if( lineNumber == ( this.LineCount - 1 ) )
+ {
+ if( ComponentCommands.MoveFocusDown.CanExecute( null, this ) )
+ {
+ ComponentCommands.MoveFocusDown.Execute( null, this );
+ return true;
+ }
+ else if( this.CanMoveFocus( FocusNavigationDirection.Down, false ) )
+ {
+ this.MoveFocus( new TraversalRequest( FocusNavigationDirection.Down ) );
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
+
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AutoSelectTextBox/Implementation/QueryMoveFocusEventArgs.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AutoSelectTextBox/Implementation/QueryMoveFocusEventArgs.cs
new file mode 100644
index 00000000..b77d1772
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/AutoSelectTextBox/Implementation/QueryMoveFocusEventArgs.cs
@@ -0,0 +1,76 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System.Windows;
+using System.Windows.Input;
+
+namespace Xceed.Wpf.Toolkit
+{
+ [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Design", "CA1003:UseGenericEventHandlerInstances" )]
+ public delegate void QueryMoveFocusEventHandler( object sender, QueryMoveFocusEventArgs e );
+
+ public class QueryMoveFocusEventArgs : RoutedEventArgs
+ {
+ //default CTOR private to prevent its usage.
+ private QueryMoveFocusEventArgs()
+ {
+ }
+
+ //internal to prevent anybody from building this type of event.
+ internal QueryMoveFocusEventArgs( FocusNavigationDirection direction, bool reachedMaxLength )
+ : base( AutoSelectTextBox.QueryMoveFocusEvent )
+ {
+ m_navigationDirection = direction;
+ m_reachedMaxLength = reachedMaxLength;
+ }
+
+ public FocusNavigationDirection FocusNavigationDirection
+ {
+ get
+ {
+ return m_navigationDirection;
+ }
+ }
+
+ public bool ReachedMaxLength
+ {
+ get
+ {
+ return m_reachedMaxLength;
+ }
+ }
+
+ public bool CanMoveFocus
+ {
+ get
+ {
+ return m_canMove;
+ }
+ set
+ {
+ m_canMove = value;
+ }
+ }
+
+ private FocusNavigationDirection m_navigationDirection;
+ private bool m_reachedMaxLength;
+ private bool m_canMove = true; //defaults to true... if nobody does nothing, then its capable of moving focus.
+
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/CheckComboBox/Implementation/CheckComboBox.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/CheckComboBox/Implementation/CheckComboBox.cs
index be17afce..1b06dc3c 100644
--- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/CheckComboBox/Implementation/CheckComboBox.cs
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/CheckComboBox/Implementation/CheckComboBox.cs
@@ -22,11 +22,14 @@ using System.Linq;
using System.Windows;
using System.Windows.Input;
using Xceed.Wpf.Toolkit.Primitives;
+using Xceed.Wpf.Toolkit.Core.Utilities;
namespace Xceed.Wpf.Toolkit
{
public class CheckComboBox : Selector
{
+ private ValueChangeHelper _displayMemberPathValuesChangeHelper;
+
#region Constructors
static CheckComboBox()
@@ -37,6 +40,7 @@ namespace Xceed.Wpf.Toolkit
public CheckComboBox()
{
Mouse.AddPreviewMouseDownOutsideCapturedElementHandler( this, OnMouseDownOutsideCapturedElement );
+ _displayMemberPathValuesChangeHelper = new ValueChangeHelper( this.OnDisplayMemberPathValuesChanged );
}
#endregion //Constructors
@@ -98,7 +102,13 @@ namespace Xceed.Wpf.Toolkit
protected override void OnDisplayMemberPathChanged( string oldDisplayMemberPath, string newDisplayMemberPath )
{
base.OnDisplayMemberPathChanged( oldDisplayMemberPath, newDisplayMemberPath );
- UpdateText();
+ this.UpdateDisplayMemberPathValuesBindings();
+ }
+
+ protected override void OnItemsSourceChanged( System.Collections.IEnumerable oldValue, System.Collections.IEnumerable newValue )
+ {
+ base.OnItemsSourceChanged( oldValue, newValue );
+ this.UpdateDisplayMemberPathValuesBindings();
}
#endregion //Base Class Overrides
@@ -114,6 +124,16 @@ namespace Xceed.Wpf.Toolkit
#region Methods
+ private void UpdateDisplayMemberPathValuesBindings()
+ {
+ _displayMemberPathValuesChangeHelper.UpdateValueSource( ItemsCollection, this.DisplayMemberPath );
+ }
+
+ private void OnDisplayMemberPathValuesChanged()
+ {
+ this.UpdateText();
+ }
+
private void UpdateText()
{
#if VS2008
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/ColorCanvas/Implementation/ColorCanvas.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/ColorCanvas/Implementation/ColorCanvas.cs
index 01a0031c..d0303af0 100644
--- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/ColorCanvas/Implementation/ColorCanvas.cs
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/ColorCanvas/Implementation/ColorCanvas.cs
@@ -25,17 +25,20 @@ using System.Windows.Media;
using Xceed.Wpf.Toolkit.Core.Utilities;
using Xceed.Wpf.Toolkit.Primitives;
using System.IO;
+using System;
namespace Xceed.Wpf.Toolkit
{
[TemplatePart( Name = PART_ColorShadingCanvas, Type = typeof( Canvas ) )]
[TemplatePart( Name = PART_ColorShadeSelector, Type = typeof( Canvas ) )]
[TemplatePart( Name = PART_SpectrumSlider, Type = typeof( ColorSpectrumSlider ) )]
+ [TemplatePart( Name = PART_HexadecimalTextBox, Type = typeof( TextBox ) )]
public class ColorCanvas : Control
{
private const string PART_ColorShadingCanvas = "PART_ColorShadingCanvas";
private const string PART_ColorShadeSelector = "PART_ColorShadeSelector";
private const string PART_SpectrumSlider = "PART_SpectrumSlider";
+ private const string PART_HexadecimalTextBox = "PART_HexadecimalTextBox";
#region Private Members
@@ -43,6 +46,7 @@ namespace Xceed.Wpf.Toolkit
private Canvas _colorShadingCanvas;
private Canvas _colorShadeSelector;
private ColorSpectrumSlider _spectrumSlider;
+ private TextBox _hexadecimalTextBox;
private Point? _currentColorPosition;
private bool _surpressPropertyChanged;
@@ -74,7 +78,7 @@ namespace Xceed.Wpf.Toolkit
protected virtual void OnSelectedColorChanged( Color oldValue, Color newValue )
{
- HexadecimalString = GetFormatedColorString( newValue );
+ SetHexadecimalStringProperty( GetFormatedColorString( newValue ), false );
UpdateRGBValues( newValue );
UpdateColorShadeSelectorPosition( newValue );
@@ -237,6 +241,8 @@ namespace Xceed.Wpf.Toolkit
string currentColorString = GetFormatedColorString( SelectedColor );
if( !currentColorString.Equals( newColorString ) )
UpdateSelectedColor( ( Color )ColorConverter.ConvertFromString( newColorString ) );
+
+ SetHexadecimalTextBoxTextProperty( newValue );
}
private static object OnCoerceHexadecimalString( DependencyObject d, object basevalue )
@@ -251,16 +257,19 @@ namespace Xceed.Wpf.Toolkit
private object OnCoerceHexadecimalString( object newValue )
{
var value = newValue as string;
+ string retValue = value;
+
try
{
ColorConverter.ConvertFromString( value );
}
catch
{
+ //When HexadecimalString is changed via Code-Behind and hexadecimal format is bad, throw.
throw new InvalidDataException( "Color provided is not in the correct format." );
}
- return value;
+ return retValue;
}
#endregion //HexadecimalString
@@ -289,7 +298,7 @@ namespace Xceed.Wpf.Toolkit
protected virtual void OnUsingAlphaChannelChanged()
{
- HexadecimalString = GetFormatedColorString( SelectedColor );
+ SetHexadecimalStringProperty( GetFormatedColorString( SelectedColor ), false );
}
#endregion //UsingAlphaChannel
@@ -342,17 +351,31 @@ namespace Xceed.Wpf.Toolkit
if( _spectrumSlider != null )
_spectrumSlider.ValueChanged += SpectrumSlider_ValueChanged;
+ if( _hexadecimalTextBox != null )
+ _hexadecimalTextBox.LostFocus -= new RoutedEventHandler( HexadecimalTextBox_LostFocus );
+
+ _hexadecimalTextBox = GetTemplateChild( PART_HexadecimalTextBox ) as TextBox;
+
+ if( _hexadecimalTextBox != null )
+ _hexadecimalTextBox.LostFocus += new RoutedEventHandler( HexadecimalTextBox_LostFocus );
+
UpdateRGBValues( SelectedColor );
UpdateColorShadeSelectorPosition( SelectedColor );
+
+ // When changing theme, HexadecimalString needs to be set since it is not binded.
+ SetHexadecimalTextBoxTextProperty( GetFormatedColorString( SelectedColor ) );
}
- protected override void OnPreviewKeyDown( KeyEventArgs e )
+ protected override void OnKeyDown( KeyEventArgs e )
{
- //hitting enter on textbox will update value of underlying source
+ base.OnKeyDown( e );
+
+ //hitting enter on textbox will update Hexadecimal string
if( e.Key == Key.Enter && e.OriginalSource is TextBox )
{
- BindingExpression be = ( ( TextBox )e.OriginalSource ).GetBindingExpression( TextBox.TextProperty );
- be.UpdateSource();
+ TextBox textBox = ( TextBox )e.OriginalSource;
+ if( textBox.Name == PART_HexadecimalTextBox )
+ SetHexadecimalStringProperty( textBox.Text, true );
}
}
@@ -404,6 +427,12 @@ namespace Xceed.Wpf.Toolkit
}
}
+ void HexadecimalTextBox_LostFocus( object sender, RoutedEventArgs e )
+ {
+ TextBox textbox = sender as TextBox;
+ SetHexadecimalStringProperty( textbox.Text, true );
+ }
+
#endregion //Event Handlers
#region Events
@@ -503,7 +532,7 @@ namespace Xceed.Wpf.Toolkit
var currentColor = ColorUtilities.ConvertHsvToRgb( hsv.H, hsv.S, hsv.V );
currentColor.A = A;
SelectedColor = currentColor;
- HexadecimalString = GetFormatedColorString( SelectedColor );
+ SetHexadecimalStringProperty( GetFormatedColorString( SelectedColor ), false );
}
private string GetFormatedColorString( Color colorToFormat )
@@ -516,6 +545,34 @@ namespace Xceed.Wpf.Toolkit
return ColorUtilities.FormatColorString( stringToFormat, UsingAlphaChannel );
}
+ private void SetHexadecimalStringProperty( string newValue, bool modifyFromUI )
+ {
+ if( modifyFromUI )
+ {
+ try
+ {
+ ColorConverter.ConvertFromString( newValue );
+ HexadecimalString = newValue;
+ }
+ catch
+ {
+ //When HexadecimalString is changed via UI and hexadecimal format is bad, keep the previous HexadecimalString.
+ SetHexadecimalTextBoxTextProperty( HexadecimalString );
+ }
+ }
+ else
+ {
+ //When HexadecimalString is changed via Code-Behind, hexadecimal format will be evaluated in OnCoerceHexadecimalString()
+ HexadecimalString = newValue;
+ }
+ }
+
+ private void SetHexadecimalTextBoxTextProperty( string newValue )
+ {
+ if( _hexadecimalTextBox != null )
+ _hexadecimalTextBox.Text = newValue;
+ }
+
#endregion //Methods
}
}
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/ColorCanvas/Themes/Generic.xaml b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/ColorCanvas/Themes/Generic.xaml
index 7164d0a0..4bb44947 100644
--- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/ColorCanvas/Themes/Generic.xaml
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/ColorCanvas/Themes/Generic.xaml
@@ -260,7 +260,7 @@
-
+
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/ColorPicker/Implementation/ColorPicker.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/ColorPicker/Implementation/ColorPicker.cs
index 42aca48a..899743e0 100644
--- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/ColorPicker/Implementation/ColorPicker.cs
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/ColorPicker/Implementation/ColorPicker.cs
@@ -343,7 +343,7 @@ namespace Xceed.Wpf.Toolkit
{
return ( bool )GetValue( UsingAlphaChannelProperty );
}
- protected set
+ set
{
SetValue( UsingAlphaChannelProperty, value );
}
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Converters/WizardPageButtonVisibilityConverter.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Converters/WizardPageButtonVisibilityConverter.cs
index 1c8bc7a9..317fafbf 100644
--- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Converters/WizardPageButtonVisibilityConverter.cs
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Converters/WizardPageButtonVisibilityConverter.cs
@@ -28,7 +28,9 @@ namespace Xceed.Wpf.Toolkit.Core.Converters
public object Convert( object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture )
{
Visibility wizardVisibility = ( Visibility )values[ 0 ];
- WizardPageButtonVisibility wizardPageVisibility = ( WizardPageButtonVisibility )values[ 1 ];
+ WizardPageButtonVisibility wizardPageVisibility = ( (values[ 1 ] == null) || (values[ 1 ] == DependencyProperty.UnsetValue) )
+ ? WizardPageButtonVisibility.Hidden
+ : ( WizardPageButtonVisibility )values[ 1 ];
Visibility visibility = Visibility.Visible;
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Input/IValidateInput.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Input/IValidateInput.cs
new file mode 100644
index 00000000..15d600da
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Input/IValidateInput.cs
@@ -0,0 +1,32 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Xceed.Wpf.Toolkit.Core.Input
+{
+ public interface IValidateInput
+ {
+ event InputValidationErrorEventHandler InputValidationError;
+ void CommitInput();
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Input/InputValidationErrorEventArgs.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Input/InputValidationErrorEventArgs.cs
new file mode 100644
index 00000000..312c90ce
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Input/InputValidationErrorEventArgs.cs
@@ -0,0 +1,46 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Xceed.Wpf.Toolkit.Core.Input
+{
+ public delegate void InputValidationErrorEventHandler( object sender, InputValidationErrorEventArgs e );
+
+ public class InputValidationErrorEventArgs : EventArgs
+ {
+ public InputValidationErrorEventArgs( string errorMsg )
+ {
+ _errorMessage = errorMsg;
+ }
+
+ public string ErrorMessage
+ {
+ get
+ {
+ return _errorMessage;
+ }
+ }
+
+ private string _errorMessage;
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/CachedTextInfo.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/CachedTextInfo.cs
new file mode 100644
index 00000000..d887ffb8
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/CachedTextInfo.cs
@@ -0,0 +1,57 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Controls;
+
+namespace Xceed.Wpf.Toolkit.Primitives
+{
+ internal class CachedTextInfo : ICloneable
+ {
+ private CachedTextInfo( string text, int caretIndex, int selectionStart, int selectionLength )
+ {
+ this.Text = text;
+ this.CaretIndex = caretIndex;
+ this.SelectionStart = selectionStart;
+ this.SelectionLength = selectionLength;
+ }
+
+ public CachedTextInfo( TextBox textBox )
+ : this( textBox.Text, textBox.CaretIndex, textBox.SelectionStart, textBox.SelectionLength )
+ {
+ }
+
+ public string Text { get; private set; }
+ public int CaretIndex { get; private set; }
+ public int SelectionStart { get; private set; }
+ public int SelectionLength { get; private set; }
+
+ #region ICloneable Members
+
+ public object Clone()
+ {
+ return new CachedTextInfo( this.Text, this.CaretIndex, this.SelectionStart, this.SelectionLength );
+ }
+
+ #endregion
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/Selector.cs b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/Selector.cs
index f8cfcd3f..12c902f4 100644
--- a/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/Selector.cs
+++ b/ExtendedWPFToolkitSolution/Src/WPFToolkit.Extended/Core/Primitives/Selector.cs
@@ -26,16 +26,28 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
+using System.Collections.Generic;
+using System.Collections.Specialized;
+using System.Reflection;
+using Xceed.Wpf.Toolkit.Core.Utilities;
namespace Xceed.Wpf.Toolkit.Primitives
{
- public class Selector : ItemsControl //should probably make this control an ICommandSource
+ public class Selector : ItemsControl, IWeakEventListener //should probably make this control an ICommandSource
{
#region Members
- private bool _ignoreSetSelectedValue;
- private bool _surpressSelectionChanged;
- private bool _surpressSelectedValueChanged;
+ private bool _surpressItemSelectionChanged;
+ private bool _ignoreSelectedItemChanged;
+ private bool _ignoreSelectedValueChanged;
+ private int _ignoreSelectedItemsCollectionChanged;
+ private int _ignoreSelectedMemberPathValuesChanged;
+ private IList _selectedItems;
+
+ private ValueChangeHelper _selectedMemberPathValuesHelper;
+ private ValueChangeHelper _valueMemberPathValuesHelper;
+
+
#endregion //Members
@@ -43,9 +55,11 @@ namespace Xceed.Wpf.Toolkit.Primitives
public Selector()
{
- SelectedItems = new ObservableCollection
+
+
+
+
+
+
+
+
+
+
+
+Here, you can test various expression that will be parsed.
+The result of the parsing will be shown in the second TextBox.
+Don't forget to check the Output Window for errors that could have occured during the
+automated tests. Untested expressions by the automated tests should be added when found.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Invariant
+ fr-FR
+ en-US
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ";
+
+ this.Title = "Expression Parser Test Window";
+ this.Width = 600;
+ this.Height = 400;
+ this.MinHeight = 224;
+ this.MinWidth = 467;
+ this.ResizeMode = ResizeMode.CanResizeWithGrip;
+
+ StringReader strReader = new StringReader( mainGridXaml );
+ XmlTextReader xmlReader = new XmlTextReader( strReader );
+ this.Content = XamlReader.Load( xmlReader ) as UIElement;
+
+ Button button = ( ( Grid )this.Content ).FindName( "closeButton" ) as Button;
+
+ if( button != null )
+ button.Click += new RoutedEventHandler( this.closeButton_Click );
+
+ m_parseButton = ( ( Grid )this.Content ).FindName( "parseButton" ) as Button;
+ m_parseButton.Click += new RoutedEventHandler( this.parseButton_Click );
+
+ m_runUnitTestsButton = ( ( Grid )this.Content ).FindName( "runUnitTestsButton" ) as Button;
+ m_runUnitTestsButton.Click += new RoutedEventHandler( this.runUnitTestsButton_Click );
+
+ m_dataTypeComboBox = ( ( Grid )this.Content ).FindName( "dataTypeComboBox" ) as ComboBox;
+ m_cultureComboBox = ( ( Grid )this.Content ).FindName( "cultureComboBox" ) as ComboBox;
+ m_expressionTextBox = ( ( Grid )this.Content ).FindName( "expressionTextBox" ) as TextBox;
+ m_resultTextBox = ( ( Grid )this.Content ).FindName( "resultTextBox" ) as TextBox;
+ }
+
+ private void parseButton_Click( object sender, RoutedEventArgs e )
+ {
+ CultureInfo culture = null;
+ string invalidCultureErrorText = null;
+ string cultureName = m_cultureComboBox.Text;
+
+ if( !string.IsNullOrEmpty( cultureName ) )
+ {
+ try
+ {
+ if( cultureName.ToLower() == "invariant" )
+ {
+ culture = CultureInfo.InvariantCulture;
+ }
+ else
+ {
+ culture = CultureInfo.GetCultureInfo( cultureName );
+ }
+ }
+ catch( Exception ex )
+ {
+ invalidCultureErrorText = cultureName + " is an invalid culture : " + ex.Message;
+ }
+ }
+
+ FilterCriterion criterion = FilterParser.TryParse( m_expressionTextBox.Text, m_dataTypeComboBox.SelectedValue as Type, culture );
+
+ if( criterion == null )
+ {
+ m_resultTextBox.Text = "Error while parsing" + Environment.NewLine +
+ FilterParser.LastError;
+ }
+ else
+ {
+ m_resultTextBox.Text = criterion.ToString() + Environment.NewLine +
+ Environment.NewLine +
+ "Normalized expression:" + Environment.NewLine +
+ criterion.ToExpression( culture );
+ }
+
+ if( !string.IsNullOrEmpty( invalidCultureErrorText ) )
+ {
+ m_resultTextBox.Text += Environment.NewLine + Environment.NewLine + invalidCultureErrorText;
+ }
+ }
+
+ private void runUnitTestsButton_Click( object sender, RoutedEventArgs e )
+ {
+ m_runUnitTestsButton.IsEnabled = false;
+ m_parseButton.IsEnabled = false;
+ m_resultTextBox.Text = "";
+
+ ThreadedUnitTester unitTester = new ThreadedUnitTester( new Action( this.UnitTestsCompletedCallback ), new Action( this.LogMessageCallback ) );
+ Thread unitTestThread = new Thread( new ThreadStart( unitTester.RunTests ) );
+
+ unitTestThread.Start();
+ }
+
+ private void UnitTestsCompletedCallback()
+ {
+ this.LogMessageCallback( "Unit tests completed." );
+
+ this.Dispatcher.BeginInvoke( new Action( delegate
+ {
+ m_runUnitTestsButton.IsEnabled = true;
+ m_parseButton.IsEnabled = true;
+ } ) );
+ }
+
+ private void LogMessageCallback( string message )
+ {
+ this.Dispatcher.BeginInvoke( new Action( delegate
+ {
+ m_resultTextBox.Text += message + Environment.NewLine;
+ m_resultTextBox.ScrollToEnd();
+ } ) );
+ }
+
+ private void closeButton_Click( object sender, RoutedEventArgs e )
+ {
+ this.Close();
+ }
+
+ private TextBox m_expressionTextBox;
+ private TextBox m_resultTextBox;
+ private ComboBox m_dataTypeComboBox;
+ private ComboBox m_cultureComboBox;
+ private Button m_parseButton;
+ private Button m_runUnitTestsButton;
+
+ #region ThreadedUnitTester Nested Type
+
+ private class ThreadedUnitTester
+ {
+ public ThreadedUnitTester( Action testCompletedCallback, Action logMessageCallback )
+ {
+ if( testCompletedCallback == null )
+ throw new ArgumentNullException( "testCompletedCallback" );
+
+ if( logMessageCallback == null )
+ throw new ArgumentNullException( "logMessageCallback" );
+
+ m_testCompletedCallback = testCompletedCallback;
+ m_logMessageCallback = logMessageCallback;
+ }
+
+ public void RunTests()
+ {
+ if( FilterParser.LogMessageCallback != null )
+ throw new InvalidOperationException( "Unit tests already running." );
+
+ try
+ {
+ FilterParser.LogMessageCallback = m_logMessageCallback;
+ FilterParser.TestQuoteParser();
+ FilterParser.TestCriterionBuilder();
+
+ if( m_testCompletedCallback != null )
+ m_testCompletedCallback();
+ }
+ finally
+ {
+ FilterParser.LogMessageCallback = null;
+ }
+ }
+
+ private Action m_testCompletedCallback = null;
+ private Action m_logMessageCallback = null;
+ }
+
+ #endregion ThreadedUnitTester Nested Type
+ }
+}
+
+#endif
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/FilterParser_UnitTests.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/FilterParser_UnitTests.cs
new file mode 100644
index 00000000..cff19c06
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/FilterParser_UnitTests.cs
@@ -0,0 +1,1465 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+#if DEBUG
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+
+using Xceed.Wpf.DataGrid.FilterCriteria;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal static partial class FilterParser
+ {
+ internal static Action LogMessageCallback
+ {
+ get
+ {
+ return s_logMessageCallback;
+ }
+
+ set
+ {
+ if( ( ( value == null ) && ( s_logMessageCallback != null ) ) ||
+ ( ( value != null ) && ( s_logMessageCallback == null ) ) )
+ {
+ s_logMessageCallback = value;
+ }
+ else
+ {
+ throw new InvalidOperationException( "Unit test already running." );
+ }
+ }
+ }
+
+ internal static void TestQuoteParser()
+ {
+ FilterParser.LogMessageCallback( "Testing quote parser..." );
+
+ FilterParser.CheckQuoteParserResult( "", new RawToken( "" ) );
+ FilterParser.CheckQuoteParserResult( "test", new RawToken( "test" ) );
+ FilterParser.CheckQuoteParserResult( "test test", new RawToken( "test test" ) );
+ FilterParser.CheckQuoteParserResult( "\"test\"", new AtomicStringToken( "test" ) );
+ FilterParser.CheckQuoteParserResult( "L'idéal du calme \"est dans\" un \"chat assis\"",
+ new RawToken( "L'idéal du calme " ),
+ new AtomicStringToken( "est dans" ),
+ new RawToken( " un " ),
+ new AtomicStringToken( "chat assis" ) );
+
+
+ FilterParser.CheckQuoteParserResult( "\"\"\"L'idéal du calme est dans un chat assis\"\"\"",
+ new AtomicStringToken( "\"L'idéal du calme est dans un chat assis\"" ) );
+
+
+ FilterParser.CheckQuoteParserResult( "L'idéal du calme \"est dans\" un \"\"\"chat assis\"",
+ new RawToken( "L'idéal du calme " ),
+ new AtomicStringToken( "est dans" ),
+ new RawToken( " un " ),
+ new AtomicStringToken( "\"chat assis" ) );
+
+ FilterParser.CheckQuoteParserResult( "L'idéal du calme \"est dans\" un \"\"\"chat assis\"\"\"",
+ new RawToken( "L'idéal du calme " ),
+ new AtomicStringToken( "est dans" ),
+ new RawToken( " un " ),
+ new AtomicStringToken( "\"chat assis\"" ) );
+ FilterParser.CheckQuoteParserResult( "\"\"", new AtomicStringToken( "" ) );
+ FilterParser.CheckQuoteParserResult( "\"\"\"\"\"\"", new AtomicStringToken( "\"\"" ) );
+ FilterParser.CheckQuoteParserResult( "\"\"\"\"\"\"\"\"", new AtomicStringToken( "\"\"\"" ) );
+ FilterParser.CheckQuoteParserResult( "\" \"\"\"\" \"", new AtomicStringToken( " \"\" " ) );
+ FilterParser.CheckQuoteParserResult( "\"\"\"", FilterParser.MissingClosingQuotesErrorText );
+ FilterParser.CheckQuoteParserResult( "\"\"L'idéal du calme est dans un chat assis\"\"", new AtomicStringToken( "" ),
+ new RawToken( "L'idéal du calme est dans un chat assis" ), new AtomicStringToken( "" ) );
+ FilterParser.CheckQuoteParserResult( "\"\"L'idéal du calme est dans un chat assis", new AtomicStringToken( "" ),
+ new RawToken( "L'idéal du calme est dans un chat assis" ) );
+ FilterParser.CheckQuoteParserResult( "\"\"\"L'idéal du calme est dans un chat assis\"", new AtomicStringToken( "\"L'idéal du calme est dans un chat assis" ) );
+ FilterParser.CheckQuoteParserResult( "\"\"\"\"L'idéal du calme est dans un chat assis\"", FilterParser.MissingClosingQuotesErrorText );
+
+ }
+
+ internal static void TestCriterionBuilder()
+ {
+ FilterParser.TestCriterionBuilderString();
+ FilterParser.TestCriterionBuilderChar();
+ FilterParser.TestCriterionBuilderDateTime();
+ FilterParser.TestCriterionBuilderNumber();
+ }
+
+ private static void TestCriterionBuilderString()
+ {
+ Type type = typeof( string );
+ FilterParser.LogMessageCallback( "Testing string filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ new ContainsFilterCriterion( "" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "=\"\"", type, null,
+ new EqualToFilterCriterion( "" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "*\"\"", type, null, new EndsWithFilterCriterion( "" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"\"*", type, null, new StartsWithFilterCriterion( "" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "*\"Test\"", type, null, new EndsWithFilterCriterion( "Test" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "Test", type, null,
+ new ContainsFilterCriterion( "Test" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "Not Test", type, null,
+ new ContainsFilterCriterion( "Not Test" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NOT", type, null,
+ new ContainsFilterCriterion( "NOT" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NOT ", type, null,
+ new ContainsFilterCriterion( "NOT" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "AND ", type, null,
+ new ContainsFilterCriterion( "AND" ) );
+
+ FilterParser.CheckCriterionBuilderResult( " AND ", type, null,
+ new ContainsFilterCriterion( "AND" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "OR OR OR OR", type, null,
+ new OrFilterCriterion(
+ new ContainsFilterCriterion( "OR" ),
+ new ContainsFilterCriterion( "OR OR" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NOT OR NOT", type, null,
+ new OrFilterCriterion(
+ new ContainsFilterCriterion( "NOT" ),
+ new ContainsFilterCriterion( "NOT" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NOT Test", type, null,
+ new NotFilterCriterion( new ContainsFilterCriterion( "Test" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<10 OR >50", type, null,
+ new OrFilterCriterion(
+ new LessThanFilterCriterion( "10" ),
+ new GreaterThanFilterCriterion( "50" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">=fou AND 10 ", type, null,
+ new AndFilterCriterion(
+ new ContainsFilterCriterion( "AND" ),
+ new GreaterThanFilterCriterion( "10" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "OR AND 10 ", type, null,
+ new AndFilterCriterion(
+ new ContainsFilterCriterion( "OR" ),
+ new ContainsFilterCriterion( "10" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "AND OR =10 ", type, null,
+ new OrFilterCriterion(
+ new ContainsFilterCriterion( "AND" ),
+ new EqualToFilterCriterion( "10" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"=10 AND 10 OR 10\"", type, null,
+ new ContainsFilterCriterion( "=10 AND 10 OR 10" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "=\"10 AND 10 OR 10\"", type, null,
+ new EqualToFilterCriterion( "10 AND 10 OR 10" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "=10 AND \"10 OR 10\"", type, null,
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( "10" ),
+ new ContainsFilterCriterion( "10 OR 10" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "*\"Test\" AND \"\"\"Test2\"*", type, null, new AndFilterCriterion( new EndsWithFilterCriterion( "Test" ), new StartsWithFilterCriterion( "\"Test2" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "*\"Test\" AND \"Test\"\"2\"\"\"*", type, null, new AndFilterCriterion( new EndsWithFilterCriterion( "Test" ), new StartsWithFilterCriterion( "Test\"2\"" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "*Test AND \"Test\"\"2\"\"\"*", type, null, new AndFilterCriterion( new EndsWithFilterCriterion( "Test" ), new StartsWithFilterCriterion( "Test\"2\"" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "*\" Test\" AND \"Test\"\"2\"\"\"*", type, null, new AndFilterCriterion( new EndsWithFilterCriterion( " Test" ), new StartsWithFilterCriterion( "Test\"2\"" ) ) );
+ FilterParser.CheckCriterionBuilderResult( "=10 AND \"10 OR 10\"", type, null,
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( "10" ),
+ new ContainsFilterCriterion( "10 OR 10" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new ContainsFilterCriterion( "NULL" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new ContainsFilterCriterion( "NULL" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "=NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "=\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "=Null", type, null,
+ new EqualToFilterCriterion( "Null" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"=10 AND <5 OR NOT >2\"", type, null,
+ new ContainsFilterCriterion( "=10 AND <5 OR NOT >2" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "=NULL OR =5", type, null,
+ new OrFilterCriterion(
+ new EqualToFilterCriterion( null ),
+ new EqualToFilterCriterion( "5" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( " =10 AND >5 OR 15 ", type, null,
+ new OrFilterCriterion(
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( "10" ),
+ new GreaterThanFilterCriterion( "5" ) ),
+ new ContainsFilterCriterion( "15" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "=NOT 10", type, null,
+ new EqualToFilterCriterion( "NOT 10" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "10 =20", type, null,
+ new ContainsFilterCriterion( "10 =20" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "cd", type, null,
+ new LessThanFilterCriterion( "ab >cd" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "= chat", type, null,
+ new EqualToFilterCriterion( "chat" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "= chat OR =chien ", type, null,
+ new OrFilterCriterion(
+ new EqualToFilterCriterion( "chat" ),
+ new EqualToFilterCriterion( "chien" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "=chat chien OR =lion", type, null,
+ new OrFilterCriterion(
+ new EqualToFilterCriterion( "chat chien" ),
+ new EqualToFilterCriterion( "lion" ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "=chat =chien", type, null,
+ new EqualToFilterCriterion( "chat =chien" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "*chien", type, null, new EndsWithFilterCriterion( "chien" ) );
+ FilterParser.CheckCriterionBuilderResult( "*chien OR chat*", type, null, new OrFilterCriterion( new EndsWithFilterCriterion( "chien" ), new StartsWithFilterCriterion( "chat" ) ) );
+ FilterParser.CheckCriterionBuilderResult( "*\"chien\" OR \"chat\"*", type, null, new OrFilterCriterion( new EndsWithFilterCriterion( "chien" ), new StartsWithFilterCriterion( "chat" ) ) );
+ FilterParser.CheckCriterionBuilderResult( "\"*chien\" OR \"chat\"*", type, null, new OrFilterCriterion( new ContainsFilterCriterion( "*chien" ), new StartsWithFilterCriterion( "chat" ) ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "=10 \"AND 10 OR 10\"", type, null,
+ FilterParser.InvalidExpressionErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "Johann \"Sebastian Bach\"", type, null,
+ FilterParser.InvalidExpressionErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( " \"Johann Sebastian\" Bach ", type, null,
+ FilterParser.InvalidExpressionErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "\"University Southern\" \"North Dakota\"", type, null,
+ FilterParser.InvalidExpressionErrorText );
+
+
+ }
+
+ private static void TestCriterionBuilderChar()
+ {
+ Type type = typeof( char );
+ FilterParser.LogMessageCallback( "Testing char filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "a", type, null,
+ new EqualToFilterCriterion( 'a' ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\" \"", type, null,
+ new EqualToFilterCriterion( ' ' ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\u03a0", type, null,
+ new EqualToFilterCriterion( '\u03a0' ) ); // Pi
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( " ", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( "abc", type, null,
+ string.Format( FilterParser.InvalidCharValueErrorText, "abc" ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\u03a0\u03a0", type, null,
+ string.Format( FilterParser.InvalidCharValueErrorText, "\u03a0\u03a0" ) );// PiPi
+
+ FilterParser.CheckCriterionBuilderResult( "=Null", type, null,
+ string.Format( FilterParser.InvalidCharValueErrorText, "Null" ) );
+ }
+
+ private static void TestCriterionBuilderDateTime()
+ {
+ Type type = typeof( DateTime );
+ FilterParser.LogMessageCallback( "Testing DateTime filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "0001-01-01", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( DateTime.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "0104-02-29", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( new DateTime( 104, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "0204-02-29", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( new DateTime( 204, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "02-29-0304", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( new DateTime( 304, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "2.29.0400", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( new DateTime( 400, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "0504-02-29T00:00", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( new DateTime( 504, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "0604-02-29T00:00:00", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( new DateTime( 604, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "0704-02-29T00:00:00.000", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( new DateTime( 704, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "0800-02-29T23:59:59.999", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( new DateTime( 800, 2, 29, 23, 59, 59, 999 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"02-29-904 23:59:59.999\"", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( new DateTime( 904, 2, 29, 23, 59, 59, 999 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"29.2.1004 23:59:59.999\"", type, IsIsCulture,
+ new EqualToFilterCriterion( new DateTime( 1004, 2, 29, 23, 59, 59, 999 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "29-Feb-1104", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( new DateTime( 1104, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "February/29/1200", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( new DateTime( 1200, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "29-février-1304", type, FrFrCulture,
+ new EqualToFilterCriterion( new DateTime( 1304, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "29.Ebrel.1404", type, BrFrCulture,
+ new EqualToFilterCriterion( new DateTime( 1404, 4, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "29-02-1504", type, FrFrCulture,
+ new EqualToFilterCriterion( new DateTime( 1504, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "février-29-1600", type, FrFrCulture,
+ new EqualToFilterCriterion( new DateTime( 1600, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "29-february-1704", type, FrFrCulture,
+ new EqualToFilterCriterion( new DateTime( 1704, 2, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "29.april.1804", type, BrFrCulture,
+ new EqualToFilterCriterion( new DateTime( 1804, 4, 29 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "02-29-1904 23:59:59.999", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( new DateTime( 1904, 2, 29, 23, 59, 59, 999 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "02-29-1904 23:59:59.999 OR 02-29-1908 23:59:59.999", type, CultureInfo.InvariantCulture,
+ new OrFilterCriterion(
+ new EqualToFilterCriterion( new DateTime( 1904, 2, 29, 23, 59, 59, 999 ) ),
+ new EqualToFilterCriterion( new DateTime( 1908, 2, 29, 23, 59, 59, 999 ) ) ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "`0100-02-29", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidDateTimeValueErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "0200-01-29T10", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidDateTimeValueErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "0300-01-29T24:00", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidDateTimeValueErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "0400-01-29T23:60", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidDateTimeValueErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "0500-01-29T23:59:60", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidDateTimeValueErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "01-avril-0600", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidDateTimeValueErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "01/avril/0700", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidDateTimeValueErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "01.avril.0904", type, BrFrCulture,
+ FilterParser.InvalidDateTimeValueErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "01-15-1004", type, FrFrCulture,
+ FilterParser.InvalidDateTimeValueErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "=Null", type, null,
+ FilterParser.InvalidDateTimeValueErrorText );
+ }
+
+ private static void TestCriterionBuilderNumber()
+ {
+ FilterParser.TestCriterionBuilderByte();
+ FilterParser.TestCriterionBuilderInt16();
+ FilterParser.TestCriterionBuilderInt32();
+ FilterParser.TestCriterionBuilderInt64();
+ FilterParser.TestCriterionBuilderSingle();
+ FilterParser.TestCriterionBuilderDouble();
+ FilterParser.TestCriterionBuilderDecimal();
+ FilterParser.TestCriterionBuilderSByte();
+ FilterParser.TestCriterionBuilderUInt16();
+ FilterParser.TestCriterionBuilderUInt32();
+ FilterParser.TestCriterionBuilderUInt64();
+ }
+
+ #region Number test methods
+
+ private static void TestCriterionBuilderByte()
+ {
+ Type type = typeof( byte );
+ FilterParser.LogMessageCallback( "Testing byte filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "0", type, null,
+ new EqualToFilterCriterion( byte.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "255", type, null,
+ new EqualToFilterCriterion( byte.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">=0", type, null,
+ new GreaterThanOrEqualToFilterCriterion( byte.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<255", type, null,
+ new LessThanFilterCriterion( byte.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( " 0 OR 255 ", type, null,
+ new OrFilterCriterion(
+ new EqualToFilterCriterion( ( byte )0 ),
+ new EqualToFilterCriterion( byte.MaxValue ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1.1", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "1,1", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "-1", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "256", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( ">-1", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<=256", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "10 3", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "Null", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "10 AND 300", type, null,
+ FilterParser.NumberOverflowErrorText );
+ }
+
+ private static void TestCriterionBuilderInt16()
+ {
+ Type type = typeof( short );
+ FilterParser.LogMessageCallback( "Testing Int16 filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "0", type, null,
+ new EqualToFilterCriterion( ( short )0 ) );
+
+ FilterParser.CheckCriterionBuilderResult( "-32768", type, null,
+ new EqualToFilterCriterion( short.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "32767", type, null,
+ new EqualToFilterCriterion( short.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">=-32768", type, null,
+ new GreaterThanOrEqualToFilterCriterion( short.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>32767", type, null,
+ new DifferentThanFilterCriterion( short.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "-309 AND 4555", type, null,
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( ( short )-309 ),
+ new EqualToFilterCriterion( ( short )4555 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<0 AND >000", type, null,
+ new AndFilterCriterion(
+ new LessThanFilterCriterion( ( short )0 ),
+ new GreaterThanFilterCriterion( ( short )0 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1.1", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "1,1", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "-32769", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "32768", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<>-32769", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<=32768", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "10,000", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "10 000", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "139 AND 40000", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "Null", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+ }
+
+ private static void TestCriterionBuilderInt32()
+ {
+ Type type = typeof( int );
+ FilterParser.LogMessageCallback( "Testing Int32 filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "0", type, null,
+ new EqualToFilterCriterion( 0 ) );
+
+ FilterParser.CheckCriterionBuilderResult( "-2147483648", type, null,
+ new EqualToFilterCriterion( int.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "2147483647", type, null,
+ new EqualToFilterCriterion( int.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">=-2147483648", type, null,
+ new GreaterThanOrEqualToFilterCriterion( int.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<2147483647", type, null,
+ new LessThanFilterCriterion( int.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "-564654 AND 71207", type, null,
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( -564654 ),
+ new EqualToFilterCriterion( 71207 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<0 OR >00000000000000000", type, null,
+ new OrFilterCriterion(
+ new LessThanFilterCriterion( 0 ),
+ new GreaterThanFilterCriterion( 0 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1.1", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "1,1", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "-2147483649", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "2147483648", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( ">-2147483649", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<2147483648", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "483 648 457", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "483,648,457", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "30 AND 21474836480", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "Null", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+ }
+
+ private static void TestCriterionBuilderInt64()
+ {
+ Type type = typeof( long );
+ FilterParser.LogMessageCallback( "Testing Int64 filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "0", type, null,
+ new EqualToFilterCriterion( 0L ) );
+
+ FilterParser.CheckCriterionBuilderResult( "-9223372036854775808", type, null,
+ new EqualToFilterCriterion( long.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "9223372036854775807", type, null,
+ new EqualToFilterCriterion( long.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">=-9223372036854775808", type, null,
+ new GreaterThanOrEqualToFilterCriterion( long.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<9223372036854775807", type, null,
+ new LessThanFilterCriterion( long.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "-4199873364 AND 127915466", type, null,
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( -4199873364L ),
+ new EqualToFilterCriterion( 127915466L ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<0 OR >00000000000000000", type, null,
+ new OrFilterCriterion(
+ new LessThanFilterCriterion( 0L ),
+ new GreaterThanFilterCriterion( 0L ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1.1", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "1,1", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "-9223372036854775809", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "9223372036854775808", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( ">-9223372036854775809", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<9223372036854775808", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "223 372 036 854 775 807", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "223,372,036,854,775,807", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "10 AND 92233720368547758080", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "Null", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+ }
+
+ private static void TestCriterionBuilderSingle()
+ {
+ Type type = typeof( float );
+ FilterParser.LogMessageCallback( "Testing Single filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "0", type, null,
+ new EqualToFilterCriterion( 0F ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1E-45", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( 1E-45F ) );
+
+ FilterParser.CheckCriterionBuilderResult( "-1E-45", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( -1E-45F ) );
+
+ FilterParser.CheckCriterionBuilderResult( "3E38 ", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( 3E38F ) );
+
+ FilterParser.CheckCriterionBuilderResult( "-3E38 ", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( -3E38F ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">=-3829.5000", type, CultureInfo.InvariantCulture,
+ new GreaterThanOrEqualToFilterCriterion( -3829.5F ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>11,687.145", type, CultureInfo.InvariantCulture,
+ new DifferentThanFilterCriterion( 11687.145F ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<1687,145", type, FrFrCulture,
+ new LessThanFilterCriterion( 1687.145F ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>\"11 687,145\"", type, FrFrCulture,
+ new DifferentThanFilterCriterion( 11687.145F ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>11 687,145", type, FrFrCulture,
+ new DifferentThanFilterCriterion( 11687.145F ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>11 687", type, FrFrCulture,
+ new DifferentThanFilterCriterion( 11687F ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<92 233,75", type, FrFrCulture,
+ new LessThanFilterCriterion( 92233.75F ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">11 687,145 AND < 12 384,55", type, FrFrCulture,
+ new AndFilterCriterion(
+ new GreaterThanFilterCriterion( 11687.145F ),
+ new LessThanFilterCriterion( 12384.55F ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "0.0 AND 3.10", type, CultureInfo.InvariantCulture,
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( 0F ),
+ new EqualToFilterCriterion( 3.1F ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( " > 11,687.145 AND < 12,384.55", type, CultureInfo.InvariantCulture,
+ new AndFilterCriterion(
+ new GreaterThanFilterCriterion( 11687.145F ),
+ new LessThanFilterCriterion( 12384.55F ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<0 OR >000000,00000000000", type, FrFrCulture,
+ new OrFilterCriterion(
+ new LessThanFilterCriterion( 0F ),
+ new GreaterThanFilterCriterion( 0F ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">-Infini", type, FrFrCulture,
+ new GreaterThanFilterCriterion( float.NegativeInfinity ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>-Infinity", type, CultureInfo.InvariantCulture,
+ new DifferentThanFilterCriterion( float.NegativeInfinity ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<+Infini", type, FrFrCulture,
+ new LessThanFilterCriterion( float.PositiveInfinity ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NOT =Infinity", type, CultureInfo.InvariantCulture,
+ new NotFilterCriterion(
+ new EqualToFilterCriterion( float.PositiveInfinity ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"Non Numérique\"", type, FrFrCulture,
+ new EqualToFilterCriterion( float.NaN ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>NaN", type, CultureInfo.InvariantCulture,
+ new DifferentThanFilterCriterion( float.NaN ) );
+
+ // Seems like a bug in the framework number parser. Everything too small will
+ // convert to 0. There doesn't seem to be a limit in the smallness.
+ FilterParser.CheckCriterionBuilderResult( "-1E-46", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( 0F ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1.1", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<92 233.72", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "26681 5989 92,233.72", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "0.0 3.10", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "14.545,1", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( ">-\"92 233,72\"", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<\"92 233.72\"", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "4E38", type, CultureInfo.InvariantCulture,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "Null", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+ }
+
+ private static void TestCriterionBuilderDouble()
+ {
+ Type type = typeof( double );
+ FilterParser.LogMessageCallback( "Testing Double filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "0", type, null,
+ new EqualToFilterCriterion( 0D ) );
+
+ FilterParser.CheckCriterionBuilderResult( "5E-324", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( 5E-324 ) );
+
+ FilterParser.CheckCriterionBuilderResult( "-5E-324", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( -5E-324 ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1E308 ", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( 1E308 ) );
+
+ FilterParser.CheckCriterionBuilderResult( "-1E308 ", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( -1E308 ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">=-3829.5000", type, CultureInfo.InvariantCulture,
+ new GreaterThanOrEqualToFilterCriterion( -3829.5 ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>11,687.145", type, CultureInfo.InvariantCulture,
+ new DifferentThanFilterCriterion( 11687.145 ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<1687,145", type, FrFrCulture,
+ new LessThanFilterCriterion( 1687.145 ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>\"11 687,145\"", type, FrFrCulture,
+ new DifferentThanFilterCriterion( 11687.145 ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>11 687,145", type, FrFrCulture,
+ new DifferentThanFilterCriterion( 11687.145 ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>11 687", type, FrFrCulture,
+ new DifferentThanFilterCriterion( 11687.0 ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<92 233,75", type, FrFrCulture,
+ new LessThanFilterCriterion( 92233.75 ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">11 687,145 AND < 12 384,55", type, FrFrCulture,
+ new AndFilterCriterion(
+ new GreaterThanFilterCriterion( 11687.145 ),
+ new LessThanFilterCriterion( 12384.55 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "0.0 AND 3.10", type, CultureInfo.InvariantCulture,
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( 0D ),
+ new EqualToFilterCriterion( 3.1 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( " > 11,687.145 AND < 12,384.55", type, CultureInfo.InvariantCulture,
+ new AndFilterCriterion(
+ new GreaterThanFilterCriterion( 11687.145 ),
+ new LessThanFilterCriterion( 12384.55 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<0 OR >000000,00000000000", type, FrFrCulture,
+ new OrFilterCriterion(
+ new LessThanFilterCriterion( 0D ),
+ new GreaterThanFilterCriterion( 0D ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">-Infini", type, FrFrCulture,
+ new GreaterThanFilterCriterion( double.NegativeInfinity ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>-Infinity", type, CultureInfo.InvariantCulture,
+ new DifferentThanFilterCriterion( double.NegativeInfinity ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<+Infini", type, FrFrCulture,
+ new LessThanFilterCriterion( double.PositiveInfinity ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NOT =Infinity", type, CultureInfo.InvariantCulture,
+ new NotFilterCriterion(
+ new EqualToFilterCriterion( double.PositiveInfinity ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"Non Numérique\"", type, FrFrCulture,
+ new EqualToFilterCriterion( double.NaN ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>NaN", type, CultureInfo.InvariantCulture,
+ new DifferentThanFilterCriterion( double.NaN ) );
+
+ // Seems like a bug in the framework number parser. Everything too small will
+ // convert to 0. There doesn't seem to be a limit in the smallness.
+ FilterParser.CheckCriterionBuilderResult( "2E-325", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( 0D ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1.1", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<\"92 233.72\"", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<92 233.72", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "26681 5989 92,233.72", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "0.0 3.10", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "14.545,1", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( ">-\"92 233,72\"", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<\"92 233.72\"", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "2E308", type, CultureInfo.InvariantCulture,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "Null", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+ }
+
+ private static void TestCriterionBuilderDecimal()
+ {
+ Type type = typeof( decimal );
+ FilterParser.LogMessageCallback( "Testing Decimal filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "0", type, null,
+ new EqualToFilterCriterion( 0M ) );
+
+ FilterParser.CheckCriterionBuilderResult( "-79228162514264337593543950335", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( decimal.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "79228162514264337593543950335 ", type, CultureInfo.InvariantCulture,
+ new EqualToFilterCriterion( decimal.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">=-3829.5000", type, CultureInfo.InvariantCulture,
+ new GreaterThanOrEqualToFilterCriterion( -3829.5M ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>11,687.145", type, CultureInfo.InvariantCulture,
+ new DifferentThanFilterCriterion( 11687.145M ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<1687,145", type, FrFrCulture,
+ new LessThanFilterCriterion( 1687.145M ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>\"11 687,145\"", type, FrFrCulture,
+ new DifferentThanFilterCriterion( 11687.145M ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>11 687,145", type, FrFrCulture,
+ new DifferentThanFilterCriterion( 11687.145M ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>11 687", type, FrFrCulture,
+ new DifferentThanFilterCriterion( 11687M ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<92 233,75", type, FrFrCulture,
+ new LessThanFilterCriterion( 92233.75M ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">11 687,145 AND < 12 384,55", type, FrFrCulture,
+ new AndFilterCriterion(
+ new GreaterThanFilterCriterion( 11687.145M ),
+ new LessThanFilterCriterion( 12384.55M ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "0.0 AND 3.10", type, CultureInfo.InvariantCulture,
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( 0M ),
+ new EqualToFilterCriterion( 3.1M ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( " > 11,687.145 AND < 12,384.55", type, CultureInfo.InvariantCulture,
+ new AndFilterCriterion(
+ new GreaterThanFilterCriterion( 11687.145M ),
+ new LessThanFilterCriterion( 12384.55M ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<0 OR >000000,00000000000", type, FrFrCulture,
+ new OrFilterCriterion(
+ new LessThanFilterCriterion( 0M ),
+ new GreaterThanFilterCriterion( 0M ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1.1", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<\"92 233.72\"", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<92 233.72", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "26681 5989 92,233.72", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "0.0 3.10", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "14.545,1", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( ">-\"92 233,72\"", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<\"92 233.72\"", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "-79228162514264337593543950336", type, CultureInfo.InvariantCulture,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "79228162514264337593543950336", type, CultureInfo.InvariantCulture,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "Null", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+ }
+
+ private static void TestCriterionBuilderSByte()
+ {
+ Type type = typeof( sbyte );
+ FilterParser.LogMessageCallback( "Testing SByte filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "0", type, null,
+ new EqualToFilterCriterion( ( sbyte )0 ) );
+
+ FilterParser.CheckCriterionBuilderResult( "-128", type, null,
+ new EqualToFilterCriterion( sbyte.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "127", type, null,
+ new EqualToFilterCriterion( sbyte.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">=-128", type, null,
+ new GreaterThanOrEqualToFilterCriterion( sbyte.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<127", type, null,
+ new LessThanFilterCriterion( sbyte.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "10 AND 20", type, null,
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( ( sbyte )10 ),
+ new EqualToFilterCriterion( ( sbyte )20 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( " 0 OR 127 ", type, null,
+ new OrFilterCriterion(
+ new EqualToFilterCriterion( ( sbyte )0 ),
+ new EqualToFilterCriterion( sbyte.MaxValue ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1.1", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "1,1", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "-129", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "128", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( ">-129", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<=128", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "10 3", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "10 340", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "Null", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+ }
+
+ private static void TestCriterionBuilderUInt16()
+ {
+ Type type = typeof( ushort );
+ FilterParser.LogMessageCallback( "Testing UInt16 filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "0", type, null,
+ new EqualToFilterCriterion( ushort.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "65535", type, null,
+ new EqualToFilterCriterion( ushort.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">=0", type, null,
+ new GreaterThanOrEqualToFilterCriterion( ushort.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<>65535", type, null,
+ new DifferentThanFilterCriterion( ushort.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "309 AND 4555", type, null,
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( ( ushort )309 ),
+ new EqualToFilterCriterion( ( ushort )4555 ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<0 AND >000", type, null,
+ new AndFilterCriterion(
+ new LessThanFilterCriterion( ushort.MinValue ),
+ new GreaterThanFilterCriterion( ushort.MinValue ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1.1", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "1,1", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "-1", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "65536", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<>-1", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<=65536", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "10,000", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "10 000", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "139 AND 66000", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "Null", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+ }
+
+ private static void TestCriterionBuilderUInt32()
+ {
+ Type type = typeof( uint );
+ FilterParser.LogMessageCallback( "Testing UInt32 filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "0", type, null,
+ new EqualToFilterCriterion( uint.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "4294967295", type, null,
+ new EqualToFilterCriterion( uint.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">=0", type, null,
+ new GreaterThanOrEqualToFilterCriterion( uint.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<4294967295", type, null,
+ new LessThanFilterCriterion( uint.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1 AND 71207", type, null,
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( 1U ),
+ new EqualToFilterCriterion( 71207U ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<0 OR >00000000000000000", type, null,
+ new OrFilterCriterion(
+ new LessThanFilterCriterion( uint.MinValue ),
+ new GreaterThanFilterCriterion( uint.MinValue ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1.1", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "1,1", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "-1", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "4294967296", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( ">-1", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<4294967296", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "42 949 672", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<42,949,672", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "26681 AND 5989 AND 429496729345", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "26681 5989 42949", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "Null", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+ }
+
+ private static void TestCriterionBuilderUInt64()
+ {
+ Type type = typeof( ulong );
+ FilterParser.LogMessageCallback( "Testing UInt64 filter criteria..." );
+
+ // Valid expressions
+ FilterParser.CheckCriterionBuilderResult( "0", type, null,
+ new EqualToFilterCriterion( ulong.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "18446744073709551615", type, null,
+ new EqualToFilterCriterion( ulong.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( ">=0", type, null,
+ new GreaterThanOrEqualToFilterCriterion( ulong.MinValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<18446744073709551615", type, null,
+ new LessThanFilterCriterion( ulong.MaxValue ) );
+
+ FilterParser.CheckCriterionBuilderResult( "54 AND 127915466", type, null,
+ new AndFilterCriterion(
+ new EqualToFilterCriterion( 54UL ),
+ new EqualToFilterCriterion( 127915466UL ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "<0 OR >00000000000000000", type, null,
+ new OrFilterCriterion(
+ new LessThanFilterCriterion( ulong.MinValue ),
+ new GreaterThanFilterCriterion( ulong.MinValue ) ) );
+
+ FilterParser.CheckCriterionBuilderResult( "NULL", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ FilterParser.CheckCriterionBuilderResult( "\"NULL\"", type, null,
+ new EqualToFilterCriterion( null ) );
+
+ // Invalid expressions
+ FilterParser.CheckCriterionBuilderResult( "", type, null,
+ string.Format( FilterParser.MissingRightOperandErrorText, typeof( EqualToFilterCriterion ).Name ) );
+
+ FilterParser.CheckCriterionBuilderResult( "1.1", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "1,1", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "-1", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "18446744073709551616", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( ">-1", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "<18446744073709551616", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "26681 AND 5989 AND 1844674407370955161455", type, null,
+ FilterParser.NumberOverflowErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "446 744 073 709 551", type, FrFrCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "446,744,073,709,551", type, CultureInfo.InvariantCulture,
+ FilterParser.InvalidNumberFormatErrorText );
+
+ FilterParser.CheckCriterionBuilderResult( "Null", type, null,
+ FilterParser.InvalidNumberFormatErrorText );
+ }
+
+ #endregion Number test methods
+
+ private static void CheckQuoteParserResult( string expression, params object[] expectedTokens )
+ {
+ List tokens = new List();
+
+ string error = null;
+
+ try
+ {
+ FilterParser.PrepareExpressionTokens( expression, tokens );
+ }
+ catch( Exception ex )
+ {
+ error = ex.Message;
+ }
+
+ if( error == null )
+ {
+ if( tokens.Count != expectedTokens.Length )
+ FilterParser.LogMessageCallback( "#ERROR# Token count mismatch for expression: " + expression );
+
+ if( tokens.Count == expectedTokens.Length )
+ {
+ for( int index = 0; index < expectedTokens.Length; index++ )
+ {
+ ValueToken expectedToken = expectedTokens[ index ] as ValueToken;
+
+ if( expectedToken == null )
+ {
+ FilterParser.LogMessageCallback( "#ERROR# Missing expected Token in expression: " + expression );
+ }
+ else
+ {
+ Token resultToken = tokens[ index ];
+
+ if( expectedToken.GetType() == resultToken.GetType() )
+ {
+ if( ( ( ValueToken )expectedToken ).Value != ( ( ValueToken )resultToken ).Value )
+ FilterParser.LogMessageCallback( "#ERROR# Value mismatch for Token in expression: " + expression );
+ }
+ else
+ {
+ FilterParser.LogMessageCallback( "#ERROR# Missing expected Token in expression: " + expression );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if( ( expectedTokens.Length == 1 ) && ( expectedTokens[ 0 ] is string ) )
+ {
+ if( ( string )expectedTokens[ 0 ] != error )
+ {
+ FilterParser.LogMessageCallback( "#ERROR# Expected <" + ( string )expectedTokens[ 0 ] + "> Got <" + error + "> Expression: " + expression );
+ }
+ }
+ else
+ {
+ FilterParser.LogMessageCallback( "#ERROR# " + error + " Expression: " + expression );
+ }
+ }
+ }
+
+ private static void CheckCriterionBuilderResult( string expression, Type dataType, CultureInfo culture, object expectedResult )
+ {
+ FilterCriterion resultFilterCriterion = FilterParser.TryParse( expression, dataType, culture );
+
+ if( resultFilterCriterion == null )
+ {
+ if( expectedResult is string )
+ {
+ if( ( string )expectedResult != FilterParser.LastError )
+ {
+ FilterParser.LogMessageCallback( "#ERROR# Expected <" + ( string )expectedResult + "> Got <" + FilterParser.LastError + "> Expression: " + expression );
+ }
+ }
+ else
+ {
+ FilterParser.LogMessageCallback( "#ERROR# " + FilterParser.LastError + " Expression: " + expression );
+ }
+ }
+ else
+ {
+ if( !string.IsNullOrEmpty( FilterParser.LastError ) )
+ FilterParser.LogMessageCallback( "#ERROR# Parse succeeded but an error was set: " + FilterParser.LastError );
+
+ if( expectedResult is FilterCriterion )
+ {
+ if( !resultFilterCriterion.Equals( expectedResult ) )
+ FilterParser.LogMessageCallback( "#ERROR# Result mismatch for expression: " + expression +
+ ". Expected <" + ( ( FilterCriterion )expectedResult ).ToString() + "> Got <" + resultFilterCriterion.ToString() + ">" );
+ }
+ else
+ {
+ FilterParser.LogMessageCallback( "#ERROR# Unexpected valid expression: " + expression );
+ }
+ }
+ }
+
+ // As standard, use culture that has little chance of being modified by the
+ // developper's regional settings. This allow the use of the default settings
+ // without having to manually force any standard value.
+ private static CultureInfo FrFrCulture = CultureInfo.GetCultureInfo( "fr-FR" ); // France
+ private static CultureInfo IsIsCulture = CultureInfo.GetCultureInfo( "is-IS" ); // Islande
+ private static CultureInfo BrFrCulture = CultureInfo.GetCultureInfo( "br-FR" ); // Breton
+ private static Action s_logMessageCallback = null;
+ }
+}
+
+#endif
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ForeignKeyConfigurationChangedEventManager.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ForeignKeyConfigurationChangedEventManager.cs
new file mode 100644
index 00000000..6ef64116
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ForeignKeyConfigurationChangedEventManager.cs
@@ -0,0 +1,75 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Windows;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class ForeignKeyConfigurationChangedEventManager : WeakEventManager
+ {
+ private ForeignKeyConfigurationChangedEventManager()
+ {
+ }
+
+ public static void AddListener( Column source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedAddListener( source, listener );
+ }
+
+ public static void RemoveListener( Column source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedRemoveListener( source, listener );
+ }
+
+ protected override void StartListening( object source )
+ {
+ Column column = ( Column )source;
+ column.ForeignKeyConfigurationChanged += new EventHandler( this.OnForeignKeyConfigurationChangedChanged );
+ }
+
+ protected override void StopListening( object source )
+ {
+ Column column = ( Column )source;
+ column.ForeignKeyConfigurationChanged -= new EventHandler( this.OnForeignKeyConfigurationChangedChanged );
+ }
+
+ private static ForeignKeyConfigurationChangedEventManager CurrentManager
+ {
+ get
+ {
+ Type managerType = typeof( ForeignKeyConfigurationChangedEventManager );
+ ForeignKeyConfigurationChangedEventManager currentManager = ( ForeignKeyConfigurationChangedEventManager )WeakEventManager.GetCurrentManager( managerType );
+
+ if( currentManager == null )
+ {
+ currentManager = new ForeignKeyConfigurationChangedEventManager();
+ WeakEventManager.SetCurrentManager( managerType, currentManager );
+ }
+
+ return currentManager;
+ }
+ }
+
+ private void OnForeignKeyConfigurationChangedChanged( object sender, EventArgs args )
+ {
+ this.DeliverEvent( sender, args );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/FrameworkElementUnloadedEventManager.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/FrameworkElementUnloadedEventManager.cs
new file mode 100644
index 00000000..f201284e
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/FrameworkElementUnloadedEventManager.cs
@@ -0,0 +1,105 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Threading;
+using System.Diagnostics;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class FrameworkElementUnloadedEventManager : WeakEventManager
+ {
+ private FrameworkElementUnloadedEventManager()
+ {
+ }
+
+ public static void AddListener( FrameworkElement source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedAddListener( source, listener );
+ }
+
+ public static void RemoveListener( FrameworkElement source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedRemoveListener( source, listener );
+ }
+
+ protected override void StartListening( object source )
+ {
+ FrameworkElement frameworkElement = source as FrameworkElement;
+
+ if( frameworkElement != null )
+ frameworkElement.Unloaded += this.FrameworkElement_Unloaded;
+ }
+
+ protected override void StopListening( object source )
+ {
+ FrameworkElement frameworkElement = source as FrameworkElement;
+
+ if( frameworkElement != null )
+ {
+ if( frameworkElement.Dispatcher.Thread == Thread.CurrentThread )
+ {
+ frameworkElement.Unloaded -= this.FrameworkElement_Unloaded;
+ }
+ else
+ {
+ System.Windows.Threading.Dispatcher dispatcher = frameworkElement.Dispatcher;
+ Debug.Assert( ( dispatcher != null ) && ( !dispatcher.HasShutdownStarted ) && ( !dispatcher.HasShutdownFinished ) );
+
+ if( dispatcher != null )
+ {
+ dispatcher.Invoke( new Action(
+ delegate
+ {
+ frameworkElement.Unloaded -= this.FrameworkElement_Unloaded;
+ } ), null );
+ }
+ }
+ }
+ }
+
+ private static FrameworkElementUnloadedEventManager CurrentManager
+ {
+ get
+ {
+ Type managerType = typeof( FrameworkElementUnloadedEventManager );
+
+ FrameworkElementUnloadedEventManager currentManager =
+ ( FrameworkElementUnloadedEventManager )WeakEventManager.GetCurrentManager( managerType );
+
+ if( currentManager == null )
+ {
+ currentManager = new FrameworkElementUnloadedEventManager();
+ WeakEventManager.SetCurrentManager( managerType, currentManager );
+ }
+
+ return currentManager;
+ }
+ }
+
+ private void FrameworkElement_Unloaded( object sender, RoutedEventArgs e )
+ {
+ this.DeliverEvent( sender, e );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GlobalSuppressions.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GlobalSuppressions.cs
new file mode 100644
index 00000000..a689ee39
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GlobalSuppressions.cs
@@ -0,0 +1,264 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage(
+ "Microsoft.Performance",
+ "CA1823:AvoidUnusedPrivateFields",
+ Scope = "member",
+ Target = "_XceedVersionInfoCommon.Build" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Performance",
+ "CA1823:AvoidUnusedPrivateFields",
+ Scope = "member",
+ Target = "_XceedVersionInfo.CurrentAssemblyPackUri" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Design",
+ "CA1020:AvoidNamespacesWithFewTypes",
+ Scope = "namespace",
+ Target = "XamlGeneratedNamespace" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2209:AssembliesShouldDeclareMinimumSecurity" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Design",
+ "CA1020:AvoidNamespacesWithFewTypes",
+ Scope = "namespace",
+ Target = "Xceed.Wpf.DataGrid.ValidationRules" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2233:OperationsShouldNotOverflow",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.DataGridCollectionView.System.Collections.ICollection.CopyTo(System.Array,System.Int32):System.Void",
+ MessageId = "index+1" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Design",
+ "CA1020:AvoidNamespacesWithFewTypes",
+ Scope = "namespace",
+ Target = "Xceed.Utils.Wpf.Markup" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Security",
+ "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.Print.DataGridPaginator.InitializeSettings(System.Printing.PrintQueue,System.Printing.PrintTicket):System.Void",
+ Justification = "A permission demand for FullTrust has been added to InitializeSettings()." )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Design",
+ "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.DataGridControl.System.Windows.Documents.IDocumentPaginatorSource.DocumentPaginator" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Design",
+ "CA1043:UseIntegralOrStringArgumentForIndexers",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.CellCollection.Item[Xceed.Wpf.DataGrid.Column]" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA1801:ReviewUnusedParameters",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.DataGridCollectionView..ctor(System.Type)", MessageId = "itemType" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Performance",
+ "CA1805:DoNotInitializeUnnecessarily",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.DetailConfiguration..ctor(Xceed.Wpf.DataGrid.DataGridContext)" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Performance",
+ "CA1800:DoNotCastUnnecessarily",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.GroupByControl.Xceed.Utils.Wpf.DragDrop.IDropTarget.CanDropElement(System.Windows.UIElement):System.Boolean" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Performance",
+ "CA1800:DoNotCastUnnecessarily",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.GroupByItem.Xceed.Utils.Wpf.DragDrop.IDropTarget.CanDropElement(System.Windows.UIElement):System.Boolean" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Design",
+ "CA1011:ConsiderPassingBaseTypesAsParameters",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.Views.Theme.IsViewSupported(System.Type,System.Type):System.Boolean" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Design",
+ "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.GroupLevelIndicatorPane.System.Windows.IWeakEventListener.ReceiveWeakEvent(System.Type,System.Object,System.EventArgs):System.Boolean" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Design",
+ "CA1033:InterfaceMethodsShouldBeCallableByChildTypes",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.HierarchicalGroupLevelIndicatorPane.System.Windows.IWeakEventListener.ReceiveWeakEvent(System.Type,System.Object,System.EventArgs):System.Boolean" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA1801:ReviewUnusedParameters",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.DetailConfiguration.AddDefaultHeadersFooters(System.Collections.ObjectModel.ObservableCollection`1,System.Collections.ObjectModel.ObservableCollection`1):System.Void", MessageId = "footersCollection" )]
+
+#region CA2214:DoNotCallOverridableMethodsInConstructors
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.Cell..ctor()" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.CellEditor..ctor()" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.Column..ctor()" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.Column..ctor(System.String,System.Object,System.Windows.Data.BindingBase)" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.ColumnManagerCell..ctor()" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.ColumnManagerRow..ctor()" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.DataCell..ctor(System.String,System.Object)" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.DataGridControl..ctor()" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.DropMarkAdorner..ctor(System.Windows.UIElement,System.Windows.Media.Pen,Xceed.Wpf.DataGrid.DropMarkOrientation)" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.Views.SynchronizedScrollViewer..ctor()" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.Views.ViewBase..ctor()" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.GroupHeaderControl..ctor(Xceed.Wpf.DataGrid.Group)" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.Views.FixedCellPanel..ctor()" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.Views.ScrollingCellsDecorator..ctor(Xceed.Wpf.DataGrid.Views.FixedCellPanel)" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.Print.DataGridPageControl..ctor(Xceed.Wpf.DataGrid.DataGridControl)" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.Views.DataGridScrollViewer..ctor()" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.GroupConfiguration..ctor()" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.DataGridContext..ctor(Xceed.Wpf.DataGrid.DataGridContext,Xceed.Wpf.DataGrid.DataGridControl,System.Object,System.Windows.Data.CollectionView,Xceed.Wpf.DataGrid.DetailConfiguration)" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.DetailConfiguration..ctor(System.Boolean)" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.DetailConfiguration..ctor(Xceed.Wpf.DataGrid.DataGridContext)" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.SaveRestoreStateVisitor..ctor()" )]
+
+[assembly: SuppressMessage(
+ "Microsoft.Usage",
+ "CA2214:DoNotCallOverridableMethodsInConstructors",
+ Scope = "member",
+ Target = "Xceed.Wpf.DataGrid.DefaultDetailConfiguration..ctor()" )]
+
+#endregion CA2214:DoNotCallOverridableMethodsInConstructors
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Group.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Group.cs
new file mode 100644
index 00000000..c2224938
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Group.cs
@@ -0,0 +1,538 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using System.Collections.ObjectModel;
+using System.Windows.Data;
+using System.Windows.Controls.Primitives;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using Xceed.Wpf.DataGrid.Automation;
+using System.Windows.Automation.Peers;
+using System.Windows.Automation;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class Group : INotifyPropertyChanged
+ {
+ internal Group( GroupGeneratorNode node, CollectionViewGroup group, IList groupLevelDescriptions, DataGridContext dataGridContext )
+ {
+ if( node == null )
+ throw new ArgumentNullException( "node" );
+
+ if( group == null )
+ throw new ArgumentNullException( "group" );
+
+ if( groupLevelDescriptions == null )
+ throw new ArgumentNullException( "groupLevelDescriptions" );
+
+ if( dataGridContext == null )
+ throw new ArgumentNullException( "dataGridContext" );
+
+ m_generatorNode = node;
+ m_collectionViewGroup = group;
+ m_groupDescriptionsCollection = groupLevelDescriptions;
+ m_dataGridContext = dataGridContext;
+
+ //In case no late bingding is to happen, and if we already have everything to get the groupLevelDescription immediately, let's do!
+ int level = m_generatorNode.Level;
+ if( m_groupDescriptionsCollection.Count > level )
+ {
+ m_groupLevelDescription = m_groupDescriptionsCollection[ level ];
+ }
+ m_lateGroupLevelDescriptionBindingPerformed = false;
+
+ m_generatorNode.TotalLeafCountChanged += OnTotalItemCountChanged;
+ m_generatorNode.IsExpandedChanged += OnIsExpandedChanged;
+ }
+
+ #region IsExpanded Property
+
+ public bool IsExpanded
+ {
+ get
+ {
+ if( m_generatorNode != null )
+ return m_generatorNode.IsExpanded;
+
+ return false;
+ }
+ set
+ {
+ if( m_generatorNode != null )
+ m_generatorNode.IsExpanded = value;
+ }
+ }
+
+ internal bool IsComputedExpanded
+ {
+ get
+ {
+ if( m_generatorNode != null )
+ return m_generatorNode.IsComputedExpanded;
+
+ return false;
+ }
+ }
+
+ #endregion IsExpanded Property
+
+ #region Level Property
+
+ public int Level
+ {
+ get
+ {
+ if( m_generatorNode != null )
+ return m_generatorNode.Level;
+
+ return -1;
+ }
+ }
+
+ #endregion Level Property
+
+ #region ItemCount Property
+
+ public int ItemCount
+ {
+ get
+ {
+ if( m_generatorNode != null )
+ return m_generatorNode.TotalLeafCount;
+
+ return 0;
+ }
+ }
+
+ #endregion ItemCount Property
+
+ #region ParentGroups Property
+
+ public ReadOnlyCollection ParentGroups
+ {
+ get
+ {
+ List list = new List();
+
+ if( m_generatorNode != null )
+ {
+ GeneratorNodeHelper nodeHelper = new GeneratorNodeHelper( m_generatorNode, 0, 0 ); //index is not important
+
+ while( nodeHelper.MoveToParent() )
+ {
+ GroupGeneratorNode parentGroup = nodeHelper.CurrentNode as GroupGeneratorNode;
+ if( parentGroup != null )
+ {
+ list.Insert( 0, parentGroup.UIGroup );
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ return list.AsReadOnly();
+ }
+ }
+
+ #endregion ParentGroups Property
+
+ #region SiblingGroups Property
+
+ public ReadOnlyCollection SiblingGroups
+ {
+ get
+ {
+ if( m_generatorNode == null )
+ return null;
+
+ HeadersFootersGeneratorNode headersFootersGeneratorNode = HeadersFootersGeneratorNode.GetSameLevelFirstHeaderNode( m_generatorNode );
+
+ if( headersFootersGeneratorNode == null )
+ return null;
+
+ int currentGeneratorContentGeneration = this.DataGridContext.CustomItemContainerGenerator.CurrentGeneratorContentGeneration;
+
+ return headersFootersGeneratorNode.GetImmediateUIGroups( currentGeneratorContentGeneration );
+ }
+ }
+
+ #endregion SiblingGroups
+
+ #region Value Property
+
+ public object Value
+ {
+ get
+ {
+ if( m_collectionViewGroup != null )
+ return m_collectionViewGroup.Name;
+
+ return null;
+ }
+ }
+
+ #endregion Value Property
+
+ #region ValueTemplate Property
+
+ public DataTemplate ValueTemplate
+ {
+ get
+ {
+ this.PerformLateGroupLevelDescriptionBinding();
+
+ if( m_groupLevelDescription != null )
+ {
+ return m_groupLevelDescription.ValueTemplate;
+ }
+
+ return null;
+ }
+ }
+
+ #endregion ValueTemplate Property
+
+ #region ValueTemplateSelector Property
+
+ public DataTemplateSelector ValueTemplateSelector
+ {
+ get
+ {
+ this.PerformLateGroupLevelDescriptionBinding();
+
+ if( m_groupLevelDescription != null )
+ {
+ return m_groupLevelDescription.ValueTemplateSelector;
+ }
+
+ return null;
+ }
+ }
+
+ #endregion ValueTemplateSelector Property
+
+ #region IsBottomLevel Property
+
+ public bool IsBottomLevel
+ {
+ get
+ {
+ if( m_collectionViewGroup != null )
+ return m_collectionViewGroup.IsBottomLevel;
+
+ return true;
+ }
+ }
+
+ #endregion IsBottomLevel Property
+
+ #region Items Property [Obsoleted]
+
+ [Obsolete( "The Items property is obsolete and has been replaced by the GroupExtensions.GetItems extensibility method.", false )]
+ public ReadOnlyObservableCollection Items
+ {
+ get
+ {
+ IList items = this.GetItems();
+
+ if( items is ReadOnlyObservableCollection )
+ return ( ReadOnlyObservableCollection )items;
+
+ return null;
+ }
+ }
+
+ #endregion Items Property [Obsoleted]
+
+ #region Title Property
+
+ public object Title
+ {
+ get
+ {
+ this.PerformLateGroupLevelDescriptionBinding();
+
+ if( m_groupLevelDescription != null )
+ {
+ return m_groupLevelDescription.Title;
+ }
+
+ return null;
+ }
+ }
+
+ #endregion Title Property
+
+ #region TitleTemplate Property
+
+ public DataTemplate TitleTemplate
+ {
+ get
+ {
+ this.PerformLateGroupLevelDescriptionBinding();
+
+ if( m_groupLevelDescription != null )
+ {
+ return m_groupLevelDescription.TitleTemplate;
+ }
+
+ return null;
+ }
+ }
+
+ #endregion TitleTemplate Property
+
+ #region TitleTemplateSelector Property
+
+ public DataTemplateSelector TitleTemplateSelector
+ {
+ get
+ {
+ this.PerformLateGroupLevelDescriptionBinding();
+
+ if( m_groupLevelDescription != null )
+ {
+ return m_groupLevelDescription.TitleTemplateSelector;
+ }
+
+ return null;
+ }
+ }
+
+ #endregion TitleTemplateSelector Property
+
+ #region StatContext Property
+
+ public object StatContext
+ {
+ get
+ {
+ return m_collectionViewGroup as DataGridCollectionViewGroup;
+ }
+ }
+
+ #endregion StatContext Property
+
+ #region GroupConfiguration Property
+
+ public GroupConfiguration GroupConfiguration
+ {
+ get
+ {
+ if( m_generatorNode != null )
+ return m_generatorNode.GroupConfiguration;
+
+ return null;
+ }
+ }
+
+ #endregion GroupConfiguration Property
+
+ #region GeneratorNode Property
+
+ internal GroupGeneratorNode GeneratorNode
+ {
+ get
+ {
+ return m_generatorNode;
+ }
+ }
+
+ #endregion GeneratorNode Property
+
+ #region GroupBy Property
+
+ internal string GroupBy
+ {
+ get
+ {
+ this.PerformLateGroupLevelDescriptionBinding();
+
+ if( m_groupLevelDescription == null )
+ {
+ return string.Empty;
+ }
+
+ return m_groupLevelDescription.FieldName;
+ }
+ }
+
+ #endregion GroupBy Property
+
+ #region DataGridContext Property
+
+ internal DataGridContext DataGridContext
+ {
+ get
+ {
+ return m_dataGridContext;
+ }
+ }
+
+ #endregion DataGridContext Property
+
+ internal DataGridGroupAutomationPeer CreateAutomationPeer()
+ {
+ return new DataGridGroupAutomationPeer( this );
+ }
+
+ internal void ClearGroup()
+ {
+ m_generatorNode.TotalLeafCountChanged -= OnTotalItemCountChanged;
+ m_generatorNode.IsExpandedChanged -= OnIsExpandedChanged;
+
+ if( m_groupLevelDescription != null )
+ {
+ m_groupLevelDescription.PropertyChanged -= GroupLevelDescriptionChangedHandler;
+ }
+
+ m_groupLevelDescription = null;
+ m_generatorNode = null;
+ m_collectionViewGroup = null;
+ }
+
+ internal CollectionViewGroup CollectionViewGroup
+ {
+ get
+ {
+ return m_collectionViewGroup;
+ }
+ }
+
+ private void GroupLevelDescriptionChangedHandler( object sender, PropertyChangedEventArgs e )
+ {
+ // Simply relay this property changed to this Group instance.
+ this.NotifyPropertyChanged( e );
+ }
+
+ private void OnTotalItemCountChanged( object sender, EventArgs e )
+ {
+ this.NotifyPropertyChanged( "ItemCount" );
+ }
+
+ private void OnIsExpandedChanged( object sender, EventArgs e )
+ {
+ this.NotifyPropertyChanged( "IsExpanded" );
+
+ if( AutomationPeer.ListenerExists( AutomationEvents.PropertyChanged ) )
+ {
+ DataGridGroupAutomationPeer groupAutomationPeer = this.CreateAutomationPeer();
+
+ ExpandCollapseState oldExpandCollapseState;
+ ExpandCollapseState newExpandCollapseState;
+
+ if( this.IsExpanded )
+ {
+ oldExpandCollapseState = ExpandCollapseState.Collapsed;
+ newExpandCollapseState = ExpandCollapseState.Expanded;
+ }
+ else
+ {
+ oldExpandCollapseState = ExpandCollapseState.Expanded;
+ newExpandCollapseState = ExpandCollapseState.Collapsed;
+ }
+
+ groupAutomationPeer.RaisePropertyChangedEvent(
+ ExpandCollapsePatternIdentifiers.ExpandCollapseStateProperty,
+ oldExpandCollapseState, newExpandCollapseState );
+ }
+ }
+
+ private void PerformLateGroupLevelDescriptionBinding()
+ {
+ int level = this.Level;
+
+ if( m_lateGroupLevelDescriptionBindingPerformed && this.ValidateGroupLevelDescription( level ) )
+ return;
+
+ m_lateGroupLevelDescriptionBindingPerformed = true;
+
+
+ if( m_groupDescriptionsCollection.Count > level )
+ {
+ m_groupLevelDescription = m_groupDescriptionsCollection[ level ];
+ }
+
+ if( m_groupLevelDescription != null )
+ {
+ m_groupLevelDescription.PropertyChanged += new PropertyChangedEventHandler( GroupLevelDescriptionChangedHandler );
+
+ this.NotifyPropertyChanged( "Title" );
+ this.NotifyPropertyChanged( "TitleTemplate" );
+ this.NotifyPropertyChanged( "TitleTemplateSelector" );
+ this.NotifyPropertyChanged( "ValueTemplate" );
+ this.NotifyPropertyChanged( "ValueTemplateSelector" );
+ }
+ }
+
+ private bool ValidateGroupLevelDescription( int level )
+ {
+ //Returns false if the m_groupLevelDescription has changed since it was first initialized, and will be updated in the calling method.
+ if( m_groupDescriptionsCollection.Count > level )
+ {
+ return ( m_groupLevelDescription == m_groupDescriptionsCollection[ level ] );
+ }
+
+ //If there is no GroupDescription corresponding to the level this group is at, then lets make sure we do not leak on the old GroupDescription
+ if( m_groupLevelDescription != null )
+ {
+ m_groupLevelDescription.PropertyChanged -= new PropertyChangedEventHandler( GroupLevelDescriptionChangedHandler );
+ m_groupLevelDescription = null;
+ }
+
+ //Return true because there is nothing else to do anyway.
+ return true;
+ }
+
+ #region INotifyPropertyChanged Members
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private void NotifyPropertyChanged( String propertyName )
+ {
+ if( this.PropertyChanged != null )
+ {
+ this.PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
+ }
+ }
+
+ private void NotifyPropertyChanged( PropertyChangedEventArgs e )
+ {
+ if( this.PropertyChanged != null )
+ {
+ this.PropertyChanged( this, e );
+ }
+ }
+
+ #endregion INotifyPropertyChanged Members
+
+ private GroupLevelDescription m_groupLevelDescription; // = null
+ private GroupGeneratorNode m_generatorNode; // = null
+ private CollectionViewGroup m_collectionViewGroup; // = null
+ private IList m_groupDescriptionsCollection; // = null
+ private DataGridContext m_dataGridContext;
+ private bool m_lateGroupLevelDescriptionBindingPerformed;
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupByControl.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupByControl.cs
new file mode 100644
index 00000000..feddab45
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupByControl.cs
@@ -0,0 +1,518 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using System.Collections.ObjectModel;
+using System.Windows.Controls;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Windows.Data;
+using System.Windows.Media;
+using Xceed.Utils.Wpf.DragDrop;
+using System.Diagnostics;
+using Xceed.Wpf.DataGrid.Views;
+using System.Windows.Documents;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class GroupByControl : ItemsControl, IDropTarget
+ {
+ static GroupByControl()
+ {
+ // This DefaultStyleKey will only be used in design-time.
+ DefaultStyleKeyProperty.OverrideMetadata( typeof( GroupByControl ), new FrameworkPropertyMetadata( new Markup.ThemeKey( typeof( Views.TableView ), typeof( GroupByControl ) ) ) );
+
+ FrameworkElementFactory staircaseFactory = new FrameworkElementFactory( typeof( StaircasePanel ) );
+ ItemsPanelTemplate itemsPanelTemplate = new ItemsPanelTemplate( staircaseFactory );
+ RelativeSource ancestorSource = new RelativeSource( RelativeSourceMode.FindAncestor, typeof( GroupByControl ), 1 );
+
+ Binding binding = new Binding();
+ binding.Path = new PropertyPath( GroupByControl.ConnectionLineAlignmentProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = ancestorSource;
+ staircaseFactory.SetBinding( StaircasePanel.ConnectionLineAlignmentProperty, binding );
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( GroupByControl.ConnectionLineOffsetProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = ancestorSource;
+ staircaseFactory.SetBinding( StaircasePanel.ConnectionLineOffsetProperty, binding );
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( GroupByControl.ConnectionLinePenProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = ancestorSource;
+ staircaseFactory.SetBinding( StaircasePanel.ConnectionLinePenProperty, binding );
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( GroupByControl.StairHeightProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = ancestorSource;
+ staircaseFactory.SetBinding( StaircasePanel.StairHeightProperty, binding );
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( GroupByControl.StairSpacingProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = ancestorSource;
+ staircaseFactory.SetBinding( StaircasePanel.StairSpacingProperty, binding );
+
+ itemsPanelTemplate.Seal();
+
+ ItemsControl.ItemsPanelProperty.OverrideMetadata( typeof( GroupByControl ), new FrameworkPropertyMetadata( itemsPanelTemplate ) );
+
+ DataGridControl.ParentDataGridControlPropertyKey.OverrideMetadata( typeof( GroupByControl ), new FrameworkPropertyMetadata( new PropertyChangedCallback( GroupByControl.ParentGridControlChangedCallback ) ) );
+ DataGridControl.DataGridContextPropertyKey.OverrideMetadata( typeof( GroupByControl ), new FrameworkPropertyMetadata( new PropertyChangedCallback( GroupByControl.DataGridContextChangedCallback ) ) );
+
+ FocusableProperty.OverrideMetadata( typeof( GroupByControl ), new FrameworkPropertyMetadata( false ) );
+ }
+
+ #region AllowGroupingModification Property
+
+ public static readonly DependencyProperty AllowGroupingModificationProperty =
+ DependencyProperty.Register( "AllowGroupingModification", typeof( bool ), typeof( GroupByControl ), new UIPropertyMetadata( true ) );
+
+ public bool AllowGroupingModification
+ {
+ get
+ {
+ return ( bool )this.GetValue( GroupByControl.AllowGroupingModificationProperty );
+ }
+ set
+ {
+ this.SetValue( GroupByControl.AllowGroupingModificationProperty, value );
+ }
+ }
+
+ #endregion AllowGroupingModification Property
+
+ #region AllowSort Property
+
+ public static readonly DependencyProperty AllowSortProperty =
+ ColumnManagerRow.AllowSortProperty.AddOwner( typeof( GroupByControl ), new UIPropertyMetadata( true ) );
+
+ public bool AllowSort
+ {
+ get
+ {
+ return ( bool )this.GetValue( GroupByControl.AllowSortProperty );
+ }
+ set
+ {
+ this.SetValue( GroupByControl.AllowSortProperty, value );
+ }
+ }
+
+ #endregion AllowSort Property
+
+ #region ConnectionLineAlignment Property
+
+ public static readonly DependencyProperty ConnectionLineAlignmentProperty =
+ StaircasePanel.ConnectionLineAlignmentProperty.AddOwner( typeof( GroupByControl ) );
+
+ public ConnectionLineAlignment ConnectionLineAlignment
+ {
+ get
+ {
+ return ( ConnectionLineAlignment )this.GetValue( GroupByControl.ConnectionLineAlignmentProperty );
+ }
+ set
+ {
+ this.SetValue( GroupByControl.ConnectionLineAlignmentProperty, value );
+ }
+ }
+
+ #endregion ConnectionLineAlignment Property
+
+ #region ConnectionLineOffset Property
+
+ public static readonly DependencyProperty ConnectionLineOffsetProperty =
+ StaircasePanel.ConnectionLineOffsetProperty.AddOwner( typeof( GroupByControl ) );
+
+ public double ConnectionLineOffset
+ {
+ get
+ {
+ return ( double )this.GetValue( GroupByControl.ConnectionLineOffsetProperty );
+ }
+ set
+ {
+ this.SetValue( GroupByControl.ConnectionLineOffsetProperty, value );
+ }
+ }
+
+ #endregion ConnectionLineOffset Property
+
+ #region ConnectionLinePen Property
+
+ public static readonly DependencyProperty ConnectionLinePenProperty =
+ StaircasePanel.ConnectionLinePenProperty.AddOwner( typeof( GroupByControl ) );
+
+ public Pen ConnectionLinePen
+ {
+ get
+ {
+ return ( Pen )this.GetValue( GroupByControl.ConnectionLinePenProperty );
+ }
+ set
+ {
+ this.SetValue( GroupByControl.ConnectionLinePenProperty, value );
+ }
+ }
+
+ #endregion ConnectionLinePen Property
+
+ #region StairHeight Property
+
+ public static readonly DependencyProperty StairHeightProperty =
+ StaircasePanel.StairHeightProperty.AddOwner( typeof( GroupByControl ) );
+
+ public double StairHeight
+ {
+ get
+ {
+ return ( double )this.GetValue( GroupByControl.StairHeightProperty );
+ }
+ set
+ {
+ this.SetValue( GroupByControl.StairHeightProperty, value );
+ }
+ }
+
+ #endregion StairHeight Property
+
+ #region StairSpacing Property
+
+ public static readonly DependencyProperty StairSpacingProperty =
+ StaircasePanel.StairSpacingProperty.AddOwner( typeof( GroupByControl ) );
+
+ public double StairSpacing
+ {
+ get
+ {
+ return ( double )this.GetValue( GroupByControl.StairSpacingProperty );
+ }
+ set
+ {
+ this.SetValue( GroupByControl.StairSpacingProperty, value );
+ }
+ }
+
+ #endregion StairSpacing Property
+
+ #region NoGroupContent Property
+
+ public static readonly DependencyProperty NoGroupContentProperty =
+ DependencyProperty.Register(
+ "NoGroupContent",
+ typeof( object ),
+ typeof( GroupByControl ),
+ new PropertyMetadata( "Drag a column header here to group by that column." ) );
+
+ public object NoGroupContent
+ {
+ get
+ {
+ return this.GetValue( GroupByControl.NoGroupContentProperty );
+ }
+ set
+ {
+ this.SetValue( GroupByControl.NoGroupContentProperty, value );
+ }
+ }
+
+ #endregion NoGroupContent Property
+
+ protected override DependencyObject GetContainerForItemOverride()
+ {
+ return new GroupByItem();
+ }
+
+ protected override bool IsItemItsOwnContainerOverride( object item )
+ {
+ return ( item is GroupByItem );
+ }
+
+ protected override void PrepareContainerForItemOverride( DependencyObject element, object item )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl grid = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ base.PrepareContainerForItemOverride( element, item );
+
+ if( grid != null )
+ {
+ GroupByItem groupByItem = ( GroupByItem )element;
+ groupByItem.PrepareDefaultStyleKey( grid.GetView() );
+ }
+ }
+
+ protected internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( GroupByControl ) );
+ }
+
+ internal bool IsGroupingModificationAllowed
+ {
+ get
+ {
+ bool allowGroupingModification = this.AllowGroupingModification;
+
+ if( allowGroupingModification )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext == null )
+ {
+ allowGroupingModification = false;
+ }
+ else
+ {
+ allowGroupingModification = dataGridContext.Items.CanGroup;
+ }
+ }
+
+ return allowGroupingModification;
+ }
+ }
+
+ private void RegisterParentDataGridContext( DataGridContext dataGridContext )
+ {
+ if( dataGridContext == null )
+ {
+ this.ItemsSource = null;
+ }
+ else
+ {
+ this.ItemsSource = dataGridContext.GroupLevelDescriptions;
+ }
+ }
+
+ private static void ParentGridControlChangedCallback( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ DataGridControl grid = e.NewValue as DataGridControl;
+ GroupByControl panel = ( GroupByControl )sender;
+
+ if( grid != null )
+ panel.PrepareDefaultStyleKey( grid.GetView() );
+ }
+
+ private static void DataGridContextChangedCallback( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ DataGridContext dataGridContext = e.NewValue as DataGridContext;
+ GroupByControl panel = ( GroupByControl )sender;
+
+ if( dataGridContext != null )
+ panel.RegisterParentDataGridContext( dataGridContext );
+ }
+
+ private void ShowFarDropMark()
+ {
+ if( m_dropMarkAdorner == null )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl grid = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ Pen pen = Xceed.Wpf.DataGrid.Views.UIViewBase.GetDropMarkPen( this );
+
+ if( ( pen == null ) && ( grid != null ) )
+ {
+ UIViewBase uiViewBase = grid.GetView() as UIViewBase;
+ pen = uiViewBase.DefaultDropMarkPen;
+ }
+
+ DropMarkOrientation orientation = UIViewBase.GetDropMarkOrientation( this );
+
+ if( ( orientation == DropMarkOrientation.Default ) && ( grid != null ) )
+ {
+ UIViewBase uiViewBase = grid.GetView() as UIViewBase;
+
+ orientation = uiViewBase.DefaultDropMarkOrientation;
+ }
+
+ m_dropMarkAdorner = new DropMarkAdorner( this, pen, orientation );
+
+ AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer( this );
+
+ if( adornerLayer != null )
+ adornerLayer.Add( m_dropMarkAdorner );
+ }
+
+ // We Only want the drop mark to be displayed at the end of the HierarchicalGroupByControlNode
+ m_dropMarkAdorner.ForceAlignment( DropMarkAlignment.Far );
+ }
+
+ private void HideDropMark()
+ {
+ if( m_dropMarkAdorner != null )
+ {
+ AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer( this );
+
+ if( adornerLayer != null )
+ adornerLayer.Remove( m_dropMarkAdorner );
+
+ m_dropMarkAdorner = null;
+ }
+ }
+
+ #region IDropTarget Members
+
+
+ bool IDropTarget.CanDropElement( UIElement draggedElement )
+ {
+ bool isAlreadyGroupedBy = false;
+
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ if( cell != null )
+ {
+ isAlreadyGroupedBy = GroupingHelper.IsAlreadyGroupedBy( cell );
+ ColumnBase parentColumn = cell.ParentColumn;
+
+ if( ( parentColumn == null ) || ( !parentColumn.AllowGroup ) )
+ return false;
+ }
+
+ DataGridContext sourceDetailContext = DataGridControl.GetDataGridContext( this );
+ Debug.Assert( sourceDetailContext != null );
+ DetailConfiguration sourceDetailConfig = ( sourceDetailContext != null ) ? sourceDetailContext.SourceDetailConfiguration : null;
+
+ DataGridContext draggedDetailContext = DataGridControl.GetDataGridContext( draggedElement );
+ Debug.Assert( draggedDetailContext != null );
+ DetailConfiguration draggedDetailConfig = ( draggedDetailContext != null ) ? draggedDetailContext.SourceDetailConfiguration : null;
+
+
+ bool canDrop = ( sourceDetailConfig == draggedDetailConfig ) &&
+ ( sourceDetailContext != null ) &&
+ ( draggedDetailContext != null ) &&
+ ( sourceDetailContext.GroupLevelDescriptions == draggedDetailContext.GroupLevelDescriptions ) &&
+ ( this.IsGroupingModificationAllowed ) &&
+ ( ( draggedElement is ColumnManagerCell ) || ( draggedElement is GroupByItem ) ) &&
+ ( !isAlreadyGroupedBy );
+
+ if( canDrop && ( cell != null ) )
+ canDrop = GroupingHelper.ValidateMaxGroupDescriptions( draggedDetailContext );
+
+ return canDrop;
+ }
+
+ void IDropTarget.DragEnter( UIElement draggedElement )
+ {
+ }
+
+ void IDropTarget.DragOver( UIElement draggedElement, Point mousePosition )
+ {
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ if( cell == null )
+ return;
+
+ DataGridContext draggedDetailContext = DataGridControl.GetDataGridContext( draggedElement );
+
+ int lastIndex = draggedDetailContext.GroupLevelDescriptions.Count - 1;
+ if( lastIndex > -1 )
+ {
+ GroupByItem groupByItem = this.ItemContainerGenerator.ContainerFromIndex( lastIndex ) as GroupByItem;
+
+ Debug.Assert( groupByItem != null );
+ if( groupByItem == null )
+ throw new DataGridInternalException();
+
+ groupByItem.ShowDropMark( mousePosition );
+ }
+ }
+
+ void IDropTarget.DragLeave( UIElement draggedElement )
+ {
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ if( cell == null )
+ return;
+
+ DataGridContext draggedDetailContext = DataGridControl.GetDataGridContext( draggedElement );
+
+ int lastIndex = draggedDetailContext.GroupLevelDescriptions.Count - 1;
+ if( lastIndex > -1 )
+ {
+ GroupByItem groupByItem = this.ItemContainerGenerator.ContainerFromIndex( lastIndex ) as GroupByItem;
+
+ Debug.Assert( groupByItem != null );
+ if( groupByItem == null )
+ throw new DataGridInternalException();
+
+ groupByItem.HideDropMark();
+ }
+ else
+ {
+ this.HideDropMark();
+ }
+ }
+
+ void IDropTarget.Drop( UIElement draggedElement )
+ {
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ if( cell == null )
+ return;
+
+ DataGridContext draggedDetailContext = DataGridControl.GetDataGridContext( draggedElement );
+
+ int lastIndex = draggedDetailContext.GroupLevelDescriptions.Count - 1;
+ if( lastIndex > -1 )
+ {
+ GroupByItem groupByItem = this.ItemContainerGenerator.ContainerFromIndex( lastIndex ) as GroupByItem;
+
+ Debug.Assert( groupByItem != null );
+ if( groupByItem == null )
+ throw new DataGridInternalException();
+
+ groupByItem.HideDropMark();
+ }
+ else
+ {
+ this.HideDropMark();
+ }
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl parentGrid = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ GroupingHelper.AppendNewGroupFromColumnManagerCell( cell, parentGrid );
+
+ }
+
+ #endregion
+
+ #region PRIVATE FIELDS
+
+ private DropMarkAdorner m_dropMarkAdorner;
+
+ #endregion
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupByItem.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupByItem.cs
new file mode 100644
index 00000000..eb405cf3
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupByItem.cs
@@ -0,0 +1,542 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using System.Windows.Media;
+using System.ComponentModel;
+using System.Windows.Input;
+using System.Windows.Data;
+using System.Collections.ObjectModel;
+using System.Windows.Controls.Primitives;
+using System.Windows.Resources;
+using System.Security;
+using Xceed.Wpf.DataGrid.Views;
+
+using Xceed.Utils.Wpf.DragDrop;
+using Xceed.Utils.Wpf;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class GroupByItem : ButtonBase, IDropTarget, INotifyPropertyChanged
+ {
+ #region PUBLIC CONSTRUCTORS
+
+ static GroupByItem()
+ {
+ // This DefaultStyleKey will only be used in design-time.
+ DefaultStyleKeyProperty.OverrideMetadata( typeof( GroupByItem ), new FrameworkPropertyMetadata( new Markup.ThemeKey( typeof( Views.TableView ), typeof( GroupByItem ) ) ) );
+
+ GroupByItem.IsBeingDraggedProperty = GroupByItem.IsBeingDraggedPropertyKey.DependencyProperty;
+
+ FocusableProperty.OverrideMetadata( typeof( GroupByItem ), new FrameworkPropertyMetadata( false ) );
+ }
+
+ #endregion
+
+ #region SortDirection Property
+
+ // Only used to bind between Column and us, but we don't want to expose it publicly
+ private static readonly DependencyProperty SortDirectionInternalProperty =
+ DependencyProperty.Register( "SortDirectionInternal", typeof( SortDirection ), typeof( GroupByItem ), new PropertyMetadata( SortDirection.None, new PropertyChangedCallback( GroupByItem.OnSortDirectionInternalChanged ) ) );
+
+ public SortDirection SortDirection
+ {
+ get
+ {
+ return ( SortDirection )this.GetValue( GroupByItem.SortDirectionInternalProperty );
+ }
+ }
+
+ private static void OnSortDirectionInternalChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ GroupByItem groupByItem = ( GroupByItem )sender;
+ groupByItem.OnPropertyChanged( new PropertyChangedEventArgs( "SortDirection" ) );
+ }
+
+ #endregion SortDirection Property
+
+ #region IsBeingDragged Read-Only Property
+
+ private static readonly DependencyPropertyKey IsBeingDraggedPropertyKey =
+ DependencyProperty.RegisterReadOnly( "IsBeingDragged", typeof( bool ), typeof( GroupByItem ), new PropertyMetadata( false ) );
+
+ public static readonly DependencyProperty IsBeingDraggedProperty;
+
+ public bool IsBeingDragged
+ {
+ get
+ {
+ return ( bool )this.GetValue( GroupByItem.IsBeingDraggedProperty );
+ }
+ }
+
+ private void SetIsBeingDragged( bool value )
+ {
+ this.SetValue( GroupByItem.IsBeingDraggedPropertyKey, value );
+ }
+
+ #endregion IsBeingDragged Read-Only Property
+
+ #region PUBLIC METHODS
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+ this.InitSortDirection();
+ this.SetupDragManager();
+ }
+
+ #endregion
+
+ #region PROTECTED METHODS
+
+ protected internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( GroupByItem ) );
+ }
+
+ #endregion
+
+ #region PRIVATE METHODS
+
+ private void InitSortDirection()
+ {
+ DataGridContext gridContext = DataGridControl.GetDataGridContext( this );
+ GroupLevelDescription groupInfo = this.Content as GroupLevelDescription;
+
+ Debug.Assert( ( gridContext != null ) && ( groupInfo != null ) || ( DesignerProperties.GetIsInDesignMode( this ) ) );
+ if( ( gridContext != null ) && ( groupInfo != null ) )
+ {
+ ColumnBase column = gridContext.Columns[ groupInfo.FieldName ];
+
+ if( column != null )
+ {
+ Binding sortBinding = new Binding();
+ sortBinding.Path = new PropertyPath( ColumnBase.SortDirectionProperty );
+ sortBinding.Mode = BindingMode.OneWay;
+ sortBinding.NotifyOnSourceUpdated = true;
+ sortBinding.Source = column;
+ sortBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
+
+ BindingOperations.SetBinding( this, GroupByItem.SortDirectionInternalProperty, sortBinding );
+ }
+ }
+ }
+
+ private GroupByControl GetParentGroupByControl()
+ {
+ DependencyObject parent = TreeHelper.GetParent( this );
+
+ while( parent != null )
+ {
+ if( parent is GroupByControl )
+ break;
+
+ parent = TreeHelper.GetParent( parent );
+ }
+
+ return parent as GroupByControl;
+ }
+
+ #endregion
+
+ #region Drag & Drop Manager
+
+ private void SetupDragManager()
+ {
+ // We do not support DragDrop when there are no AdornerLayer because there wouldn't
+ // be any visual feedback for the operation.
+ if( AdornerLayer.GetAdornerLayer( this ) == null )
+ return;
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl dataGridControl = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ // Can be null in design-time (edition of a style TargetType GroupByItem).
+ if( dataGridControl == null )
+ return;
+
+ Debug.Assert( m_dragSourceManager == null, "There might be problems when there is already a m_dragSourceManager." );
+
+ if( m_dragSourceManager != null )
+ {
+ m_dragSourceManager.PropertyChanged -= new PropertyChangedEventHandler( m_dragSourceManager_PropertyChanged );
+ m_dragSourceManager.DragOutsideQueryCursor -= new QueryCursorEventHandler( m_dragSourceManager_DragOutsideQueryCursor );
+ m_dragSourceManager.DroppedOutside -= new EventHandler( m_dragSourceManager_DroppedOutside );
+ }
+
+ // The DataGridControl's AdornerDecoratorForDragAndDrop must be used for dragging in order to include the
+ // RenderTransform the DataGridControl may performs. This AdornerDecorator is defined in the ControlTemplate
+ // as PART_DragDropAdornerDecorator
+ if( ( dataGridControl.DragDropAdornerDecorator != null )
+ && ( dataGridControl.DragDropAdornerDecorator.AdornerLayer != null ) )
+ {
+ m_dragSourceManager = new DragSourceManager( this, dataGridControl.DragDropAdornerDecorator.AdornerLayer, dataGridControl );
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert( false, "The drag and drop functionnality won't be fully working properly: PART_DragDropAdornerDecorator was not found" );
+ m_dragSourceManager = new DragSourceManager( this, null, dataGridControl );
+ }
+
+ m_dragSourceManager.PropertyChanged += new PropertyChangedEventHandler( m_dragSourceManager_PropertyChanged );
+ m_dragSourceManager.DragOutsideQueryCursor += new QueryCursorEventHandler( m_dragSourceManager_DragOutsideQueryCursor );
+ m_dragSourceManager.DroppedOutside += new EventHandler( m_dragSourceManager_DroppedOutside );
+ }
+
+ void m_dragSourceManager_DroppedOutside( object sender, EventArgs e )
+ {
+ bool allowGroupingModification = true;
+ GroupByControl parentGBC = this.GetParentGroupByControl();
+
+ if( parentGBC != null )
+ allowGroupingModification = parentGBC.IsGroupingModificationAllowed;
+
+ if( allowGroupingModification )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+ Debug.Assert( dataGridContext != null );
+
+ if( dataGridContext != null )
+ {
+ GroupingHelper.RemoveGroupDescription(
+ dataGridContext.Items.GroupDescriptions,
+ this.Content as GroupLevelDescription,
+ dataGridContext.DataGridControl );
+ }
+ }
+ }
+
+ void m_dragSourceManager_PropertyChanged( object sender, PropertyChangedEventArgs e )
+ {
+ if( e.PropertyName == "IsDragging" )
+ {
+ this.SetIsBeingDragged( m_dragSourceManager.IsDragging );
+ }
+ }
+
+ private void m_dragSourceManager_DragOutsideQueryCursor( object sender, QueryCursorEventArgs e )
+ {
+ GroupByControl parentGBC = this.GetParentGroupByControl();
+
+ if( ( parentGBC == null ) || !parentGBC.IsGroupingModificationAllowed )
+ return;
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( ( dataGridContext != null ) && ( dataGridContext.DataGridControl != null ) )
+ {
+ UIViewBase uiViewBase = dataGridContext.DataGridControl.GetView() as UIViewBase;
+
+ e.Cursor = ( uiViewBase != null )
+ ? uiViewBase.RemovingGroupCursor
+ : UIViewBase.DefaultGroupDraggedOutsideCursor;
+ }
+ else
+ {
+ e.Cursor = UIViewBase.DefaultGroupDraggedOutsideCursor;
+ }
+ }
+
+ protected override void OnMouseLeftButtonDown( MouseButtonEventArgs e )
+ {
+ if( this.CaptureMouse() )
+ {
+ if( m_dragSourceManager != null )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext != null )
+ {
+ // Update the DropOutsideCursor since it is defined on the View
+ UIViewBase uiViewBase = dataGridContext.DataGridControl.GetView() as UIViewBase;
+
+ m_dragSourceManager.DropOutsideCursor = ( uiViewBase != null )
+ ? uiViewBase.RemovingGroupCursor
+ : UIViewBase.DefaultGroupDraggedOutsideCursor;
+ }
+
+ m_dragSourceManager.ProcessMouseLeftButtonDown( e );
+ }
+
+ e.Handled = true;
+ }
+
+ base.OnMouseLeftButtonDown( e );
+ }
+
+ protected override void OnMouseMove( MouseEventArgs e )
+ {
+ if( ( this.IsMouseCaptured ) && ( e.LeftButton == MouseButtonState.Pressed ) )
+ {
+ if( m_dragSourceManager != null )
+ m_dragSourceManager.ProcessMouseMove( e );
+ }
+
+ base.OnMouseMove( e );
+ }
+
+ protected override void OnMouseLeftButtonUp( MouseButtonEventArgs e )
+ {
+ bool isMouseCaptured = this.IsMouseCaptured;
+ bool isPressed = this.IsPressed;
+
+ if( m_dragSourceManager != null )
+ m_dragSourceManager.ProcessMouseLeftButtonUp( e );
+
+ if( isMouseCaptured )
+ {
+ bool click = isPressed;
+
+ if( click )
+ {
+ bool allowSort = true;
+ GroupByControl parentGBC = this.GetParentGroupByControl();
+
+ if( parentGBC != null )
+ allowSort = parentGBC.AllowSort;
+
+ if( allowSort )
+ {
+ DataGridContext gridContext = DataGridControl.GetDataGridContext( this );
+ GroupLevelDescription groupInfo = this.Content as GroupLevelDescription;
+ Debug.Assert( ( gridContext != null ) && ( groupInfo != null ) );
+
+ if( ( gridContext != null ) && ( groupInfo != null ) )
+ {
+ ColumnBase column = gridContext.Columns[ groupInfo.FieldName ];
+
+ if( ( column != null ) && ( column.AllowSort ) )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+ SortDescriptionCollection sortDescriptions = dataGridContext.Items.SortDescriptions;
+ ColumnCollection columns = dataGridContext.Columns;
+
+ SortingHelper.ToggleColumnSort(
+ dataGridContext, sortDescriptions,
+ columns, column, ( ( Keyboard.Modifiers & ModifierKeys.Shift ) != ModifierKeys.Shift ) );
+
+ e.Handled = true;
+ }
+ }
+ }
+ }
+ }
+
+ base.OnMouseLeftButtonUp( e );
+ }
+
+ protected override void OnLostMouseCapture( MouseEventArgs e )
+ {
+ if( m_dragSourceManager != null )
+ m_dragSourceManager.ProcessLostMouseCapture( e );
+
+ base.OnLostMouseCapture( e );
+ }
+
+ internal void ShowDropMark( Point mousePosition )
+ {
+ if( m_dropMarkAdorner == null )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl grid = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ Pen pen = UIViewBase.GetDropMarkPen( this );
+
+ if( ( pen == null ) && ( grid != null ) )
+ {
+ UIViewBase uiViewBase = grid.GetView() as UIViewBase;
+ pen = uiViewBase.DefaultDropMarkPen;
+ }
+
+ DropMarkOrientation orientation = UIViewBase.GetDropMarkOrientation( this );
+
+ if( ( orientation == DropMarkOrientation.Default ) && ( grid != null ) )
+ {
+ UIViewBase uiViewBase = grid.GetView() as UIViewBase;
+
+ orientation = uiViewBase.DefaultDropMarkOrientation;
+ }
+
+ m_dropMarkAdorner = new DropMarkAdorner( this, pen, orientation );
+
+ AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer( this );
+
+ if( adornerLayer != null )
+ adornerLayer.Add( m_dropMarkAdorner );
+ }
+
+ m_dropMarkAdorner.UpdateAlignment( mousePosition );
+ }
+
+ internal void HideDropMark()
+ {
+ if( m_dropMarkAdorner != null )
+ {
+ AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer( this );
+
+ if( adornerLayer != null )
+ adornerLayer.Remove( m_dropMarkAdorner );
+
+ m_dropMarkAdorner = null;
+ }
+ }
+
+ #endregion Drag & Drop Manager
+
+ #region IDropTarget Members
+
+ bool IDropTarget.CanDropElement( UIElement draggedElement )
+ {
+ bool allowGroupingModification = true;
+ GroupByControl parentGBC = this.GetParentGroupByControl();
+
+ if( parentGBC != null )
+ allowGroupingModification = parentGBC.IsGroupingModificationAllowed;
+
+ // We don't accept any ColumnManagerCell from Details
+ DataGridContext context = DataGridControl.GetDataGridContext( draggedElement );
+
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ bool isAlreadyGroupedBy = false;
+
+ if( cell != null )
+ {
+ isAlreadyGroupedBy = GroupingHelper.IsAlreadyGroupedBy( cell );
+ ColumnBase parentColumn = cell.ParentColumn;
+
+ if( ( parentColumn == null ) || ( !parentColumn.AllowGroup ) )
+ return false;
+ }
+
+ DataGridContext sourceDetailContext = DataGridControl.GetDataGridContext( this );
+ Debug.Assert( sourceDetailContext != null );
+ DetailConfiguration sourceDetailConfig = ( sourceDetailContext != null ) ? sourceDetailContext.SourceDetailConfiguration : null;
+
+ DataGridContext draggedDetailContext = DataGridControl.GetDataGridContext( draggedElement );
+ Debug.Assert( draggedDetailContext != null );
+ DetailConfiguration draggedDetailConfig = ( draggedDetailContext != null ) ? draggedDetailContext.SourceDetailConfiguration : null;
+
+
+ bool canDrop = ( sourceDetailConfig == draggedDetailConfig ) &&
+ ( allowGroupingModification ) &&
+ ( ( draggedElement is ColumnManagerCell ) || ( draggedElement is GroupByItem ) ) &&
+ ( draggedElement != this ) &&
+ ( isAlreadyGroupedBy == false );
+
+ if( canDrop && ( cell != null ) )
+ canDrop = GroupingHelper.ValidateMaxGroupDescriptions( draggedDetailContext );
+
+ return canDrop;
+ }
+
+ void IDropTarget.DragEnter( UIElement draggedElement )
+ {
+ }
+
+ void IDropTarget.DragOver( UIElement draggedElement, Point mousePosition )
+ {
+ this.ShowDropMark( mousePosition );
+ }
+
+ void IDropTarget.DragLeave( UIElement draggedElement )
+ {
+ this.HideDropMark();
+ }
+
+ void IDropTarget.Drop( UIElement draggedElement )
+ {
+ if( m_dropMarkAdorner != null )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ Debug.Assert( dataGridContext != null );
+ if( dataGridContext != null )
+ {
+ GroupLevelDescription draggedOverGroupLevelDescription = this.Content as GroupLevelDescription;
+
+ Debug.Assert( draggedOverGroupLevelDescription != null );
+ if( draggedOverGroupLevelDescription != null )
+ {
+ DropMarkAlignment alignment = m_dropMarkAdorner.Alignment;
+
+ this.HideDropMark();
+
+ ColumnManagerCell draggedCell = draggedElement as ColumnManagerCell;
+
+ if( draggedCell != null )
+ {
+ GroupingHelper.AddNewGroupFromColumnManagerCell( draggedCell, draggedOverGroupLevelDescription, alignment, dataGridContext.DataGridControl );
+ }
+ else
+ {
+ GroupByItem draggedGroupBy = draggedElement as GroupByItem;
+
+ Debug.Assert( draggedGroupBy != null );
+
+ if( draggedGroupBy != null )
+ {
+ GroupLevelDescription draggedGroupLevelDescription = draggedGroupBy.Content as GroupLevelDescription;
+
+ GroupingHelper.MoveGroupDescription( dataGridContext.Columns, dataGridContext.Items.GroupDescriptions, draggedOverGroupLevelDescription, alignment, draggedGroupLevelDescription, dataGridContext.DataGridControl );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region INotifyPropertyChanged Members
+
+ protected virtual void OnPropertyChanged( PropertyChangedEventArgs e )
+ {
+ if( this.PropertyChanged != null )
+ this.PropertyChanged( this, e );
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ #endregion
+
+ #region PRIVATE FIELDS
+
+ ///
+ /// Will remain null when no AdornerLayer is found.
+ ///
+ private DragSourceManager m_dragSourceManager;
+ private DropMarkAdorner m_dropMarkAdorner;
+
+ #endregion
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupConfiguration.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupConfiguration.cs
new file mode 100644
index 00000000..cfd06aa2
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupConfiguration.cs
@@ -0,0 +1,250 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System.Collections.ObjectModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.ComponentModel;
+using System;
+using System.Windows.Data;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class GroupConfiguration : Freezable
+ {
+ static GroupConfiguration()
+ {
+ GroupConfiguration.HeadersProperty = GroupConfiguration.HeadersPropertyKey.DependencyProperty;
+ GroupConfiguration.FootersProperty = GroupConfiguration.FootersPropertyKey.DependencyProperty;
+
+ DefaultHeaderTemplate = new GroupHeaderFooterItemTemplate();
+ DefaultHeaderTemplate.VisibleWhenCollapsed = true;
+ DefaultHeaderTemplate.Template = new DataTemplate();
+ DefaultHeaderTemplate.Template.VisualTree = new FrameworkElementFactory( typeof( GroupHeaderControl ) );
+ DefaultHeaderTemplate.Template.Seal();
+ DefaultHeaderTemplate.Seal();
+
+ DefaultGroupConfiguration = new GroupConfiguration();
+ DefaultGroupConfiguration.AddDefaultHeadersFooters();
+ DefaultGroupConfiguration.Freeze();
+ }
+
+ internal static readonly GroupConfiguration DefaultGroupConfiguration;
+
+ public GroupConfiguration()
+ {
+ this.SetHeaders( new GroupHeaderFooterCollection() );
+ this.SetFooters( new GroupHeaderFooterCollection() );
+ }
+
+ #region Headers Read-Only Property
+
+ private static readonly DependencyPropertyKey HeadersPropertyKey =
+ DependencyProperty.RegisterReadOnly( "Headers", typeof( ObservableCollection ), typeof( GroupConfiguration ), new PropertyMetadata( null ) );
+
+ public static readonly DependencyProperty HeadersProperty;
+
+ public ObservableCollection Headers
+ {
+ get
+ {
+ return ( ObservableCollection )this.GetValue( GroupConfiguration.HeadersProperty );
+ }
+ }
+
+ private void SetHeaders( ObservableCollection value )
+ {
+ this.SetValue( GroupConfiguration.HeadersPropertyKey, value );
+ }
+
+ #endregion Headers Read-Only Property
+
+ #region Footers Read-Only Property
+
+ private static readonly DependencyPropertyKey FootersPropertyKey =
+ DependencyProperty.RegisterReadOnly( "Footers", typeof( ObservableCollection ), typeof( GroupConfiguration ), new PropertyMetadata( null ) );
+
+ public static readonly DependencyProperty FootersProperty;
+
+ public ObservableCollection Footers
+ {
+ get
+ {
+ return ( ObservableCollection )this.GetValue( GroupConfiguration.FootersProperty );
+ }
+ }
+
+ private void SetFooters( ObservableCollection value )
+ {
+ this.SetValue( GroupConfiguration.FootersPropertyKey, value );
+ }
+
+ #endregion Footers Read-Only Property
+
+ #region InitiallyExpanded Property
+
+ public static readonly DependencyProperty InitiallyExpandedProperty =
+ DependencyProperty.Register( "InitiallyExpanded", typeof( bool ), typeof( GroupConfiguration ), new UIPropertyMetadata( true ) );
+
+ public bool InitiallyExpanded
+ {
+ get
+ {
+ return ( bool )this.GetValue( GroupConfiguration.InitiallyExpandedProperty );
+ }
+ set
+ {
+ this.SetValue( GroupConfiguration.InitiallyExpandedProperty, value );
+ }
+ }
+
+ #endregion InitiallyExpanded Property
+
+ #region ItemContainerStyle Property
+
+ public static readonly DependencyProperty ItemContainerStyleProperty = DataGridControl.ItemContainerStyleProperty.AddOwner( typeof( GroupConfiguration ) );
+
+ public Style ItemContainerStyle
+ {
+ get
+ {
+ return ( Style )this.GetValue( GroupConfiguration.ItemContainerStyleProperty );
+ }
+ set
+ {
+ this.SetValue( GroupConfiguration.ItemContainerStyleProperty, value );
+ }
+ }
+
+ #endregion ItemContainerStyle Property
+
+ #region ItemContainerStyleSelector Property
+
+ public static readonly DependencyProperty ItemContainerStyleSelectorProperty = DataGridControl.ItemContainerStyleSelectorProperty.AddOwner( typeof( GroupConfiguration ) );
+
+ public StyleSelector ItemContainerStyleSelector
+ {
+ get
+ {
+ return ( StyleSelector )this.GetValue( GroupConfiguration.ItemContainerStyleSelectorProperty );
+ }
+ set
+ {
+ this.SetValue( GroupConfiguration.ItemContainerStyleSelectorProperty, value );
+ }
+ }
+
+ #endregion ItemContainerStyleSelector Property
+
+ #region GroupLevelIndicatorStyle Property
+
+ public static readonly DependencyProperty GroupLevelIndicatorStyleProperty =
+ DependencyProperty.Register( "GroupLevelIndicatorStyle", typeof( Style ), typeof( GroupConfiguration ), new UIPropertyMetadata( null ) );
+
+ public Style GroupLevelIndicatorStyle
+ {
+ get
+ {
+ return ( Style )this.GetValue( GroupConfiguration.GroupLevelIndicatorStyleProperty );
+ }
+ set
+ {
+ this.SetValue( GroupConfiguration.GroupLevelIndicatorStyleProperty, value );
+ }
+ }
+
+ #endregion GroupLevelIndicatorStyle Property
+
+ #region UseDefaultHeadersFooters Property
+
+ public static readonly DependencyProperty UseDefaultHeadersFootersProperty =
+ DependencyProperty.Register( "UseDefaultHeadersFooters", typeof( bool ), typeof( GroupConfiguration ), new PropertyMetadata( true ) );
+
+ public bool UseDefaultHeadersFooters
+ {
+ get
+ {
+ return ( bool )this.GetValue( GroupConfiguration.UseDefaultHeadersFootersProperty );
+ }
+ set
+ {
+ this.SetValue( GroupConfiguration.UseDefaultHeadersFootersProperty, value );
+ }
+ }
+
+ #endregion UseDefaultHeadersFooters Property
+
+ internal void AddDefaultHeadersFooters()
+ {
+ if( m_defaultHeadersFootersAdded )
+ return;
+
+ m_defaultHeadersFootersAdded = true;
+ this.Headers.Insert( 0, GroupConfiguration.DefaultHeaderTemplate );
+ }
+
+ internal static GroupConfiguration GetGroupConfiguration( DataGridContext dataGridContext, ObservableCollection groupDescriptions, GroupConfigurationSelector groupConfigSelector, int groupLevel, CollectionViewGroup collectionViewGroup )
+ {
+ if( dataGridContext == null )
+ throw new ArgumentNullException( "dataGridContext" );
+
+ if( groupDescriptions == null )
+ throw new ArgumentNullException( "groupDescriptions" );
+
+ if( groupLevel >= groupDescriptions.Count )
+ throw new ArgumentException( "The specified group level is greater than the number of GroupDescriptions in the DataGridContext.", "groupLevel" );
+
+ GroupDescription groupDescription = groupDescriptions[ groupLevel ];
+
+ GroupConfiguration retval = null;
+ DataGridGroupDescription dataGridGroupDescription = groupDescription as DataGridGroupDescription;
+
+ if( ( dataGridGroupDescription != null ) && ( dataGridGroupDescription.GroupConfiguration != null ) )
+ {
+ retval = dataGridGroupDescription.GroupConfiguration;
+ }
+ else if( groupConfigSelector != null )
+ {
+ retval = groupConfigSelector.SelectGroupConfiguration( groupLevel, collectionViewGroup, groupDescription );
+ }
+
+ if( retval == null )
+ {
+ retval = dataGridContext.DefaultGroupConfiguration;
+ }
+
+ if( retval == null )
+ {
+ retval = GroupConfiguration.DefaultGroupConfiguration;
+ }
+
+ return retval;
+ }
+
+ protected override Freezable CreateInstanceCore()
+ {
+ return new GroupConfiguration();
+ }
+
+
+ private bool m_defaultHeadersFootersAdded; // = false
+ private static readonly GroupHeaderFooterItemTemplate DefaultHeaderTemplate;
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupConfigurationSelector.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupConfigurationSelector.cs
new file mode 100644
index 00000000..e0254185
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupConfigurationSelector.cs
@@ -0,0 +1,35 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.ComponentModel;
+using System.Windows.Data;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public abstract class GroupConfigurationSelector
+ {
+ public virtual GroupConfiguration SelectGroupConfiguration( int groupLevel, CollectionViewGroup collectionViewGroup, GroupDescription groupDescription )
+ {
+ return null;
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupConfigurationSelectorChangedEventManager.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupConfigurationSelectorChangedEventManager.cs
new file mode 100644
index 00000000..1cd9298d
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupConfigurationSelectorChangedEventManager.cs
@@ -0,0 +1,119 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class GroupConfigurationSelectorChangedEventManager : WeakEventManager
+ {
+ private GroupConfigurationSelectorChangedEventManager()
+ {
+ }
+
+ public static void AddListener( object source, IWeakEventListener listener)
+ {
+ CurrentManager.ProtectedAddListener( source, listener );
+ }
+
+ public static void RemoveListener( object source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedRemoveListener( source, listener );
+ }
+
+ protected override void StartListening( object source )
+ {
+ DataGridContext context = source as DataGridContext;
+
+ if (context != null)
+ {
+ context.GroupConfigurationSelectorChanged += new EventHandler(this.OnGroupConfigurationSelectorChanged);
+ return;
+ }
+
+ DataGridControl dataGridControl = source as DataGridControl;
+ if (dataGridControl != null)
+ {
+ dataGridControl.GroupConfigurationSelectorChanged += new EventHandler(this.OnGroupConfigurationSelectorChanged);
+ return;
+ }
+
+ DetailConfiguration detailConfig = source as DetailConfiguration;
+ if (detailConfig != null)
+ {
+ detailConfig.GroupConfigurationSelectorChanged += new EventHandler(this.OnGroupConfigurationSelectorChanged);
+ return;
+ }
+
+ throw new InvalidOperationException("An attempt was made to register an item other than a DataGridContext, DataGridControl, or DetailConfiguration to the GroupConfigurationSelectorChangedEventManager.");
+ }
+
+ protected override void StopListening(object source)
+ {
+ DataGridContext context = source as DataGridContext;
+
+ if (context != null)
+ {
+ context.GroupConfigurationSelectorChanged -= new EventHandler(this.OnGroupConfigurationSelectorChanged);
+ return;
+ }
+
+ DataGridControl dataGridControl = source as DataGridControl;
+ if (dataGridControl != null)
+ {
+ dataGridControl.GroupConfigurationSelectorChanged -= new EventHandler(this.OnGroupConfigurationSelectorChanged);
+ return;
+ }
+
+ DetailConfiguration detailConfig = source as DetailConfiguration;
+ if (detailConfig != null)
+ {
+ detailConfig.GroupConfigurationSelectorChanged -= new EventHandler(this.OnGroupConfigurationSelectorChanged);
+ return;
+ }
+
+ throw new InvalidOperationException( "An attempt was made to unregister an item other than a DataGridContext, DataGridControl, or DetailConfiguration from the GroupConfigurationSelectorChangedEventManager." );
+ }
+
+ private static GroupConfigurationSelectorChangedEventManager CurrentManager
+ {
+ get
+ {
+ Type managerType = typeof( GroupConfigurationSelectorChangedEventManager );
+ GroupConfigurationSelectorChangedEventManager currentManager = ( GroupConfigurationSelectorChangedEventManager )WeakEventManager.GetCurrentManager( managerType );
+
+ if( currentManager == null )
+ {
+ currentManager = new GroupConfigurationSelectorChangedEventManager();
+ WeakEventManager.SetCurrentManager( managerType, currentManager );
+ }
+
+ return currentManager;
+ }
+ }
+
+ private void OnGroupConfigurationSelectorChanged( object sender, EventArgs args )
+ {
+ this.DeliverEvent( sender, args );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupExtensions.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupExtensions.cs
new file mode 100644
index 00000000..eb9d5fdb
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupExtensions.cs
@@ -0,0 +1,40 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Data;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public static class GroupExtensions
+ {
+ public static IList GetItems( this Group group )
+ {
+ CollectionViewGroup collectionViewGroup = group.CollectionViewGroup;
+
+ if( collectionViewGroup == null )
+ return null;
+
+ return collectionViewGroup.GetItems();
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupHeaderControl.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupHeaderControl.cs
new file mode 100644
index 00000000..147ad364
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupHeaderControl.cs
@@ -0,0 +1,308 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Automation.Peers;
+using System.Collections.Specialized;
+using System;
+using System.Diagnostics;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class GroupHeaderControl : ContentControl, INotifyPropertyChanged, IDataGridItemContainer
+ {
+ #region Constructors
+
+ static GroupHeaderControl()
+ {
+ // This DefaultStyleKey will only be used in design-time.
+ DefaultStyleKeyProperty.OverrideMetadata( typeof( GroupHeaderControl ), new FrameworkPropertyMetadata( new Markup.ThemeKey( typeof( Views.TableView ), typeof( GroupHeaderControl ) ) ) );
+
+ DataGridControl.ParentDataGridControlPropertyKey.OverrideMetadata( typeof( GroupHeaderControl ), new FrameworkPropertyMetadata( new PropertyChangedCallback( OnParentGridControlChanged ) ) );
+
+ KeyboardNavigation.TabNavigationProperty.OverrideMetadata(
+ typeof( GroupHeaderControl ), new FrameworkPropertyMetadata( KeyboardNavigationMode.None ) );
+ }
+
+ public GroupHeaderControl()
+ {
+ this.CommandBindings.Add( new CommandBinding( DataGridCommands.ExpandGroup,
+ this.OnExpandExecuted,
+ this.OnExpandCanExecute ) );
+
+ this.CommandBindings.Add( new CommandBinding( DataGridCommands.CollapseGroup,
+ this.OnCollapseExecuted,
+ this.OnCollapseCanExecute ) );
+
+ this.CommandBindings.Add( new CommandBinding( DataGridCommands.ToggleGroupExpansion,
+ this.OnToggleExecuted,
+ this.OnToggleCanExecute ) );
+ }
+
+ #endregion
+
+ #region Group Internal Attached Property
+
+ internal static readonly DependencyProperty GroupProperty = DependencyProperty.RegisterAttached(
+ "Group", typeof( Group ), typeof( GroupHeaderControl ),
+ new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.Inherits ) );
+
+ internal static Group GetGroup( DependencyObject obj )
+ {
+ return ( Group )obj.GetValue( GroupHeaderControl.GroupProperty );
+ }
+
+ internal static void SetGroup( DependencyObject obj, Group value )
+ {
+ obj.SetValue( GroupHeaderControl.GroupProperty, value );
+ }
+
+ #endregion
+
+ #region Group Property
+
+ private Group m_group;
+
+ public Group Group
+ {
+ get
+ {
+ return m_group;
+ }
+ }
+
+ internal void SetGroup( Group group )
+ {
+ m_group = group;
+ this.Content = m_group;
+ GroupHeaderControl.SetGroup( this, group );
+
+ if( this.PropertyChanged != null )
+ {
+ this.PropertyChanged( this, new PropertyChangedEventArgs( "Group" ) );
+ }
+ }
+
+ #endregion Group Property
+
+ #region Protected Overrides Methods
+
+ protected override System.Windows.Automation.Peers.AutomationPeer OnCreateAutomationPeer()
+ {
+ return new FrameworkElementAutomationPeer( this );
+ }
+
+ protected override void OnIsKeyboardFocusWithinChanged( DependencyPropertyChangedEventArgs e )
+ {
+ base.OnIsKeyboardFocusWithinChanged( e );
+
+ bool newValue = ( bool )e.NewValue;
+
+ if( newValue == true )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext != null )
+ {
+ object item = dataGridContext.GetItemFromContainer( this );
+
+ if( ( item != null ) && ( dataGridContext.InternalCurrentItem != item ) )
+ {
+ try
+ {
+ dataGridContext.SetCurrent( item, null, -1, dataGridContext.CurrentColumn, true, true, false );
+ }
+ catch( DataGridException )
+ {
+ // We swallow the exception if it occurs because of a validation error or Cell was read-only or
+ // any other GridException.
+ }
+ }
+ }
+ }
+ }
+
+ #endregion
+
+ #region Protected Internal Methods
+
+ protected internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( GroupHeaderControl ) );
+ }
+
+ protected internal virtual void PrepareContainer( DataGridContext dataGridContext, object item )
+ {
+ if( m_isContainerPrepared )
+ Debug.Fail( "A GroupHeaderControl can't be prepared twice, it must be cleaned before PrepareContainer is called again" );
+
+ Group group = null;
+
+ DataGridContext gridContext = DataGridControl.GetDataGridContext( this );
+ if( gridContext != null )
+ {
+ object dataItem = gridContext.GetItemFromContainer( this );
+ if( dataItem != null )
+ {
+ group = gridContext.GetGroupFromItem( dataItem );
+ }
+ }
+
+ this.SetGroup( group );
+
+ m_isContainerPrepared = true;
+ }
+
+ protected internal virtual void ClearContainer()
+ {
+ //Do nothing!
+ m_isContainerPrepared = false;
+ }
+
+ #endregion
+
+ #region Private Static Methods
+
+ private static void OnParentGridControlChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ DataGridControl grid = e.NewValue as DataGridControl;
+ GroupHeaderControl groupHeaderControl = sender as GroupHeaderControl;
+
+ if( ( groupHeaderControl != null ) && ( grid != null ) )
+ {
+ groupHeaderControl.PrepareDefaultStyleKey( grid.GetView() );
+ }
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private void OnExpandCanExecute( object sender, CanExecuteRoutedEventArgs e )
+ {
+ e.CanExecute = false;
+
+ if( e.Parameter == null )
+ {
+ //can execute the Expand command if the group is NOT expanded.
+ e.CanExecute = !this.FindGroupCommandTarget( e.OriginalSource ).IsExpanded;
+ }
+ }
+
+ private void OnExpandExecuted( object sender, ExecutedRoutedEventArgs e )
+ {
+ this.FindGroupCommandTarget( e.OriginalSource ).IsExpanded = true;
+ }
+
+ private void OnCollapseCanExecute( object sender, CanExecuteRoutedEventArgs e )
+ {
+ e.CanExecute = false;
+
+ if( e.Parameter == null )
+ {
+ //can execute the Collapse command if the group is expanded.
+ e.CanExecute = this.FindGroupCommandTarget( e.OriginalSource ).IsExpanded;
+ }
+ }
+
+ private void OnCollapseExecuted( object sender, ExecutedRoutedEventArgs e )
+ {
+ this.FindGroupCommandTarget( e.OriginalSource ).IsExpanded = false;
+ }
+
+ private void OnToggleCanExecute( object sender, CanExecuteRoutedEventArgs e )
+ {
+ e.CanExecute = false;
+
+ if( e.Parameter == null )
+ {
+ e.CanExecute = true; //can always toggle
+ }
+ }
+
+ private void OnToggleExecuted( object sender, ExecutedRoutedEventArgs e )
+ {
+ Group group = FindGroupCommandTarget( e.OriginalSource );
+
+ group.IsExpanded = !group.IsExpanded;
+ }
+
+ // We use an ItemsControl inside the GroupHeaderControl to represent the
+ // ancestors (ParentGroups) of this.Group, and each item in this ItemsControl
+ // is a Group templated to look like a single, stand-alone GroupHeaderControl.
+ //
+ // In the ItemTemplate for each Group in the ItemsControl, we have a Border that
+ // declares some InputBindings to the group commands. In this case, we want to
+ // execute the command on this specific instance of Group, which is the DataContext
+ // of the Border.
+ private Group FindGroupCommandTarget( object originalSource )
+ {
+ object dataContext = null;
+
+ FrameworkElement fe = originalSource as FrameworkElement;
+ if( fe != null )
+ {
+ dataContext = fe.DataContext;
+ }
+ else
+ {
+ FrameworkContentElement fce = originalSource as FrameworkContentElement;
+ if( fce != null )
+ {
+ dataContext = fce.DataContext;
+ }
+ }
+
+ Group groupCommandTarget = dataContext as Group;
+
+ return ( groupCommandTarget != null ) ? groupCommandTarget : this.Group;
+ }
+
+ #endregion
+
+ #region Private Fields
+
+ private bool m_isContainerPrepared;
+
+ #endregion Private Fields
+
+ #region INotifyPropertyChanged Members
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ #endregion
+
+ #region IDataGridItemContainer Members
+
+ void IDataGridItemContainer.PrepareContainer( DataGridContext dataGridContext, object item )
+ {
+ this.PrepareContainer( dataGridContext, item );
+ }
+
+ void IDataGridItemContainer.ClearContainer()
+ {
+ this.ClearContainer();
+ }
+
+ #endregion
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupHeaderFooterCollection.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupHeaderFooterCollection.cs
new file mode 100644
index 00000000..3a6b2c89
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupHeaderFooterCollection.cs
@@ -0,0 +1,56 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.ObjectModel;
+using System.Windows;
+using Xceed.Wpf.DataGrid.Markup;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class GroupHeaderFooterCollection : ObservableCollection
+ {
+ protected override void InsertItem( int index, object item )
+ {
+ if( item is DataTemplate )
+ {
+ base.InsertItem( index, item );
+ }
+ else
+ {
+ GroupHeaderFooterItemTemplate vwc = item as GroupHeaderFooterItemTemplate;
+ if( vwc != null )
+ {
+ if(vwc.Template == null)
+ {
+ throw new ArgumentException( "A VisibleWhenCollapsed object must have its Template property set to an instance of a DataTemplate.");
+ }
+ else
+ {
+ base.InsertItem( index, item );
+ }
+ }
+ else
+ {
+ throw new ArgumentException( "Group headers and footers can only receive ClearHeadersFooters, DataTemplate, or VisibleWhenCollapsed objects." );
+ }
+ }
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelConfiguration.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelConfiguration.cs
new file mode 100644
index 00000000..7df0c3ab
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelConfiguration.cs
@@ -0,0 +1,125 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.ObjectModel;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using System.ComponentModel;
+
+namespace Xceed.Wpf.DataGrid
+{
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ [Obsolete( "The GroupLevelConfiguration class is obsolete and has been replaced by the LevelGroupConfigurationSelector class.", true )]
+ public class GroupLevelConfiguration : DependencyObject
+ {
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ [Obsolete( "The GroupLevelConfiguration class is obsolete and has been replaced by the LevelGroupConfigurationSelector class.", true )]
+ public GroupLevelConfiguration()
+ {
+ }
+
+ #region Headers Read-Only Property
+
+ [Browsable( false )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ [Obsolete( "The GroupLevelConfiguration class is obsolete and has been replaced by the LevelGroupConfigurationSelector class.", true )]
+ public static readonly DependencyProperty HeadersProperty;
+
+ [Browsable( false )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ [Obsolete( "The GroupLevelConfiguration class is obsolete and has been replaced by the LevelGroupConfigurationSelector class.", true )]
+ public ObservableCollection Headers
+ {
+ get
+ {
+ return null;
+ }
+ }
+
+ #endregion Headers Read-Only Property
+
+ #region Footers Read-Only Property
+
+ [Browsable( false )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ [Obsolete( "The GroupLevelConfiguration class is obsolete and has been replaced by the LevelGroupConfigurationSelector class.", true )]
+ public static readonly DependencyProperty FootersProperty;
+
+ [Browsable( false )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ [Obsolete( "The GroupLevelConfiguration class is obsolete and has been replaced by the LevelGroupConfigurationSelector class.", true )]
+ public ObservableCollection Footers
+ {
+ get
+ {
+ return null;
+ }
+ }
+
+ #endregion Footers Read-Only Property
+
+ #region InitiallyExpanded Property
+
+ [Browsable( false )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ [Obsolete( "The GroupLevelConfiguration class is obsolete and has been replaced by the LevelGroupConfigurationSelector class.", true )]
+ public static readonly DependencyProperty InitiallyExpandedProperty;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value" ), System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1822:MarkMembersAsStatic" ), Browsable( false )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ [Obsolete( "The GroupLevelConfiguration class is obsolete and has been replaced by the LevelGroupConfigurationSelector class.", true )]
+ public bool InitiallyExpanded
+ {
+ get
+ {
+ return false;
+ }
+ set
+ {
+ }
+ }
+
+ #endregion InitiallyExpanded Property
+
+ #region GroupLevelIndicatorStyle Property
+
+ [Browsable( false )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ [Obsolete( "The GroupLevelConfiguration class is obsolete and has been replaced by the LevelGroupConfigurationSelector class.", true )]
+ public static readonly DependencyProperty GroupLevelIndicatorStyleProperty;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "value" ), System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1822:MarkMembersAsStatic" ), Browsable( false )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ [Obsolete( "The GroupLevelConfiguration class is obsolete and has been replaced by the LevelGroupConfigurationSelector class.", true )]
+ public Style GroupLevelIndicatorStyle
+ {
+ get
+ {
+ return null;
+ }
+ set
+ {
+ }
+ }
+
+ #endregion GroupLevelIndicatorStyle Property
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelConfigurationCollection.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelConfigurationCollection.cs
new file mode 100644
index 00000000..a1c0e24f
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelConfigurationCollection.cs
@@ -0,0 +1,33 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.ObjectModel;
+using Xceed.Wpf.DataGrid.Views;
+using Xceed.Wpf.DataGrid.Markup;
+using System.ComponentModel;
+
+namespace Xceed.Wpf.DataGrid
+{
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ [Obsolete( "The GroupLevelConfigurationCollection class is obsolete and has been replaced by the LevelGroupConfigurationSelectorItemCollection class.", true )]
+ public class GroupLevelConfigurationCollection : ObservableCollection
+ {
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelDescription.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelDescription.cs
new file mode 100644
index 00000000..57d43882
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelDescription.cs
@@ -0,0 +1,193 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.ComponentModel;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class GroupLevelDescription : DependencyObject, INotifyPropertyChanged
+ {
+ #region CONSTRUCTORS
+
+ internal GroupLevelDescription( GroupDescription groupDescription, string fieldName )
+ {
+ if( groupDescription == null )
+ throw new DataGridInternalException( "GroupDescription cannot be null." );
+
+ m_groupDescription = groupDescription;
+ m_fieldName = fieldName;
+ }
+
+ #endregion
+
+ #region FieldName Property
+
+ public string FieldName
+ {
+ get
+ {
+ return m_fieldName;
+ }
+ }
+
+ #endregion
+
+ #region GroupDescription Property
+
+ internal GroupDescription GroupDescription
+ {
+ get
+ {
+ return m_groupDescription;
+ }
+ }
+
+ #endregion
+
+ #region Title Property
+
+ internal static readonly DependencyProperty TitleProperty =
+ DependencyProperty.Register( "Title", typeof( object ), typeof( GroupLevelDescription ), new UIPropertyMetadata( null, new PropertyChangedCallback( GroupPropertiesChangedHandler ) ) );
+
+ public object Title
+ {
+ get
+ {
+ return ( object )this.GetValue( GroupLevelDescription.TitleProperty );
+ }
+ }
+
+ internal void SetTitle( object title )
+ {
+ this.SetValue( GroupLevelDescription.TitleProperty, title );
+ }
+
+ #endregion Title Property
+
+ #region TitleTemplate Property
+
+ internal static readonly DependencyProperty TitleTemplateProperty =
+ DependencyProperty.Register( "TitleTemplate", typeof( DataTemplate ), typeof( GroupLevelDescription ), new UIPropertyMetadata( null, new PropertyChangedCallback( GroupPropertiesChangedHandler ) ) );
+
+ public DataTemplate TitleTemplate
+ {
+ get
+ {
+ return ( DataTemplate )this.GetValue( GroupLevelDescription.TitleTemplateProperty );
+ }
+ }
+
+ internal void SetTitleTemplate( DataTemplate titleTemplate )
+ {
+ this.SetValue( GroupLevelDescription.TitleTemplateProperty, titleTemplate );
+ }
+
+ #endregion TitleTemplate Property
+
+ #region TitleTemplateSelector Property
+
+ internal static readonly DependencyProperty TitleTemplateSelectorProperty =
+ DependencyProperty.Register( "TitleTemplateSelector", typeof( DataTemplateSelector ), typeof( GroupLevelDescription ), new UIPropertyMetadata( null, new PropertyChangedCallback( GroupPropertiesChangedHandler ) ) );
+
+ public DataTemplateSelector TitleTemplateSelector
+ {
+ get
+ {
+ return ( DataTemplateSelector )this.GetValue( GroupLevelDescription.TitleTemplateSelectorProperty );
+ }
+ }
+
+ internal void SetTitleTemplateSelector( DataTemplateSelector titleTemplateSelector )
+ {
+ this.SetValue( GroupLevelDescription.TitleTemplateSelectorProperty, titleTemplateSelector );
+ }
+
+ #endregion TitleTemplateSelector Property
+
+ #region ValueTemplate Property
+
+ public static readonly DependencyProperty ValueTemplateProperty =
+ DependencyProperty.Register( "ValueTemplate", typeof( DataTemplate ), typeof( GroupLevelDescription ), new UIPropertyMetadata( null, new PropertyChangedCallback( GroupPropertiesChangedHandler ) ) );
+
+ public DataTemplate ValueTemplate
+ {
+ get
+ {
+ return ( DataTemplate )this.GetValue( GroupLevelDescription.ValueTemplateProperty );
+ }
+ set
+ {
+ this.SetValue( GroupLevelDescription.ValueTemplateProperty, value );
+ }
+ }
+
+ #endregion ValueTemplate Property
+
+ #region ValueTemplateSelector Property
+
+ public static readonly DependencyProperty ValueTemplateSelectorProperty =
+ DependencyProperty.Register( "ValueTemplateSelector", typeof( DataTemplateSelector ), typeof( GroupLevelDescription ), new UIPropertyMetadata( null, new PropertyChangedCallback( GroupPropertiesChangedHandler ) ) );
+
+ public DataTemplateSelector ValueTemplateSelector
+ {
+ get
+ {
+ return ( DataTemplateSelector )this.GetValue( GroupLevelDescription.ValueTemplateSelectorProperty );
+ }
+ set
+ {
+ this.SetValue( GroupLevelDescription.ValueTemplateSelectorProperty, value );
+ }
+ }
+
+ #endregion ValueTemplateSelector Property
+
+ #region PRIVATE METHODS
+
+ private static void GroupPropertiesChangedHandler( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ GroupLevelDescription info = ( GroupLevelDescription )sender;
+
+ if( info.PropertyChanged != null )
+ {
+ info.PropertyChanged( info, new PropertyChangedEventArgs( e.Property.Name ) );
+ }
+ }
+
+ #endregion
+
+ #region INotifyPropertyChanged Members
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ #endregion
+
+ #region PRIVATE FIELDS
+
+ private string m_fieldName;
+ private GroupDescription m_groupDescription;
+
+ #endregion
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelDescriptionCollection.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelDescriptionCollection.cs
new file mode 100644
index 00000000..7a9b0bb8
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelDescriptionCollection.cs
@@ -0,0 +1,59 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Collections.ObjectModel;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class GroupLevelDescriptionCollection : ReadOnlyObservableCollection
+ {
+ public GroupLevelDescriptionCollection()
+ : base( new ObservableCollection() )
+ {
+ }
+
+ internal void Add( GroupLevelDescription info )
+ {
+ this.Items.Add( info );
+ }
+
+ internal void Insert( int index, GroupLevelDescription info )
+ {
+ this.Items.Insert( index, info );
+ }
+
+ internal bool Remove( GroupLevelDescription info )
+ {
+ return this.Items.Remove( info );
+ }
+
+ internal void RemoveAt( int index )
+ {
+ this.Items.RemoveAt( index );
+ }
+
+ internal void Clear()
+ {
+ this.Items.Clear();
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelIndicator.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelIndicator.cs
new file mode 100644
index 00000000..aa4e3062
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelIndicator.cs
@@ -0,0 +1,55 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Controls;
+using System.Windows;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class GroupLevelIndicator : Control
+ {
+ static GroupLevelIndicator()
+ {
+ // This DefaultStyleKey will only be used in design-time.
+ DefaultStyleKeyProperty.OverrideMetadata( typeof( GroupLevelIndicator ), new FrameworkPropertyMetadata( new Markup.ThemeKey( typeof( Views.TableView ), typeof( GroupLevelIndicator ) ) ) );
+
+ DataGridControl.DataGridContextPropertyKey.OverrideMetadata( typeof( GroupLevelIndicator ), new FrameworkPropertyMetadata( new PropertyChangedCallback( GroupLevelIndicator.OnDataGridContextChanged ) ) );
+
+ FocusableProperty.OverrideMetadata( typeof( GroupLevelIndicator ), new FrameworkPropertyMetadata( false ) );
+ }
+
+ protected internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( GroupLevelIndicator ) );
+ }
+
+ private static void OnDataGridContextChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ DataGridContext dataGridContext = e.NewValue as DataGridContext;
+
+ if( dataGridContext != null )
+ {
+ ( ( GroupLevelIndicator )sender ).PrepareDefaultStyleKey( dataGridContext.DataGridControl.GetView() );
+ }
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelIndicatorPane.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelIndicatorPane.cs
new file mode 100644
index 00000000..4d71d49f
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupLevelIndicatorPane.cs
@@ -0,0 +1,431 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Diagnostics;
+using Xceed.Wpf.DataGrid.Views;
+using System.Windows.Data;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Collections.ObjectModel;
+using System.Windows.Media;
+
+namespace Xceed.Wpf.DataGrid
+{
+ [TemplatePart( Name = "PART_GroupLevelIndicatorHost", Type = typeof( Panel ) )]
+ public class GroupLevelIndicatorPane : Control, IWeakEventListener
+ {
+ static GroupLevelIndicatorPane()
+ {
+ GroupLevelIndicatorPane.IsLeafProperty = GroupLevelIndicatorPane.IsLeafPropertyKey.DependencyProperty;
+ GroupLevelIndicatorPane.CurrentIndicatorCountProperty = GroupLevelIndicatorPane.CurrentIndicatorCountPropertyKey.DependencyProperty;
+
+ FocusableProperty.OverrideMetadata( typeof( GroupLevelIndicatorPane ), new FrameworkPropertyMetadata( false ) );
+
+ DataGridControl.DataGridContextPropertyKey.OverrideMetadata( typeof( GroupLevelIndicatorPane ), new FrameworkPropertyMetadata( new PropertyChangedCallback( OnDataGridContextChanged ) ) );
+ CustomItemContainerGenerator.DataItemPropertyProperty.OverrideMetadata( typeof( GroupLevelIndicatorPane ), new FrameworkPropertyMetadata( new PropertyChangedCallback( OnDataItemChanged ) ) );
+
+ AreGroupsFlattenedBinding = new Binding();
+ AreGroupsFlattenedBinding.Mode = BindingMode.OneWay;
+ AreGroupsFlattenedBinding.RelativeSource = new RelativeSource( RelativeSourceMode.Self );
+
+ AreGroupsFlattenedBinding.Path = new PropertyPath( "(0).(1)",
+ DataGridControl.DataGridContextProperty,
+ TableflowView.AreGroupsFlattenedProperty );
+ }
+
+ public GroupLevelIndicatorPane()
+ {
+ this.SetBinding( GroupLevelIndicatorPane.AreGroupsFlattenedProperty, GroupLevelIndicatorPane.AreGroupsFlattenedBinding );
+ }
+
+ #region AreGroupsFlattened Internal Property
+
+ internal static readonly DependencyProperty AreGroupsFlattenedProperty = DependencyProperty.Register(
+ "AreGroupsFlattened", typeof( bool ), typeof( GroupLevelIndicatorPane ),
+ new FrameworkPropertyMetadata( false, FrameworkPropertyMetadataOptions.AffectsMeasure ) );
+
+ internal bool AreGroupsFlattened
+ {
+ get
+ {
+ return ( bool )this.GetValue( GroupLevelIndicatorPane.AreGroupsFlattenedProperty );
+ }
+ set
+ {
+ this.SetValue( GroupLevelIndicatorPane.AreGroupsFlattenedProperty, value );
+ }
+ }
+
+ private static Binding AreGroupsFlattenedBinding;
+
+ #endregion AreGroupsFlattened Internal Property
+
+ #region GroupLevelIndicatorPaneHost Read-Only Property
+
+ private Panel GroupLevelIndicatorPaneHost
+ {
+ get
+ {
+ //if there is no local storage for the host panel, try to retrieve and store the value
+ if( m_storedGroupLevelIndicatorPaneHost == null )
+ {
+ m_storedGroupLevelIndicatorPaneHost = this.RetrieveGroupLevelIndicatorPaneHostPanel();
+ }
+
+ return m_storedGroupLevelIndicatorPaneHost;
+ }
+ }
+
+ #endregion GroupLevelIndicatorPaneHost Read-Only Property
+
+ #region ShowIndicators Attached Property
+
+ public static readonly DependencyProperty ShowIndicatorsProperty = DependencyProperty.RegisterAttached(
+ "ShowIndicators", typeof( bool ), typeof( GroupLevelIndicatorPane ),
+ new FrameworkPropertyMetadata( true, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsMeasure ) );
+
+ public static bool GetShowIndicators( DependencyObject obj )
+ {
+ return ( bool )obj.GetValue( GroupLevelIndicatorPane.ShowIndicatorsProperty );
+ }
+
+ public static void SetShowIndicators( DependencyObject obj, bool value )
+ {
+ obj.SetValue( GroupLevelIndicatorPane.ShowIndicatorsProperty, value );
+ }
+
+ #endregion ShowIndicators Attached Property
+
+ #region ShowVerticalBorder Attached Property
+
+ public static readonly DependencyProperty ShowVerticalBorderProperty =
+ DependencyProperty.RegisterAttached( "ShowVerticalBorder", typeof( bool ), typeof( GroupLevelIndicatorPane ),
+ new FrameworkPropertyMetadata( true, FrameworkPropertyMetadataOptions.AffectsMeasure ) );
+
+ public static bool GetShowVerticalBorder( DependencyObject obj )
+ {
+ return ( bool )obj.GetValue( ShowVerticalBorderProperty );
+ }
+
+ public static void SetShowVerticalBorder( DependencyObject obj, bool value )
+ {
+ obj.SetValue( ShowVerticalBorderProperty, value );
+ }
+
+ #endregion
+
+ #region Indented Property
+
+ public static readonly DependencyProperty IndentedProperty =
+ DependencyProperty.Register( "Indented", typeof( bool ), typeof( GroupLevelIndicatorPane ),
+ new FrameworkPropertyMetadata( true, FrameworkPropertyMetadataOptions.AffectsMeasure ) );
+
+ public bool Indented
+ {
+ get
+ {
+ return ( bool )this.GetValue( GroupLevelIndicatorPane.IndentedProperty );
+ }
+ set
+ {
+ this.SetValue( GroupLevelIndicatorPane.IndentedProperty, value );
+ }
+ }
+
+ #endregion Indented Property
+
+ #region GroupLevel Attached Property
+
+ public static readonly DependencyProperty GroupLevelProperty = DependencyProperty.RegisterAttached(
+ "GroupLevel", typeof( int ), typeof( GroupLevelIndicatorPane ),
+ new FrameworkPropertyMetadata( 0, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.AffectsMeasure ) );
+
+ public static int GetGroupLevel( DependencyObject obj )
+ {
+ return ( int )obj.GetValue( GroupLevelIndicatorPane.GroupLevelProperty );
+ }
+
+ public static void SetGroupLevel( DependencyObject obj, int value )
+ {
+ obj.SetValue( GroupLevelIndicatorPane.GroupLevelProperty, value );
+ }
+
+ #endregion GroupLevel Attached Property
+
+ #region IsLeaf Read-Only Property
+
+ internal static readonly DependencyPropertyKey IsLeafPropertyKey =
+ DependencyProperty.RegisterReadOnly( "IsLeaf", typeof( bool ), typeof( GroupLevelIndicatorPane ),
+ new FrameworkPropertyMetadata( true, FrameworkPropertyMetadataOptions.AffectsMeasure ) );
+
+ public static readonly DependencyProperty IsLeafProperty;
+
+ public bool IsLeaf
+ {
+ get
+ {
+ return ( bool )this.GetValue( GroupLevelIndicatorPane.IsLeafProperty );
+ }
+ }
+
+ internal void SetIsLeaf( bool value )
+ {
+ this.SetValue( GroupLevelIndicatorPane.IsLeafPropertyKey, value );
+ }
+
+ #endregion IsLeaf Read-Only Property
+
+ #region CurrentIndicatorCount Read-Only Property
+
+ private static readonly DependencyPropertyKey CurrentIndicatorCountPropertyKey =
+ DependencyProperty.RegisterReadOnly( "CurrentIndicatorCount", typeof( int ), typeof( GroupLevelIndicatorPane ), new PropertyMetadata( 0 ) );
+
+ public static readonly DependencyProperty CurrentIndicatorCountProperty;
+
+ public int CurrentIndicatorCount
+ {
+ get
+ {
+ return ( int )this.GetValue( GroupLevelIndicatorPane.CurrentIndicatorCountProperty );
+ }
+ }
+
+ internal void SetCurrentIndicatorCount( int value )
+ {
+ this.SetValue( GroupLevelIndicatorPane.CurrentIndicatorCountPropertyKey, value );
+ }
+
+ #endregion CurrentIndicatorCount Read-Only Property
+
+ internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( GroupLevelIndicatorPane ) );
+ }
+
+ protected override Size MeasureOverride( Size availableSize )
+ {
+ Panel panel = this.GroupLevelIndicatorPaneHost;
+
+ if( panel == null )
+ return base.MeasureOverride( availableSize );
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext != null )
+ {
+ ObservableCollection groupDescriptions = DataGridContext.GetGroupDescriptionsHelper( dataGridContext.Items );
+
+ int leafGroupLevel = GroupLevelIndicatorPane.GetGroupLevel( this );
+
+ // If Indented is true (default), we use the total groupDescriptions.Count for this DataGridContext
+ int correctedGroupLevel = ( this.Indented == true ) ? groupDescriptions.Count : leafGroupLevel;
+
+ // Ensure that the GroupLevel retrieved does not exceeds the number of group descriptions for the DataGridContext
+ correctedGroupLevel = Math.Min( correctedGroupLevel, groupDescriptions.Count );
+
+ // Then finally, if the GroupLevel is -1, then indent at maximum.
+ if( correctedGroupLevel == -1 )
+ correctedGroupLevel = groupDescriptions.Count;
+
+ if( ( correctedGroupLevel > 0 )
+ && ( this.AreGroupsFlattened ) )
+ {
+ correctedGroupLevel = ( this.Indented ) ? 1 : 0;
+ }
+
+ UIElementCollection children = panel.Children;
+ int childrenCount = children.Count;
+
+ // If we need to add/remove GroupLevelIndicators from the panel
+ if( correctedGroupLevel != childrenCount )
+ {
+ // When grouping change, we take for granted that the group deepness will change,
+ // so we initialize DataContext of the margin only in there.
+
+ // Clear all the panel's children!
+ children.Clear();
+
+ // Create 1 group margin content presenter for each group level
+ for( int i = correctedGroupLevel - 1; i >= 0; i-- )
+ {
+ GroupLevelIndicator groupMargin = new GroupLevelIndicator();
+ groupMargin.DataContext = dataGridContext.GroupLevelDescriptions[ i ];
+ children.Insert( 0, new GroupLevelIndicator() );
+ }
+
+ childrenCount = correctedGroupLevel;
+ this.SetCurrentIndicatorCount( childrenCount );
+ }
+
+ object item = dataGridContext.GetItemFromContainer( this );
+
+ for( int i = 0; i < childrenCount; i++ )
+ {
+ GroupLevelIndicator groupMargin = children[ i ] as GroupLevelIndicator;
+
+ CollectionViewGroup groupForIndicator = GroupLevelIndicatorPane.GetCollectionViewGroupHelper(
+ dataGridContext, groupDescriptions, item, i );
+
+ GroupConfiguration groupLevelConfig = GroupConfiguration.GetGroupConfiguration(
+ dataGridContext, groupDescriptions, dataGridContext.GroupConfigurationSelector, i, groupForIndicator );
+
+ if( groupLevelConfig != null )
+ {
+ Binding groupLevelIndicatorStyleBinding = BindingOperations.GetBinding( groupMargin, GroupLevelIndicator.StyleProperty );
+
+ if( ( groupLevelIndicatorStyleBinding == null ) || ( groupLevelIndicatorStyleBinding.Source != groupLevelConfig ) )
+ {
+ groupLevelIndicatorStyleBinding = new Binding( "GroupLevelIndicatorStyle" );
+ groupLevelIndicatorStyleBinding.Source = groupLevelConfig;
+ groupMargin.SetBinding( GroupLevelIndicator.StyleProperty, groupLevelIndicatorStyleBinding );
+ }
+ }
+ else
+ {
+ groupMargin.ClearValue( GroupLevelIndicator.StyleProperty );
+ }
+
+ // If the ShowIndicators property is False or there is already leafGroupLevel GroupLevelIndicators in the panel,
+ // the current newGroupMargin must be hidden.
+ if( ( !GroupLevelIndicatorPane.GetShowIndicators( this ) ) || ( ( i >= leafGroupLevel ) && ( leafGroupLevel != -1 ) ) )
+ {
+ groupMargin.Visibility = Visibility.Hidden;
+ }
+ else
+ {
+ groupMargin.Visibility = Visibility.Visible;
+ }
+ }
+ }
+
+ return base.MeasureOverride( availableSize );
+ }
+
+ private static CollectionViewGroup GetCollectionViewGroupHelper( DataGridContext dataGridContext, ObservableCollection groupDescriptions, object item, int groupLevel )
+ {
+ if( item == null )
+ return null;
+
+ int levelOfRecursion = groupDescriptions.Count - groupLevel - 1;
+
+ CollectionViewGroup retval = dataGridContext.GetParentGroupFromItemCore( item, true );
+
+ if( retval == null )
+ return null;
+
+ for( int i = 0; i < levelOfRecursion; i++ )
+ {
+ retval = dataGridContext.GetParentGroupFromItemCore( retval, true ) as CollectionViewGroup;
+
+ if( retval == null )
+ return null;
+ }
+
+ return retval;
+ }
+
+ private static void OnDataGridContextChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ GroupLevelIndicatorPane self = sender as GroupLevelIndicatorPane;
+
+ if( self != null )
+ {
+ DataGridContext dataGridContext = e.OldValue as DataGridContext;
+
+ //unregister to the old contexts Collection GroupDescriptions Changed event
+ if( dataGridContext != null )
+ {
+ CollectionChangedEventManager.RemoveListener( dataGridContext.Items.GroupDescriptions, self );
+ }
+
+ dataGridContext = e.NewValue as DataGridContext;
+
+ //register to the new contexts Collection GroupDescriptions Changed event
+ if( dataGridContext != null )
+ {
+ CollectionChangedEventManager.AddListener( dataGridContext.Items.GroupDescriptions, self );
+ self.PrepareDefaultStyleKey( dataGridContext.DataGridControl.GetView() );
+ }
+
+ self.InvalidateMeasure();
+ }
+ }
+
+ private static void OnDataItemChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ GroupLevelIndicatorPane self = sender as GroupLevelIndicatorPane;
+
+ if( ( self != null ) && ( e.NewValue != CustomItemContainerGenerator.NotSet ) )
+ {
+ self.InvalidateMeasure();
+ }
+ }
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ //whenever the template gets "applied" I want to invalidate the stored Panel.
+ m_storedGroupLevelIndicatorPaneHost = null;
+ }
+
+ protected override void OnPropertyChanged( DependencyPropertyChangedEventArgs e )
+ {
+ base.OnPropertyChanged( e );
+
+ if( e.Property == GroupLevelIndicatorPane.GroupLevelProperty )
+ {
+ this.InvalidateMeasure();
+ }
+ }
+
+ private Panel RetrieveGroupLevelIndicatorPaneHostPanel()
+ {
+ //get the template part
+ return this.GetTemplateChild( "PART_GroupLevelIndicatorHost" ) as Panel;
+ }
+
+ private Panel m_storedGroupLevelIndicatorPaneHost = null;
+
+ #region IWeakEventListener Members
+
+ bool IWeakEventListener.ReceiveWeakEvent( Type managerType, object sender, EventArgs e )
+ {
+ return this.OnReceiveWeakEvent( managerType, sender, e );
+ }
+
+ protected virtual bool OnReceiveWeakEvent( Type managerType, object sender, EventArgs e )
+ {
+ if( managerType == typeof( CollectionChangedEventManager ) )
+ {
+ this.InvalidateMeasure();
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupNavigationButton.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupNavigationButton.cs
new file mode 100644
index 00000000..90f30cb8
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupNavigationButton.cs
@@ -0,0 +1,87 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Controls;
+using System.Windows;
+using Xceed.Wpf.DataGrid.Views;
+using System.Windows.Input;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class GroupNavigationButton : Button
+ {
+ #region CONSTRUCTORS
+
+ static GroupNavigationButton()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(
+ typeof( GroupNavigationButton ),
+ new FrameworkPropertyMetadata( new Markup.ThemeKey( typeof( TableView ), typeof( GroupNavigationButton ) ) ) );
+
+ DataGridControl.ParentDataGridControlPropertyKey.OverrideMetadata(
+ typeof( GroupNavigationButton ), new FrameworkPropertyMetadata( new PropertyChangedCallback( GroupNavigationButton.OnParentGridControlChanged ) ) );
+ }
+
+ public GroupNavigationButton()
+ {
+ this.Focusable = false;
+ }
+
+ #endregion CONSTRUCTORS
+
+ #region NavigateToGroup Command
+
+ public static readonly RoutedCommand NavigateToGroup =
+ new RoutedCommand( "NavigateToGroup", typeof( GroupNavigationButton ) );
+
+ #endregion NavigateToGroup Command
+
+ #region PROTECTED METHODS
+
+ protected internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( GroupNavigationButton ) );
+ }
+
+ #endregion PROTECTED METHODS
+
+ #region PRIVATE METHODS
+
+ private static void OnParentGridControlChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ GroupNavigationButton button = sender as GroupNavigationButton;
+
+ if( button == null )
+ return;
+
+ DataGridControl parentGridControl = e.NewValue as DataGridControl;
+
+ if( parentGridControl == null )
+ return;
+
+ button.PrepareDefaultStyleKey( parentGridControl.GetView() );
+ }
+
+ #endregion PRIVATE METHODS
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupNavigationControl.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupNavigationControl.cs
new file mode 100644
index 00000000..ba4716bd
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupNavigationControl.cs
@@ -0,0 +1,436 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Controls;
+using System.Windows;
+using System.Windows.Input;
+using System.ComponentModel;
+using System.Windows.Media;
+using System.Windows.Controls.Primitives;
+using Xceed.Wpf.DataGrid.Views;
+using System.Windows.Data;
+
+namespace Xceed.Wpf.DataGrid
+{
+ [TemplatePart( Name = "PART_Button", Type = typeof( Button ) )]
+ [TemplatePart( Name = "PART_ToggleButton", Type = typeof( ToggleButton ) )]
+ [TemplatePart( Name = "PART_Popup", Type = typeof( Popup ) )]
+ [StyleTypedProperty( Property = "ItemContainerStyle", StyleTargetType = typeof( GroupNavigationControlItem ) )]
+ public class GroupNavigationControl : ItemsControl
+ {
+ #region CONSTRUCTORS
+
+ static GroupNavigationControl()
+ {
+ GroupNavigationControl.GroupProperty = GroupHeaderControl.GroupProperty.AddOwner( typeof( GroupNavigationControl ) );
+
+ DefaultStyleKeyProperty.OverrideMetadata(
+ typeof( GroupNavigationControl ),
+ new FrameworkPropertyMetadata( new Markup.ThemeKey( typeof( TableView ), typeof( GroupNavigationControl ) ) ) );
+
+ DataGridControl.ParentDataGridControlPropertyKey.OverrideMetadata(
+ typeof( GroupNavigationControl ),
+ new FrameworkPropertyMetadata( new PropertyChangedCallback( GroupNavigationControl.OnParentGridControlChanged ) ) );
+
+ EventManager.RegisterClassHandler(
+ typeof( GroupNavigationControl ),
+ Mouse.LostMouseCaptureEvent,
+ new MouseEventHandler( GroupNavigationControl.OnLostMouseCapture ) );
+
+ EventManager.RegisterClassHandler(
+ typeof( GroupNavigationControl ),
+ Mouse.PreviewMouseDownEvent,
+ new MouseButtonEventHandler( GroupNavigationControl.OnPreviewMouseButtonDown ) );
+
+ EventManager.RegisterClassHandler(
+ typeof( GroupNavigationControl ),
+ Mouse.MouseDownEvent,
+ new MouseButtonEventHandler( GroupNavigationControl.OnMouseButtonDown ), true );
+
+ EventManager.RegisterClassHandler(
+ typeof( GroupNavigationControl ),
+ Mouse.MouseWheelEvent,
+ new MouseWheelEventHandler( GroupNavigationControl.OnMouseWheel ), true );
+ }
+
+ public GroupNavigationControl()
+ {
+ }
+
+ #endregion CONSTRUCTORS
+
+ #region Group Property
+
+ public static readonly DependencyProperty GroupProperty;
+
+ public Group Group
+ {
+ get
+ {
+ return GroupHeaderControl.GetGroup( this );
+ }
+ set
+ {
+ GroupHeaderControl.SetGroup( this, value );
+ }
+ }
+
+ #endregion Group Property
+
+ #region MainItemTemplate Property
+
+ public static readonly DependencyProperty MainItemTemplateProperty = DependencyProperty.Register(
+ "MainItemTemplate",
+ typeof( DataTemplate ),
+ typeof( GroupNavigationControl ) );
+
+ public DataTemplate MainItemTemplate
+ {
+ get
+ {
+ return ( DataTemplate )this.GetValue( GroupNavigationControl.MainItemTemplateProperty );
+ }
+ set
+ {
+ this.SetValue( GroupNavigationControl.MainItemTemplateProperty, value );
+ }
+ }
+
+ #endregion MainItemTemplate Property
+
+ #region MainItemTemplateSelector Property
+
+ public static readonly DependencyProperty MainItemTemplateSelectorProperty = DependencyProperty.Register(
+ "MainItemTemplateSelector",
+ typeof( DataTemplateSelector ),
+ typeof( GroupNavigationControl ) );
+
+ public DataTemplateSelector MainItemTemplateSelector
+ {
+ get
+ {
+ return ( DataTemplateSelector )this.GetValue( GroupNavigationControl.MainItemTemplateSelectorProperty );
+ }
+ set
+ {
+ this.SetValue( GroupNavigationControl.MainItemTemplateSelectorProperty, value );
+ }
+ }
+
+ #endregion MainItemTemplateSelector Property
+
+ #region MaxDropDownHeight Property
+
+ public static readonly DependencyProperty MaxDropDownHeightProperty = DependencyProperty.Register(
+ "MaxDropDownHeight",
+ typeof( double ),
+ typeof( GroupNavigationControl ),
+ new FrameworkPropertyMetadata( SystemParameters.PrimaryScreenHeight / 3.0 ) );
+
+ public double MaxDropDownHeight
+ {
+ get
+ {
+ return ( double )this.GetValue( GroupNavigationControl.MaxDropDownHeightProperty );
+ }
+ set
+ {
+ this.SetValue( GroupNavigationControl.MaxDropDownHeightProperty, value );
+ }
+ }
+
+ #endregion MaxDropDownHeight Property
+
+ #region IsDropDownOpen Property
+
+ [Browsable( false )]
+ public static readonly DependencyProperty IsDropDownOpenProperty = DependencyProperty.Register(
+ "IsDropDownOpen",
+ typeof( bool ),
+ typeof( GroupNavigationControl ),
+ new FrameworkPropertyMetadata( new PropertyChangedCallback( GroupNavigationControl.OnIsDropDownOpenChanged ) ) );
+
+ [Browsable( false )]
+ public bool IsDropDownOpen
+ {
+ get
+ {
+ return ( bool )this.GetValue( GroupNavigationControl.IsDropDownOpenProperty );
+ }
+ set
+ {
+ this.SetValue( GroupNavigationControl.IsDropDownOpenProperty, value );
+ }
+ }
+
+ private static void OnIsDropDownOpenChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ GroupNavigationControl groupNavigationControl = ( GroupNavigationControl )sender;
+
+ if( ( bool )e.NewValue )
+ {
+ // To avoid lag while generating the control, we do
+ // a late binding of the items.
+ if( groupNavigationControl.ItemsSource == null )
+ {
+ Group group = groupNavigationControl.DataContext as Group;
+ if( group != null )
+ {
+ groupNavigationControl.ItemsSource = group.SiblingGroups;
+ }
+ }
+
+ Mouse.Capture( groupNavigationControl, CaptureMode.SubTree );
+ }
+ else
+ {
+ groupNavigationControl.ItemsSource = null;
+
+ if( groupNavigationControl.IsKeyboardFocusWithin )
+ {
+ groupNavigationControl.Focus();
+ }
+
+ if( Mouse.Captured == groupNavigationControl )
+ {
+ Mouse.Capture( null );
+ }
+ }
+ }
+
+ #endregion IsDropDownOpen Property
+
+ #region PartButton Private Property
+
+ private Button PartButton
+ {
+ get;
+ set;
+ }
+
+ #endregion PartButton Private Property
+
+ #region PartToggleButton Private Property
+
+ private ToggleButton PartToggleButton
+ {
+ get;
+ set;
+ }
+
+ #endregion PartToggleButton Private Property
+
+ #region PartPopup Private Property
+
+ private Popup PartPopup
+ {
+ get;
+ set;
+ }
+
+ #endregion PartPopup Private Property
+
+ #region EVENT HANDLERS
+
+ private static void OnLostMouseCapture( object sender, MouseEventArgs e )
+ {
+ GroupNavigationControl groupNavigationControl = ( GroupNavigationControl )sender;
+
+ DependencyObject originalSource = e.OriginalSource as DependencyObject;
+
+ if( Mouse.Captured != groupNavigationControl )
+ {
+ if( e.OriginalSource == groupNavigationControl )
+ {
+ if( ( Mouse.Captured == null )
+ && ( Xceed.Utils.Wpf.TreeHelper.IsDescendantOf( originalSource, groupNavigationControl ) ) )
+ {
+ groupNavigationControl.Close();
+ }
+ }
+ else if( ( originalSource != null ) && ( Xceed.Utils.Wpf.TreeHelper.IsDescendantOf( originalSource, groupNavigationControl ) ) )
+ {
+ if( ( groupNavigationControl.IsDropDownOpen ) && ( Mouse.Captured == null ) )
+ {
+ Mouse.Capture( groupNavigationControl, CaptureMode.SubTree );
+ e.Handled = true;
+ }
+ }
+ else
+ {
+ groupNavigationControl.Close();
+ }
+ }
+ }
+
+ private static void OnPreviewMouseButtonDown( object sender, MouseButtonEventArgs e )
+ {
+ GroupNavigationControl groupNavigationControl = ( GroupNavigationControl )sender;
+ Visual originalSource = e.OriginalSource as Visual;
+
+ if( ( originalSource != null )
+ && ( groupNavigationControl.PartButton != null )
+ && ( groupNavigationControl.PartButton.IsAncestorOf( originalSource ) ) )
+ {
+ if( groupNavigationControl.IsDropDownOpen )
+ {
+ groupNavigationControl.Close();
+ }
+ }
+ }
+
+ private static void OnMouseButtonDown( object sender, MouseButtonEventArgs e )
+ {
+ GroupNavigationControl groupNavigationControl = ( GroupNavigationControl )sender;
+
+ if( !groupNavigationControl.IsKeyboardFocusWithin )
+ {
+ groupNavigationControl.Focus();
+ }
+
+ e.Handled = true;
+
+ if( ( Mouse.Captured == groupNavigationControl ) && ( e.OriginalSource == groupNavigationControl ) )
+ {
+ groupNavigationControl.Close();
+ }
+ }
+
+ private static void OnMouseWheel( object sender, MouseWheelEventArgs e )
+ {
+ GroupNavigationControl groupNavigationControl = ( GroupNavigationControl )sender;
+
+ if( groupNavigationControl.IsDropDownOpen )
+ {
+ // This is to avoid scrolling any other list if the drop down is open.
+ // If not set, the grid could scroll while leaving the drop down in view.
+ // The dorp down will then be left alone while the GroupNavigationControl
+ // might not be in view anymore.
+ e.Handled = true;
+ }
+ }
+
+ private static void OnParentGridControlChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ GroupNavigationControl groupNavigationControl = sender as GroupNavigationControl;
+
+ if( groupNavigationControl == null )
+ return;
+
+ DataGridControl parentGridControl = e.NewValue as DataGridControl;
+
+ if( parentGridControl == null )
+ return;
+
+ groupNavigationControl.PrepareDefaultStyleKey( parentGridControl.GetView() );
+ }
+
+ #endregion EVENT HANDLERS
+
+ #region OVERRIDES
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ this.PartButton = this.GetTemplateChild( "PART_Button" ) as Button;
+ this.PartToggleButton = this.GetTemplateChild( "PART_ToggleButton" ) as ToggleButton;
+ this.PartPopup = this.GetTemplateChild( "PART_Popup" ) as Popup;
+ }
+
+ protected override DependencyObject GetContainerForItemOverride()
+ {
+ return new GroupNavigationControlItem( this );
+ }
+
+ protected override void PrepareContainerForItemOverride( DependencyObject element, object item )
+ {
+ base.PrepareContainerForItemOverride( element, item );
+
+ element.SetValue( GroupHeaderControl.GroupProperty, item );
+ }
+
+ protected override void ClearContainerForItemOverride( DependencyObject element, object item )
+ {
+ element.ClearValue( GroupHeaderControl.GroupProperty );
+
+ base.ClearContainerForItemOverride( element, item );
+ }
+
+ protected override void OnIsKeyboardFocusWithinChanged( DependencyPropertyChangedEventArgs e )
+ {
+ base.OnIsKeyboardFocusWithinChanged( e );
+
+ if( ( this.IsDropDownOpen )
+ && ( !base.IsKeyboardFocusWithin ) )
+ {
+ this.Close();
+ }
+ }
+
+ #endregion OVERRIDES
+
+ #region INTERNAL METHODS
+
+ internal void NotifyGroupNavigationControlItemMouseDown( GroupNavigationControlItem groupNavigationControlItem )
+ {
+ }
+
+ internal void NotifyGroupNavigationControlItemMouseUp( GroupNavigationControlItem groupNavigationControlItem )
+ {
+ Group group = groupNavigationControlItem.DataContext as Group;
+ if( group != null )
+ {
+ GroupNavigationButton.NavigateToGroup.Execute( group, this );
+ }
+
+ this.Close();
+ }
+
+ #endregion INTERNAL METHODS
+
+ #region PROTECTED METHODS
+
+ protected internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( GroupNavigationControl ) );
+ }
+
+ #endregion PROTECTED METHODS
+
+ #region PRIVATE METHODS
+
+ private void Close()
+ {
+ if( this.IsDropDownOpen )
+ {
+ base.ClearValue( GroupNavigationControl.IsDropDownOpenProperty );
+
+ if( this.IsDropDownOpen )
+ this.IsDropDownOpen = false;
+ }
+ }
+
+ #endregion PRIVATE METHODS
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupNavigationControlItem.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupNavigationControlItem.cs
new file mode 100644
index 00000000..c7e844cc
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupNavigationControlItem.cs
@@ -0,0 +1,134 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Controls;
+using System.Windows;
+using Xceed.Wpf.DataGrid.Views;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class GroupNavigationControlItem : ContentControl
+ {
+ #region CONSTRUCTORS
+
+ static GroupNavigationControlItem()
+ {
+ GroupNavigationControlItem.GroupProperty = GroupHeaderControl.GroupProperty.AddOwner( typeof( GroupNavigationControlItem ) );
+
+ DefaultStyleKeyProperty.OverrideMetadata(
+ typeof( GroupNavigationControlItem ),
+ new FrameworkPropertyMetadata( new Markup.ThemeKey( typeof( TableView ), typeof( GroupNavigationControlItem ) ) ) );
+
+ DataGridControl.ParentDataGridControlPropertyKey.OverrideMetadata(
+ typeof( GroupNavigationControlItem ),
+ new FrameworkPropertyMetadata( new PropertyChangedCallback( GroupNavigationControlItem.OnParentGridControlChanged ) ) );
+ }
+
+ public GroupNavigationControlItem( GroupNavigationControl parentNavigationControl )
+ {
+ this.ParentNavigationControl = parentNavigationControl;
+ }
+
+ #endregion CONSTRUCTORS
+
+ #region ParentGroupNavigationControl Internal Property
+
+ internal GroupNavigationControl ParentNavigationControl
+ {
+ get;
+ private set;
+ }
+
+ #endregion ParentGroupNavigationControl Internal Property
+
+ #region Group Property
+
+ public static readonly DependencyProperty GroupProperty;
+
+ public Group Group
+ {
+ get
+ {
+ return GroupHeaderControl.GetGroup( this );
+ }
+ }
+
+ #endregion Group Property
+
+ #region OVERRIDES
+
+ protected override void OnMouseLeftButtonDown( System.Windows.Input.MouseButtonEventArgs e )
+ {
+ e.Handled = true;
+
+ if( this.ParentNavigationControl != null )
+ {
+ this.ParentNavigationControl.NotifyGroupNavigationControlItemMouseDown( this );
+ }
+
+ base.OnMouseLeftButtonDown( e );
+ }
+
+ protected override void OnMouseLeftButtonUp( System.Windows.Input.MouseButtonEventArgs e )
+ {
+ e.Handled = true;
+
+ if( this.ParentNavigationControl != null )
+ {
+ this.ParentNavigationControl.NotifyGroupNavigationControlItemMouseUp( this );
+ }
+
+ base.OnMouseLeftButtonUp( e );
+ }
+
+ #endregion OVERRIDES
+
+ #region EVENT HANDLERS
+
+ private static void OnParentGridControlChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ GroupNavigationControlItem groupNavigationControlItem = sender as GroupNavigationControlItem;
+
+ if( groupNavigationControlItem == null )
+ return;
+
+ DataGridControl parentGridControl = e.NewValue as DataGridControl;
+
+ if( parentGridControl == null )
+ return;
+
+ groupNavigationControlItem.PrepareDefaultStyleKey( parentGridControl.GetView() );
+ }
+
+ #endregion EVENT HANDLERS
+
+ #region PROTECTED METHODS
+
+ protected internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( GroupNavigationControlItem ) );
+ }
+
+ #endregion PROTECTED METHODS
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupingHelper.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupingHelper.cs
new file mode 100644
index 00000000..5eef9ceb
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/GroupingHelper.cs
@@ -0,0 +1,286 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.ComponentModel;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Windows;
+using Xceed.Utils.Wpf;
+using System.Windows.Data;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal static class GroupingHelper
+ {
+ public static bool HasGroup( DataGridContext dataGridContext )
+ {
+ if( dataGridContext.GroupLevelDescriptions.Count > 0 )
+ return true;
+
+ return GroupingHelper.HasGroup( dataGridContext.DetailConfigurations );
+ }
+
+ private static bool HasGroup( DetailConfigurationCollection configurations )
+ {
+ bool returnValue = false;
+
+ int detailConfigurationCount = configurations.Count;
+
+ for( int i = 0; i < detailConfigurationCount; i++ )
+ {
+ DetailConfiguration detailConfiguration = configurations[ i ];
+
+ if( detailConfiguration.DetailConfigurations != null )
+ returnValue = GroupingHelper.HasGroup( detailConfiguration.DetailConfigurations );
+
+ if( returnValue )
+ break;
+
+ returnValue = ( detailConfiguration.GroupLevelDescriptions.Count > 0 );
+
+ if( returnValue )
+ break;
+ }
+
+ return returnValue;
+ }
+
+ public static bool IsAlreadyGroupedBy( ColumnManagerCell cell )
+ {
+ DataGridContext cellDataGridContext = DataGridControl.GetDataGridContext( cell );
+
+ GroupLevelDescriptionCollection cellGroupLevelDescriptions = cellDataGridContext.GroupLevelDescriptions;
+
+ bool isAlreadyGroupedBy = false;
+ if( cellDataGridContext != null )
+ {
+ foreach( GroupLevelDescription description in cellGroupLevelDescriptions )
+ {
+ if( description.FieldName == cell.FieldName )
+ {
+ isAlreadyGroupedBy = true;
+ break;
+ }
+ }
+ }
+
+ return isAlreadyGroupedBy;
+ }
+
+ public static HierarchicalGroupByControl GetHierarchicalGroupByControl( UIElement element )
+ {
+ DependencyObject parent = TreeHelper.GetParent( element );
+
+ while( parent != null )
+ {
+ if( parent is HierarchicalGroupByControl )
+ break;
+
+ parent = TreeHelper.GetParent( parent );
+ }
+
+ return parent as HierarchicalGroupByControl;
+ }
+
+ public static int GetGroupDescriptionIndex( ObservableCollection groupDescriptions, GroupLevelDescription groupLevelDescription, DropMarkAlignment alignment )
+ {
+ int groupIndex = groupDescriptions.IndexOf( groupLevelDescription.GroupDescription );
+
+ if( groupIndex > -1 )
+ {
+ if( alignment == DropMarkAlignment.Far )
+ groupIndex++;
+
+ return groupIndex;
+ }
+ else
+ {
+ // return the size of the Collection if not found
+ return groupDescriptions.Count;
+ }
+ }
+
+ public static int GetGroupDescriptionIndexFromFieldName( DataGridContext dataGridContext, string fieldName, DropMarkAlignment alignment )
+ {
+ ObservableCollection groupDescriptions = dataGridContext.Items.GroupDescriptions;
+
+ for( int i = groupDescriptions.Count - 1; i >= 0; i-- )
+ {
+ if( DataGridContext.GetColumnNameFromGroupDescription( groupDescriptions[ i ] ) == fieldName )
+ {
+ if( alignment == DropMarkAlignment.Far )
+ i++;
+
+ return i;
+ }
+ }
+
+ return dataGridContext.Items.GroupDescriptions.Count;
+ }
+
+ public static bool IsColumnManagerCellInDataGridContext( DataGridContext dataGridContext, ColumnManagerCell cell )
+ {
+ DataGridContext cellDataGridContext = DataGridControl.GetDataGridContext( cell );
+
+ Debug.Assert( cellDataGridContext != null );
+
+ if( ( dataGridContext != null ) &&
+ ( cellDataGridContext != null ) &&
+ ( dataGridContext.GroupLevelDescriptions == cellDataGridContext.GroupLevelDescriptions ) )
+ return true;
+
+ return GroupingHelper.IsGroupLevelDescriptionsInDetailConfigurations( dataGridContext.DetailConfigurations, cellDataGridContext.GroupLevelDescriptions );
+ }
+
+ private static bool IsGroupLevelDescriptionsInDetailConfigurations( DetailConfigurationCollection configurations, GroupLevelDescriptionCollection groupLevelDescriptions )
+ {
+ bool returnValue = false;
+
+ int detailConfigurationCount = configurations.Count;
+
+ for( int i = 0; i < detailConfigurationCount; i++ )
+ {
+ DetailConfiguration detailConfiguration = configurations[ i ];
+
+ if( detailConfiguration.DetailConfigurations != null )
+ returnValue = GroupingHelper.IsGroupLevelDescriptionsInDetailConfigurations( detailConfiguration.DetailConfigurations, groupLevelDescriptions );
+
+ if( returnValue )
+ break;
+
+ returnValue = ( detailConfiguration.GroupLevelDescriptions == groupLevelDescriptions );
+
+ if( returnValue )
+ break;
+ }
+
+ return returnValue;
+ }
+
+ public static void AddNewGroupFromColumnManagerCell( ColumnManagerCell cell, GroupLevelDescription draggedOverDescription, DropMarkAlignment alignment, DataGridControl parentDataGridControl )
+ {
+ if( cell == null )
+ return;
+
+ DataGridContext cellDataGridContext = DataGridControl.GetDataGridContext( cell );
+ DataGridControl cellParentGrid = cellDataGridContext.DataGridControl;
+
+ if( ( cellDataGridContext == null ) ||
+ ( parentDataGridControl == null ) ||
+ ( parentDataGridControl != cellParentGrid ) )
+ return;
+
+ // By default, add it at the end
+ int newPos = cellDataGridContext.GroupLevelDescriptions.Count;
+
+ if( draggedOverDescription != null )
+ newPos = GroupingHelper.GetGroupDescriptionIndexFromFieldName( cellDataGridContext, draggedOverDescription.FieldName, alignment );
+
+ ColumnBase column = cell.ParentColumn;
+
+ if( column != null )
+ {
+ GroupingHelper.AddNewGroupFromColumn( column, cellDataGridContext.Items.GroupDescriptions, newPos );
+ }
+ }
+
+ public static void AppendNewGroupFromColumnManagerCell( ColumnManagerCell cell, DataGridControl parentDataGridControl )
+ {
+ GroupingHelper.AddNewGroupFromColumnManagerCell( cell, null, DropMarkAlignment.Far, parentDataGridControl );
+ }
+
+ public static void AddNewGroupFromColumn( ColumnBase column, ObservableCollection targetGroupDescriptions, int position )
+ {
+ GroupDescription groupDescription = column.GroupDescription;
+
+ if( groupDescription == null )
+ groupDescription = new DataGridGroupDescription( column.FieldName );
+
+ DataGridGroupDescription dataGridGroupDescription = groupDescription as DataGridGroupDescription;
+
+ if( ( dataGridGroupDescription != null ) && ( dataGridGroupDescription.GroupConfiguration == null ) )
+ dataGridGroupDescription.GroupConfiguration = column.GroupConfiguration;
+
+ targetGroupDescriptions.Insert( position, groupDescription );
+ }
+
+ public static void MoveGroupDescription( ColumnCollection targetColumns, ObservableCollection targetGroupDescriptions, GroupLevelDescription targetGroupLevelDescription, DropMarkAlignment targetAlignment, GroupLevelDescription movedGroupLevelDescription, DataGridControl parentDataGridControl )
+ {
+ Debug.Assert( targetColumns != null );
+ Debug.Assert( targetGroupDescriptions != null );
+ Debug.Assert( targetGroupLevelDescription != null );
+ Debug.Assert( movedGroupLevelDescription != null );
+
+ if( ( parentDataGridControl == null ) ||
+ ( targetColumns == null ) ||
+ ( targetGroupDescriptions == null ) ||
+ ( targetGroupLevelDescription == null ) ||
+ ( movedGroupLevelDescription == null ) )
+ return;
+
+ int oldPos = GroupingHelper.GetGroupDescriptionIndex( targetGroupDescriptions, movedGroupLevelDescription, DropMarkAlignment.Near );
+ int newPos = GroupingHelper.GetGroupDescriptionIndex( targetGroupDescriptions, targetGroupLevelDescription, targetAlignment );
+
+ if( newPos > oldPos )
+ newPos--;
+
+ if( newPos != oldPos )
+ {
+ Debug.Assert( oldPos < targetGroupDescriptions.Count );
+
+ targetGroupDescriptions.Move( oldPos, newPos );
+ }
+ }
+
+ public static void RemoveGroupDescription( ObservableCollection groupDescriptions, GroupLevelDescription groupLevelDescription, DataGridControl parentDataGridControl )
+ {
+ if( ( groupLevelDescription == null ) ||
+ ( parentDataGridControl == null ) )
+ return;
+
+ groupDescriptions.Remove( groupLevelDescription.GroupDescription );
+ }
+
+ public static bool ValidateMaxGroupDescriptions( DataGridContext draggedContext )
+ {
+ Debug.Assert( draggedContext != null );
+
+ int maxGroupDescriptionCount = -1;
+ int currentGroupDescriptionCount = -1;
+
+ CollectionView collectionView = draggedContext.Items;
+
+ if( collectionView == null )
+ return true;
+
+ ObservableCollection groupDescriptions = collectionView.GroupDescriptions;
+
+ if( groupDescriptions == null )
+ return true;
+
+ maxGroupDescriptionCount = draggedContext.MaxGroupLevels;
+ currentGroupDescriptionCount = groupDescriptions.Count;
+
+ return ( ( maxGroupDescriptionCount == -1 ) || ( currentGroupDescriptionCount < maxGroupDescriptionCount ) );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HashedLinkedList.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HashedLinkedList.cs
new file mode 100644
index 00000000..63291035
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HashedLinkedList.cs
@@ -0,0 +1,383 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Collections;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class HashedLinkedList : ICollection, ICollection
+ {
+ #region Constructors
+
+ public HashedLinkedList()
+ : this( null )
+ {
+ }
+
+ public HashedLinkedList( IEnumerable collection )
+ {
+ if( collection == null )
+ {
+ m_innerLinkedList = new LinkedList();
+ }
+ else
+ {
+ m_innerLinkedList = new LinkedList( collection );
+ }
+
+ m_nodeDictionary = new Dictionary>( m_innerLinkedList.Count );
+
+ var node = m_innerLinkedList.First;
+
+ while( node != null )
+ {
+ var value = node.Value;
+
+ this.ValidateValue( value );
+
+ m_nodeDictionary.Add( value, node );
+ }
+ }
+
+ #endregion
+
+ #region Validation
+
+ private void ValidateNewNode( LinkedListNode node )
+ {
+ if( node == null )
+ throw new ArgumentNullException( "node" );
+
+ this.ValidateValue( node.Value );
+ }
+
+ private void ValidateExistingNode( LinkedListNode node )
+ {
+ if( node == null )
+ throw new ArgumentNullException( "node" );
+
+ if( node.List != m_innerLinkedList )
+ throw new InvalidOperationException( "The specified LinkedListNode does belong to this LinkedList." );
+ }
+
+ private void ValidateValue( T value )
+ {
+ if( value == null )
+ throw new InvalidOperationException( "Null values are not supported by the HashedLinkedList" );
+
+ if( m_nodeDictionary.ContainsKey( value ) )
+ throw new ArgumentException( "The HashedLinkedList already contains this value.", "value" );
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ public LinkedListNode AddAfter( LinkedListNode node, T value )
+ {
+ this.ValidateValue( value );
+
+ var newNode = m_innerLinkedList.AddAfter( node, value );
+
+ m_nodeDictionary.Add( value, newNode );
+
+ return newNode;
+ }
+
+ public void AddAfter( LinkedListNode node, LinkedListNode newNode )
+ {
+ this.ValidateNewNode( newNode );
+
+ m_innerLinkedList.AddAfter( node, newNode );
+
+ m_nodeDictionary.Add( newNode.Value, newNode );
+ }
+
+ public LinkedListNode AddBefore( LinkedListNode node, T value )
+ {
+ this.ValidateValue( value );
+
+ var newNode = m_innerLinkedList.AddBefore( node, value );
+
+ m_nodeDictionary.Add( value, newNode );
+
+ return newNode;
+ }
+
+ public void AddBefore( LinkedListNode node, LinkedListNode newNode )
+ {
+ this.ValidateNewNode( newNode );
+
+ m_innerLinkedList.AddBefore( node, newNode );
+
+ m_nodeDictionary.Add( newNode.Value, newNode );
+ }
+
+ public LinkedListNode AddFirst( T value )
+ {
+ this.ValidateValue( value );
+
+ var newNode = m_innerLinkedList.AddFirst( value );
+
+ m_nodeDictionary.Add( value, newNode );
+
+ return newNode;
+ }
+
+ public void AddFirst( LinkedListNode node )
+ {
+ this.ValidateNewNode( node );
+
+ m_innerLinkedList.AddFirst( node );
+
+ m_nodeDictionary.Add( node.Value, node );
+ }
+
+
+ public LinkedListNode AddLast( T value )
+ {
+ this.ValidateValue( value );
+
+ var newNode = m_innerLinkedList.AddLast( value );
+
+ m_nodeDictionary.Add( value, newNode );
+
+ return newNode;
+ }
+
+ public void AddLast( LinkedListNode node )
+ {
+ this.ValidateNewNode( node );
+
+ m_innerLinkedList.AddLast( node );
+
+ m_nodeDictionary.Add( node.Value, node );
+ }
+
+ public void Clear()
+ {
+ m_innerLinkedList.Clear();
+ m_nodeDictionary.Clear();
+ }
+
+ public bool Contains( T value )
+ {
+ return m_nodeDictionary.ContainsKey( value );
+ }
+
+ public void CopyTo( T[] array, int index )
+ {
+ m_innerLinkedList.CopyTo( array, index );
+ }
+
+ public LinkedListNode Find( T value )
+ {
+ if( value == null )
+ return null;
+
+ LinkedListNode node = null;
+
+ m_nodeDictionary.TryGetValue( value, out node );
+
+ return node;
+ }
+
+ public LinkedList.Enumerator GetEnumerator()
+ {
+ return m_innerLinkedList.GetEnumerator();
+ }
+
+
+ public bool Remove( T value )
+ {
+ LinkedListNode node = this.Find( value );
+
+ if( node != null )
+ {
+ this.Remove( node );
+ return true;
+ }
+
+ return false;
+ }
+
+ public void Remove( LinkedListNode node )
+ {
+ m_innerLinkedList.Remove( node );
+ m_nodeDictionary.Remove( node.Value );
+ }
+
+ public void RemoveFirst()
+ {
+ this.Remove( this.First );
+ }
+
+ public void RemoveLast()
+ {
+ this.Remove( this.Last );
+ }
+
+ #endregion
+
+ #region Count Property
+
+ public int Count
+ {
+ get
+ {
+ return m_innerLinkedList.Count;
+ }
+ }
+
+ #endregion
+
+ #region First Property
+
+ public LinkedListNode First
+ {
+ get
+ {
+ return m_innerLinkedList.First;
+ }
+ }
+
+
+ #endregion
+
+ #region Last Property
+
+ public LinkedListNode Last
+ {
+ get
+ {
+ return m_innerLinkedList.Last;
+ }
+ }
+
+ #endregion
+
+ #region Private Fields
+
+ private LinkedList m_innerLinkedList;
+ private Dictionary> m_nodeDictionary;
+
+ #endregion
+
+
+ #region ICollection Members
+
+ void ICollection.Add( T value )
+ {
+ this.AddLast( value );
+ }
+
+ void ICollection.Clear()
+ {
+ this.Clear();
+ }
+
+ bool ICollection.Contains( T value )
+ {
+ return this.Contains( value );
+ }
+
+ void ICollection.CopyTo( T[] array, int arrayIndex )
+ {
+ this.CopyTo( array, arrayIndex );
+ }
+
+ int ICollection.Count
+ {
+ get
+ {
+ return this.Count;
+ }
+ }
+
+ bool ICollection.IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ bool ICollection.Remove( T value )
+ {
+ return this.Remove( value );
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ void ICollection.CopyTo( Array array, int index )
+ {
+ ( ( ICollection )m_innerLinkedList ).CopyTo( array, index );
+ }
+
+ int ICollection.Count
+ {
+ get
+ {
+ return this.Count;
+ }
+ }
+
+ bool ICollection.IsSynchronized
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ object ICollection.SyncRoot
+ {
+ get
+ {
+ return ( ( ICollection )m_innerLinkedList ).SyncRoot;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HeaderFooterItem.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HeaderFooterItem.cs
new file mode 100644
index 00000000..a6ca0998
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HeaderFooterItem.cs
@@ -0,0 +1,558 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Media;
+
+using Xceed.Wpf.DataGrid.Print;
+using Xceed.Utils.Wpf;
+using System.Windows.Automation.Peers;
+using Xceed.Wpf.DataGrid.Automation;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class HeaderFooterItem : ContentPresenter, IPrintInfo, IDataGridItemContainer
+ {
+ #region CONSTRUCTORS
+
+ static HeaderFooterItem()
+ {
+ KeyboardNavigation.TabNavigationProperty.OverrideMetadata( typeof( HeaderFooterItem ), new FrameworkPropertyMetadata( KeyboardNavigationMode.None ) );
+
+ HeaderFooterItem.ContainerProperty = ContainerPropertyKey.DependencyProperty;
+
+ m_sItemIndexBinding = new Binding();
+ m_sItemIndexBinding.RelativeSource = RelativeSource.Self;
+ m_sItemIndexBinding.Path = new PropertyPath( "(0).(1)", HeaderFooterItem.ContainerProperty, DataGridVirtualizingPanel.ItemIndexProperty );
+ m_sItemIndexBinding.Mode = BindingMode.OneWay;
+
+ m_sIsCurrentInternalBinding = new Binding();
+ m_sIsCurrentInternalBinding.RelativeSource = RelativeSource.Self;
+ m_sIsCurrentInternalBinding.Path = new PropertyPath( "(0).(1)", HeaderFooterItem.ContainerProperty, Row.IsCurrentProperty );
+ m_sIsCurrentInternalBinding.Mode = BindingMode.OneWay;
+
+ m_sIsBeingEditedInternalBinding = new Binding();
+ m_sIsBeingEditedInternalBinding.RelativeSource = RelativeSource.Self;
+ m_sIsBeingEditedInternalBinding.Path = new PropertyPath( "(0).(1)", HeaderFooterItem.ContainerProperty, Row.IsBeingEditedProperty );
+ m_sIsBeingEditedInternalBinding.Mode = BindingMode.OneWay;
+
+ m_sHasValidationErrorInternalBinding = new Binding();
+ m_sHasValidationErrorInternalBinding.RelativeSource = RelativeSource.Self;
+ m_sHasValidationErrorInternalBinding.Path = new PropertyPath( "(0).(1)", HeaderFooterItem.ContainerProperty, Row.HasValidationErrorProperty );
+ m_sHasValidationErrorInternalBinding.Mode = BindingMode.OneWay;
+
+ m_sRowSelectorVisibleBinding = new Binding();
+ m_sRowSelectorVisibleBinding.RelativeSource = RelativeSource.Self;
+ m_sRowSelectorVisibleBinding.Path = new PropertyPath( "(0).(1)", HeaderFooterItem.ContainerProperty, RowSelector.VisibleProperty );
+ m_sRowSelectorVisibleBinding.Mode = BindingMode.OneWay;
+
+ m_sRowSelectorStyleBinding = new Binding();
+ m_sRowSelectorStyleBinding.RelativeSource = RelativeSource.Self;
+ m_sRowSelectorStyleBinding.Path = new PropertyPath( "(0).(1)", HeaderFooterItem.ContainerProperty, RowSelector.RowSelectorStyleProperty );
+ m_sRowSelectorStyleBinding.Mode = BindingMode.OneWay;
+
+ }
+
+ internal HeaderFooterItem()
+ {
+ //to prevent creation of the headerfooteritem by anybody else than us.
+ BindingOperations.SetBinding( this, HeaderFooterItem.ItemIndexProperty, m_sItemIndexBinding );
+ BindingOperations.SetBinding( this, HeaderFooterItem.IsBeingEditedInternalProperty, m_sIsBeingEditedInternalBinding );
+ BindingOperations.SetBinding( this, HeaderFooterItem.IsCurrentInternalProperty, m_sIsCurrentInternalBinding );
+ BindingOperations.SetBinding( this, HeaderFooterItem.HasValidationErrorInternalProperty, m_sHasValidationErrorInternalBinding );
+
+ BindingOperations.SetBinding( this, RowSelector.VisibleProperty, m_sRowSelectorVisibleBinding );
+ BindingOperations.SetBinding( this, RowSelector.RowSelectorStyleProperty, m_sRowSelectorStyleBinding );
+ }
+
+ #endregion
+
+ #region IsCurrentInternal
+
+ private static readonly DependencyProperty IsCurrentInternalProperty =
+ DependencyProperty.Register( "IsCurrentInternal", typeof( bool ), typeof( HeaderFooterItem ),
+ new FrameworkPropertyMetadata( ( bool )false,
+ new PropertyChangedCallback( OnIsCurrentInternalChanged ) ) );
+
+ private static void OnIsCurrentInternalChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
+ {
+ ( ( HeaderFooterItem )d ).OnIsCurrentInternalChanged( e );
+ }
+
+ protected virtual void OnIsCurrentInternalChanged( DependencyPropertyChangedEventArgs e )
+ {
+ this.SetValue( Row.IsCurrentPropertyKey, e.NewValue );
+ }
+
+ #endregion
+
+ #region IsBeingEditedInternal
+
+ private static readonly DependencyProperty IsBeingEditedInternalProperty =
+ DependencyProperty.Register( "IsBeingEditedInternal", typeof( bool ), typeof( HeaderFooterItem ),
+ new FrameworkPropertyMetadata( ( bool )false,
+ new PropertyChangedCallback( OnIsBeingEditedInternalChanged ) ) );
+
+ private static void OnIsBeingEditedInternalChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
+ {
+ ( ( HeaderFooterItem )d ).OnIsBeingEditedInternalChanged( e );
+ }
+
+ protected virtual void OnIsBeingEditedInternalChanged( DependencyPropertyChangedEventArgs e )
+ {
+ this.SetValue( Row.IsBeingEditedPropertyKey, e.NewValue );
+ }
+
+ #endregion
+
+ #region HasValidationErrorInternal
+
+ private static readonly DependencyProperty HasValidationErrorInternalProperty =
+ DependencyProperty.Register( "HasValidationErrorInternal", typeof( bool ), typeof( HeaderFooterItem ),
+ new FrameworkPropertyMetadata( ( bool )false,
+ new PropertyChangedCallback( OnHasValidationErrorInternalChanged ) ) );
+
+ private static void OnHasValidationErrorInternalChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
+ {
+ ( ( HeaderFooterItem )d ).OnHasValidationErrorInternalChanged( e );
+ }
+
+ protected virtual void OnHasValidationErrorInternalChanged( DependencyPropertyChangedEventArgs e )
+ {
+ this.SetValue( Row.HasValidationErrorPropertyKey, e.NewValue );
+ }
+
+ #endregion
+
+ #region IsCurrent Property
+
+ public static readonly DependencyProperty IsCurrentProperty = Row.IsCurrentProperty.AddOwner( typeof( HeaderFooterItem ) );
+
+ public bool IsCurrent
+ {
+ get
+ {
+ return ( bool )this.GetValue( IsCurrentProperty );
+ }
+ }
+
+ #endregion
+
+ #region IsBeingEdited Property
+
+ public static readonly DependencyProperty IsBeingEditedProperty = Row.IsBeingEditedProperty.AddOwner( typeof( HeaderFooterItem ) );
+
+ public bool IsBeingEdited
+ {
+ get
+ {
+ return ( bool )this.GetValue( IsBeingEditedProperty );
+ }
+ }
+
+ #endregion
+
+ #region HasValidationError Property
+
+ public static readonly DependencyProperty HasValidationErrorProperty = Row.HasValidationErrorProperty.AddOwner( typeof( HeaderFooterItem ) );
+
+ public bool HasValidationError
+ {
+ get
+ {
+ return ( bool )this.GetValue( HasValidationErrorProperty );
+ }
+ }
+
+ #endregion
+
+ #region ItemIndex Property
+
+ public static readonly DependencyProperty ItemIndexProperty = DataGridVirtualizingPanel.ItemIndexProperty.AddOwner( typeof( HeaderFooterItem ) );
+
+ #endregion
+
+ #region Container Property
+
+ private static readonly DependencyPropertyKey ContainerPropertyKey
+ = DependencyProperty.RegisterReadOnly( "Container", typeof( DependencyObject ), typeof( HeaderFooterItem ),
+ new FrameworkPropertyMetadata( ( DependencyObject )null ) );
+
+ public static readonly DependencyProperty ContainerProperty;
+
+ public DependencyObject Container
+ {
+ get
+ {
+ return ( DependencyObject )this.GetValue( ContainerProperty );
+ }
+ }
+
+ private void SetContainer( DependencyObject value )
+ {
+ this.SetValue( ContainerPropertyKey, value );
+ }
+
+ #endregion
+
+ #region VisualRootElementType Property
+
+ internal Type VisualRootElementType
+ {
+ get
+ {
+ Type elementType = null;
+ DataTemplate template = m_initializingDataItem as DataTemplate;
+
+ if( template == null )
+ template = this.ContentTemplate;
+
+ if( ( template != null ) && ( template.VisualTree != null ) )
+ elementType = template.VisualTree.Type;
+
+ if( elementType == null )
+ {
+ Visual visual = this.AsVisual();
+
+ if( visual == null )
+ {
+ //this will force the control to apply the content template "inline"...
+ this.ApplyTemplate();
+
+ visual = this.AsVisual();
+ }
+
+ Debug.Assert( visual != null );
+
+
+ if( visual != null )
+ elementType = visual.GetType();
+ }
+
+ return elementType;
+ }
+ }
+
+ #endregion
+
+ #region PUBLIC METHODS
+
+ public Visual AsVisual()
+ {
+ if( this.VisualChildrenCount == 0 )
+ return null;
+
+ return this.GetVisualChild( 0 );
+ }
+
+ #endregion
+
+ #region PROTECTED METHODS
+
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new FrameworkElementAutomationPeer( this );
+ }
+
+ protected internal void PrepareContainer( DataGridContext dataGridContext, object item )
+ {
+ if( dataGridContext == null )
+ throw new ArgumentNullException( "dataGridContext" );
+
+
+ m_initializingDataGridContext = dataGridContext;
+ m_initializingDataItem = item;
+
+ if( this.IsLoaded == false )
+ {
+ if( dataGridContext.DataGridControl.IsPrinting )
+ {
+ this.ApplyTemplate();
+ this.PrepareContainerWorker();
+ }
+ else if( dataGridContext.DataGridControl.ItemsHost.IsAncestorOf( this ) )
+ {
+ this.ApplyTemplate();
+ this.PrepareContainerWorker();
+ }
+ else
+ {
+ // When a HeaderFooterItem is in the FixedHeaders or FixedFooters of the grid,
+ // we must prepare the container only when the container is loaded so that the
+ // TableView.CanScrollHorizontallyProperty attached property is taken into consideration.
+ m_loadedHandler = this.HeaderFooterItem_Loaded;
+ this.Loaded += m_loadedHandler;
+ }
+ }
+ else
+ {
+ this.PrepareContainerWorker();
+ }
+ }
+
+ protected internal void ClearContainer()
+ {
+ Visual visual = this.AsVisual();
+
+ // If the root of a HeaderFooterItem is not a IDataGridItemContainer,
+ // drill down the VisualTree to ensure there are none
+ var container = HeaderFooterItem.FindIDataGridItemContainerInChildren( this, visual );
+
+ while( container != null )
+ {
+ container.ClearContainer();
+ container = HeaderFooterItem.FindIDataGridItemContainerInChildren( this, ( DependencyObject )container, false );
+ }
+
+ // Ensure we removed the HeaderFooterItem_Loaded event handler if
+ // it was not yet loaded
+ if( m_loadedHandler != null )
+ {
+ this.Loaded -= m_loadedHandler;
+ m_loadedHandler = null;
+ }
+ }
+
+ protected override void OnIsKeyboardFocusWithinChanged( DependencyPropertyChangedEventArgs e )
+ {
+ base.OnIsKeyboardFocusWithinChanged( e );
+
+ //In this case, since the first visual child is not a row, I want to stub the IsCurrent property with the KeyboardFocus.
+ Row row = this.AsVisual() as Row;
+
+ if( row == null )
+ {
+ this.SetValue( IsCurrentInternalProperty, e.NewValue );
+ }
+ }
+
+ protected override void OnMouseLeftButtonDown( MouseButtonEventArgs e )
+ {
+ base.OnMouseLeftButtonDown( e );
+
+ if( !e.Handled )
+ {
+ e.Handled = DataGridControl.SetFocusUIElementHelper( this );
+ }
+ }
+
+ #endregion
+
+ #region INTERNAL METHODS
+
+ internal static IDataGridItemContainer FindIDataGridItemContainerInChildren( HeaderFooterItem headerFooterItem, DependencyObject currentChild )
+ {
+ return HeaderFooterItem.FindIDataGridItemContainerInChildren( headerFooterItem, currentChild, true );
+ }
+
+ internal static IDataGridItemContainer FindIDataGridItemContainerInChildren( HeaderFooterItem headerFooterItem, DependencyObject currentChild, bool checkRoot )
+ {
+ if( currentChild == null )
+ return null;
+
+ IDataGridItemContainer container = currentChild as IDataGridItemContainer;
+
+ if( checkRoot )
+ {
+ if( container != null )
+ return container;
+ }
+
+ int childCount = VisualTreeHelper.GetChildrenCount( currentChild );
+
+ for( int i = 0; i < childCount; i++ )
+ {
+ DependencyObject child = VisualTreeHelper.GetChild( currentChild, i );
+
+ if( child == null )
+ continue;
+
+ if( !HeaderFooterItem.IsPartOfHeaderFooterItemTemplate( child, headerFooterItem ) )
+ {
+ // We don't want to parse further the DataTemplate
+ return null;
+ }
+
+ container = child as IDataGridItemContainer;
+
+ if( container != null )
+ return container;
+
+ int innerChildCount = VisualTreeHelper.GetChildrenCount( child );
+
+ if( innerChildCount > 0 )
+ container = HeaderFooterItem.FindIDataGridItemContainerInChildren( headerFooterItem, child, false );
+
+ if( container != null )
+ return container;
+ }
+
+ return null;
+ }
+
+ private static bool IsPartOfHeaderFooterItemTemplate( DependencyObject element, HeaderFooterItem headerFooterItem )
+ {
+ while( element != null )
+ {
+ DependencyObject templatedParent = null;
+
+ var frameworkElement = element as FrameworkElement;
+ if( frameworkElement != null )
+ {
+ templatedParent = frameworkElement.TemplatedParent;
+ }
+ else
+ {
+ var frameworkContentElement = element as FrameworkContentElement;
+
+ if( frameworkContentElement != null )
+ {
+ templatedParent = frameworkContentElement.TemplatedParent;
+ }
+ }
+
+ if( templatedParent == headerFooterItem )
+ return true;
+
+ element = templatedParent;
+ }
+
+ return false;
+ }
+
+ #endregion INTERNAL METHODS
+
+ #region PRIVATE METHODS
+
+ private void HeaderFooterItem_Loaded( object sender, RoutedEventArgs e )
+ {
+ if( m_loadedHandler != null )
+ {
+ this.Loaded -= m_loadedHandler;
+ m_loadedHandler = null;
+ }
+
+ this.PrepareContainerWorker();
+ //Bind properties that CAN be bound (if the DataTemplate instantiated a Row as first visual child )
+ }
+
+ private void PrepareContainerWorker()
+ {
+ DependencyObject visualTree = this.AsVisual();
+
+ this.SetContainer( visualTree );
+
+ // If the root of a HeaderFooterItem is not a IDataGridItemContainer,
+ // drill down the VisualTree to ensure there are none
+ var container = HeaderFooterItem.FindIDataGridItemContainerInChildren( this, visualTree, true );
+
+ // Loop to prepare all IDataGridContainer found along the way.
+ while( container != null )
+ {
+ // Row based objects apply their template though their implementation of PrepareContainer.
+ // No need to call ApplyTemplate beforehand.
+ if( !( container is Row ) )
+ {
+ var frameworkElement = container as FrameworkElement;
+
+ if( frameworkElement != null )
+ frameworkElement.ApplyTemplate();
+ }
+
+ container.PrepareContainer( m_initializingDataGridContext, m_initializingDataItem );
+ container = HeaderFooterItem.FindIDataGridItemContainerInChildren( this, ( DependencyObject )container, false );
+ }
+
+
+ m_initializingDataGridContext = null;
+ m_initializingDataItem = null;
+ }
+
+ #endregion
+
+ #region PRIVATE FIELDS
+
+ private DataGridContext m_initializingDataGridContext; // = null
+ private object m_initializingDataItem; // = null
+
+ private static readonly Binding m_sIsCurrentInternalBinding;
+ private static readonly Binding m_sItemIndexBinding;
+ private static readonly Binding m_sIsBeingEditedInternalBinding;
+ private static readonly Binding m_sHasValidationErrorInternalBinding;
+ private static readonly Binding m_sRowSelectorVisibleBinding;
+ private static readonly Binding m_sRowSelectorStyleBinding;
+
+ private RoutedEventHandler m_loadedHandler;
+
+ #endregion
+
+ #region IPrintInfo Members
+
+ double IPrintInfo.GetPageRightOffset( double horizontalOffset, double viewportWidth )
+ {
+ IPrintInfo subPrintInfo = this.Content as IPrintInfo;
+
+ if( subPrintInfo != null )
+ return subPrintInfo.GetPageRightOffset( horizontalOffset, viewportWidth );
+
+ return horizontalOffset + viewportWidth;
+ }
+
+ void IPrintInfo.UpdateElementVisibility( double horizontalOffset, double viewportWidth, object state )
+ {
+ IPrintInfo subPrintInfo = this.AsVisual() as IPrintInfo;
+
+ if( subPrintInfo != null )
+ subPrintInfo.UpdateElementVisibility( horizontalOffset, viewportWidth, state );
+ }
+
+ object IPrintInfo.CreateElementVisibilityState()
+ {
+ IPrintInfo subPrintInfo = this.AsVisual() as IPrintInfo;
+ if( subPrintInfo == null )
+ return null;
+
+ return subPrintInfo.CreateElementVisibilityState();
+ }
+
+ #endregion
+
+ #region IDataGridItemContainer Members
+
+ void IDataGridItemContainer.PrepareContainer( DataGridContext dataGridContext, object item )
+ {
+ this.PrepareContainer( dataGridContext, item );
+ }
+
+ void IDataGridItemContainer.ClearContainer()
+ {
+ this.ClearContainer();
+ }
+
+ #endregion
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HierarchicalGroupByControl.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HierarchicalGroupByControl.cs
new file mode 100644
index 00000000..2f41d7f4
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HierarchicalGroupByControl.cs
@@ -0,0 +1,689 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using Xceed.Utils.Wpf.DragDrop;
+using System.Diagnostics;
+using System.Windows.Media;
+using System.Windows.Data;
+using Xceed.Utils.Wpf;
+using System.Windows.Documents;
+using Xceed.Wpf.DataGrid.Views;
+using System.ComponentModel;
+
+namespace Xceed.Wpf.DataGrid
+{
+ [TemplatePart( Name = "PART_HierarchicalGroupByControlTreeView", Type = typeof( TreeView ) )]
+ public class HierarchicalGroupByControl : Control, IDropTarget, INotifyPropertyChanged
+ {
+ #region CONSTRUCTORS
+
+ static HierarchicalGroupByControl()
+ {
+ // This DefaultStyleKey will only be used in design-time.
+ DefaultStyleKeyProperty.OverrideMetadata( typeof( HierarchicalGroupByControl ), new FrameworkPropertyMetadata( new Markup.ThemeKey( typeof( Views.TableView ), typeof( HierarchicalGroupByControl ) ) ) );
+
+ // Default binding to HierarchicalGroupByControl value
+ FrameworkElementFactory staircaseFactory = new FrameworkElementFactory( typeof( StaircasePanel ) );
+ ItemsPanelTemplate itemsPanelTemplate = new ItemsPanelTemplate( staircaseFactory );
+
+ Binding binding = new Binding();
+ binding.Path = new PropertyPath( HierarchicalGroupByControl.ConnectionLineAlignmentProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = RelativeSource.Self;
+ staircaseFactory.SetBinding( StaircasePanel.ConnectionLineAlignmentProperty, binding );
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( HierarchicalGroupByControl.ConnectionLineOffsetProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = RelativeSource.Self;
+ staircaseFactory.SetBinding( StaircasePanel.ConnectionLineOffsetProperty, binding );
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( HierarchicalGroupByControl.ConnectionLinePenProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = RelativeSource.Self;
+ staircaseFactory.SetBinding( StaircasePanel.ConnectionLinePenProperty, binding );
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( HierarchicalGroupByControl.StairHeightProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = RelativeSource.Self;
+ staircaseFactory.SetBinding( StaircasePanel.StairHeightProperty, binding );
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( HierarchicalGroupByControl.StairSpacingProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = RelativeSource.Self;
+ staircaseFactory.SetBinding( StaircasePanel.StairSpacingProperty, binding );
+
+ itemsPanelTemplate.Seal();
+
+ ItemsControl.ItemsPanelProperty.OverrideMetadata( typeof( HierarchicalGroupByControl ), new FrameworkPropertyMetadata( itemsPanelTemplate ) );
+
+ DataGridControl.ParentDataGridControlPropertyKey.OverrideMetadata( typeof( HierarchicalGroupByControl ), new FrameworkPropertyMetadata( new PropertyChangedCallback( HierarchicalGroupByControl.ParentGridControlChangedCallback ) ) );
+ FocusableProperty.OverrideMetadata( typeof( HierarchicalGroupByControl ), new FrameworkPropertyMetadata( false ) );
+
+ }
+
+ #endregion
+
+ #region AllowGroupingModification Property
+
+ public static readonly DependencyProperty AllowGroupingModificationProperty =
+ GroupByControl.AllowGroupingModificationProperty.AddOwner( typeof( HierarchicalGroupByControl ), new UIPropertyMetadata( true ) );
+
+ public bool AllowGroupingModification
+ {
+ get
+ {
+ return ( bool )this.GetValue( HierarchicalGroupByControl.AllowGroupingModificationProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControl.AllowGroupingModificationProperty, value );
+ }
+ }
+
+ #endregion AllowGroupingModification Property
+
+ #region AllowSort Property
+
+ public static readonly DependencyProperty AllowSortProperty =
+ ColumnManagerRow.AllowSortProperty.AddOwner( typeof( HierarchicalGroupByControl ), new UIPropertyMetadata( true ) );
+
+ public bool AllowSort
+ {
+ get
+ {
+ return ( bool )this.GetValue( HierarchicalGroupByControl.AllowSortProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControl.AllowSortProperty, value );
+ }
+ }
+
+ #endregion AllowSort Property
+
+ #region ConnectionLineAlignment Property
+
+ public static readonly DependencyProperty ConnectionLineAlignmentProperty =
+ StaircasePanel.ConnectionLineAlignmentProperty.AddOwner( typeof( HierarchicalGroupByControl ) );
+
+ public ConnectionLineAlignment ConnectionLineAlignment
+ {
+ get
+ {
+ return ( ConnectionLineAlignment )this.GetValue( HierarchicalGroupByControl.ConnectionLineAlignmentProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControl.ConnectionLineAlignmentProperty, value );
+ }
+ }
+
+ #endregion ConnectionLineAlignment Property
+
+ #region ConnectionLineOffset Property
+
+ public static readonly DependencyProperty ConnectionLineOffsetProperty =
+ StaircasePanel.ConnectionLineOffsetProperty.AddOwner( typeof( HierarchicalGroupByControl ) );
+
+ public double ConnectionLineOffset
+ {
+ get
+ {
+ return ( double )this.GetValue( HierarchicalGroupByControl.ConnectionLineOffsetProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControl.ConnectionLineOffsetProperty, value );
+ }
+ }
+
+ #endregion ConnectionLineOffset Property
+
+ #region ConnectionLinePen Property
+
+ public static readonly DependencyProperty ConnectionLinePenProperty =
+ StaircasePanel.ConnectionLinePenProperty.AddOwner( typeof( HierarchicalGroupByControl ) );
+
+ public Pen ConnectionLinePen
+ {
+ get
+ {
+ return ( Pen )this.GetValue( HierarchicalGroupByControl.ConnectionLinePenProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControl.ConnectionLinePenProperty, value );
+ }
+ }
+
+ #endregion ConnectionLinePen Property
+
+ #region StairHeight Property
+
+ public static readonly DependencyProperty StairHeightProperty =
+ StaircasePanel.StairHeightProperty.AddOwner( typeof( HierarchicalGroupByControl ) );
+
+ public double StairHeight
+ {
+ get
+ {
+ return ( double )this.GetValue( HierarchicalGroupByControl.StairHeightProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControl.StairHeightProperty, value );
+ }
+ }
+
+ #endregion StairHeight Property
+
+ #region StairSpacing Property
+
+ public static readonly DependencyProperty StairSpacingProperty =
+ StaircasePanel.StairSpacingProperty.AddOwner( typeof( HierarchicalGroupByControl ) );
+
+ public double StairSpacing
+ {
+ get
+ {
+ return ( double )this.GetValue( HierarchicalGroupByControl.StairSpacingProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControl.StairSpacingProperty, value );
+ }
+ }
+
+ #endregion StairSpacing Property
+
+ #region HasGroups
+
+ public bool HasGroups
+ {
+ get
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext == null )
+ return false;
+
+ // We only display the NoGroupContent if the HierarchicalGroupByControl is at the master level
+ if( dataGridContext.ParentDataGridContext != null )
+ return false;
+
+ // We look for GroupLevelDescription in the DataGridContext and all details
+ return ( GroupingHelper.HasGroup( dataGridContext ) == false );
+ }
+ }
+
+ #endregion
+
+ #region NoGroupContent Property
+
+ public static readonly DependencyProperty NoGroupContentProperty =
+ GroupByControl.NoGroupContentProperty.AddOwner(
+ typeof( HierarchicalGroupByControl ),
+ new PropertyMetadata( "Drag a column header here to group by that column." ) );
+
+ public object NoGroupContent
+ {
+ get
+ {
+ return this.GetValue( HierarchicalGroupByControl.NoGroupContentProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControl.NoGroupContentProperty, value );
+ }
+ }
+
+ #endregion NoGroupContent Property
+
+ #region PROTECTED METHODS
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ if( ( LicenseManager.UsageMode != LicenseUsageMode.Designtime )
+ && ( m_watermarkAdorner == null ) )
+ {
+ AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer( this );
+ m_watermarkAdorner = new WatermarkAdorner( this );
+ adornerLayer.Add( m_watermarkAdorner );
+ }
+ }
+
+ #endregion PROTECTED METHODS
+
+ #region PROTECTED INTERNAL METHODS
+
+ protected internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( HierarchicalGroupByControl ) );
+ }
+
+ #endregion
+
+ #region PRIVATE STATIC METHODS
+
+ private static HierarchicalGroupByControlNode GetHierarchicalGroupByControlNode( UIElement element )
+ {
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = element as HierarchicalGroupByControlNode;
+
+ if( hierarchicalGroupByControlNode != null )
+ return hierarchicalGroupByControlNode;
+
+ int childCount = VisualTreeHelper.GetChildrenCount( element );
+
+ for( int i = 0; i < childCount; i++ )
+ {
+ UIElement child = VisualTreeHelper.GetChild( element, i ) as UIElement;
+
+ if( child != null )
+ hierarchicalGroupByControlNode = HierarchicalGroupByControl.GetHierarchicalGroupByControlNode( child );
+
+ if( hierarchicalGroupByControlNode != null )
+ break;
+ }
+
+ return hierarchicalGroupByControlNode;
+ }
+
+ private static TreeViewItem GetTreeViewItemFromGroupLevelDescriptionCollection( TreeViewItem rootItem, GroupLevelDescriptionCollection groupLevelDescriptions )
+ {
+ TreeViewItem returned = null;
+ Debug.Assert( rootItem != null );
+ Debug.Assert( groupLevelDescriptions != null );
+
+ if( rootItem == null )
+ throw new DataGridInternalException();
+
+ foreach( object item in rootItem.Items )
+ {
+ TreeViewItem child = rootItem.ItemContainerGenerator.ContainerFromItem( item ) as TreeViewItem;
+
+ // It may not be visible
+ if( child == null )
+ continue;
+
+ DetailConfiguration detailConfiguration = child.Header as DetailConfiguration;
+
+ if( detailConfiguration == null )
+ throw new DataGridInternalException( "The item's DataContext must be a DetailConfiguration except for the top-most HierarchicalGroupByControl, which contains a DataGridContext." );
+
+ if( groupLevelDescriptions == detailConfiguration.GroupLevelDescriptions )
+ {
+ returned = child;
+ break;
+ }
+
+ returned = HierarchicalGroupByControl.GetTreeViewItemFromGroupLevelDescriptionCollection( child, groupLevelDescriptions );
+
+ if( returned != null )
+ break;
+ }
+
+ return returned;
+ }
+
+ private static TreeViewItem GetTreeViewItemFromGroupLevelDescription( TreeViewItem rootItem, GroupLevelDescription groupLevelDescription )
+ {
+ TreeViewItem returned = null;
+ Debug.Assert( rootItem != null );
+ Debug.Assert( groupLevelDescription != null );
+
+ if( rootItem == null )
+ throw new DataGridInternalException();
+
+ foreach( object item in rootItem.Items )
+ {
+ TreeViewItem child = rootItem.ItemContainerGenerator.ContainerFromItem( item ) as TreeViewItem;
+
+ Debug.Assert( child != null );
+ if( child == null )
+ throw new DataGridInternalException( "An item does not contain a valid item." );
+
+ DetailConfiguration detailConfiguration = child.Header as DetailConfiguration;
+
+ if( detailConfiguration == null )
+ throw new DataGridInternalException( "An item's DataContext must be a DetailConfiguration except for the top-most HierarchicalGroupByControl, which contains a DataGridContext." );
+
+ if( detailConfiguration.GroupLevelDescriptions.Contains( groupLevelDescription ) )
+ {
+ returned = child;
+ break;
+ }
+
+ returned = HierarchicalGroupByControl.GetTreeViewItemFromGroupLevelDescription( child, groupLevelDescription );
+
+ if( returned != null )
+ break;
+ }
+
+ return returned;
+ }
+
+ private static void ParentGridControlChangedCallback( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ DataGridControl parentDataGrid = e.NewValue as DataGridControl;
+ HierarchicalGroupByControl groupByControl = ( HierarchicalGroupByControl )sender;
+
+ if( parentDataGrid != null )
+ groupByControl.PrepareDefaultStyleKey( parentDataGrid.GetView() );
+ }
+
+ #endregion
+
+ #region INTERNAL METHODS
+
+ internal HierarchicalGroupByControlNode GetHierarchicalGroupByControlNodeFromColumnManagerCell( ColumnManagerCell cell )
+ {
+ Debug.Assert( cell != null );
+ if( cell == null )
+ return null;
+
+ DataGridContext cellDataGridContext = DataGridControl.GetDataGridContext( cell );
+
+ if( cellDataGridContext == null )
+ throw new DataGridInternalException( "A DataGridContext cannot be null on ColumnManagerCell." );
+
+ TreeView treeView = this.GetTemplateChild( @"PART_HierarchicalGroupByControlTreeView" ) as TreeView;
+
+ if( treeView == null )
+ return null;
+
+ if( treeView.Items.Count == 0 )
+ throw new DataGridInternalException( "The HierarchicalGroupByControl should contain at least one grouping level." );
+
+ // The first item is always the top level HierarchicalGroupByControlNode
+ TreeViewItem rootItem = treeView.ItemContainerGenerator.ContainerFromItem( treeView.Items[ 0 ] ) as TreeViewItem;
+
+ // It may not be visible
+ if( rootItem == null )
+ return null;
+
+ TreeViewItem dropMarkedTreeViewItem = null;
+
+ DataGridContext rootDataGridContext = rootItem.Header as DataGridContext;
+
+ if( ( rootDataGridContext != null ) && ( rootDataGridContext.GroupLevelDescriptions == cellDataGridContext.GroupLevelDescriptions ) )
+ {
+ dropMarkedTreeViewItem = rootItem;
+ }
+ else
+ {
+ GroupLevelDescriptionCollection groupLevelDescriptions = cellDataGridContext.GroupLevelDescriptions;
+
+ dropMarkedTreeViewItem = HierarchicalGroupByControl.GetTreeViewItemFromGroupLevelDescriptionCollection( rootItem, groupLevelDescriptions );
+ }
+
+ // If null, it means the cell does not belong to this detail
+ if( dropMarkedTreeViewItem == null )
+ return null;
+
+ ContentPresenter treeViewItemHeader = dropMarkedTreeViewItem.Template.FindName( "PART_Header", dropMarkedTreeViewItem ) as ContentPresenter;
+
+ // It may not be visible
+ if( treeViewItemHeader == null )
+ return null;
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = HierarchicalGroupByControl.GetHierarchicalGroupByControlNode( treeViewItemHeader );
+
+ return hierarchicalGroupByControlNode;
+ }
+
+ internal HierarchicalGroupByControlNode GetHierarchicalGroupByControlNodeFromHierarchicalGroupByItem( HierarchicalGroupByItem hierarchicalGroupByItem )
+ {
+ Debug.Assert( hierarchicalGroupByItem != null );
+ if( hierarchicalGroupByItem == null )
+ return null;
+
+ TreeView treeView = this.GetTemplateChild( @"PART_HierarchicalGroupByControlTreeView" ) as TreeView;
+
+ if( treeView == null )
+ return null;
+
+ if( treeView.Items.Count == 0 )
+ throw new DataGridInternalException( "The HierarchicalGroupByControl should contain at least one grouping level." );
+
+ // The first item is always the top level HierarchicalGroupByControlNode
+ TreeViewItem rootItem = treeView.ItemContainerGenerator.ContainerFromItem( treeView.Items[ 0 ] ) as TreeViewItem;
+
+ if( rootItem == null )
+ throw new DataGridInternalException( "The root item is null." );
+
+ GroupLevelDescription detailGroupLevelDescription = hierarchicalGroupByItem.Content as GroupLevelDescription;
+
+ Debug.Assert( detailGroupLevelDescription != null );
+
+ TreeViewItem dropMarkedTreeViewItem = null;
+
+ DataGridContext rootDataGridContext = rootItem.Header as DataGridContext;
+
+ Debug.Assert( rootDataGridContext != null );
+
+ if( rootDataGridContext.GroupLevelDescriptions.Contains( detailGroupLevelDescription ) )
+ {
+ dropMarkedTreeViewItem = rootItem;
+ }
+ else
+ {
+ dropMarkedTreeViewItem = HierarchicalGroupByControl.GetTreeViewItemFromGroupLevelDescription( rootItem, detailGroupLevelDescription );
+ }
+
+
+ // If null, it means the cell does not belong to this detail
+ if( dropMarkedTreeViewItem == null )
+ return null;
+
+ ContentPresenter treeViewItemHeader = dropMarkedTreeViewItem.Template.FindName( "PART_Header", dropMarkedTreeViewItem ) as ContentPresenter;
+
+ Debug.Assert( treeViewItemHeader != null );
+ if( treeViewItemHeader == null )
+ throw new DataGridInternalException( "An error occurred while retrieving the PART_Header template part of an item containing a HierarchicalGroupByControlNode." );
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = HierarchicalGroupByControl.GetHierarchicalGroupByControlNode( treeViewItemHeader );
+
+ return hierarchicalGroupByControlNode;
+ }
+
+ #endregion
+
+ #region IDropTarget Members
+
+
+ bool IDropTarget.CanDropElement( UIElement draggedElement )
+ {
+ bool canDrop = this.AllowGroupingModification;
+
+ ColumnManagerCell cell = null;
+ HierarchicalGroupByItem hierarchicalGroupByItem = null;
+
+ if( canDrop )
+ {
+ cell = draggedElement as ColumnManagerCell;
+
+ if( cell != null )
+ {
+ ColumnBase parentColumn = cell.ParentColumn;
+
+ if( ( parentColumn == null ) || ( !parentColumn.AllowGroup ) )
+ return false;
+
+ // Check if already grouped using the cell's DataGridContext
+ canDrop = !GroupingHelper.IsAlreadyGroupedBy( cell );
+
+ if( canDrop )
+ {
+ DataGridContext thisDataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( thisDataGridContext.Items != null )
+ canDrop = thisDataGridContext.Items.CanGroup;
+
+ if( canDrop )
+ {
+ canDrop = GroupingHelper.IsColumnManagerCellInDataGridContext( thisDataGridContext, cell );
+
+ if( canDrop == true )
+ canDrop = GroupingHelper.ValidateMaxGroupDescriptions( DataGridControl.GetDataGridContext( draggedElement ) );
+ }
+ }
+ }
+ else
+ {
+ hierarchicalGroupByItem = draggedElement as HierarchicalGroupByItem;
+
+ if( hierarchicalGroupByItem == null )
+ canDrop = false;
+
+ if( canDrop )
+ {
+ // Try to get the HierarchicalGroupByControlNode in which this HierarchicalGroupByItem can be added using the parent HierarchicalGroupByControl => null it can't
+ HierarchicalGroupByControlNode draggedHierarchicalGroupByControlNode = this.GetHierarchicalGroupByControlNodeFromHierarchicalGroupByItem( hierarchicalGroupByItem );
+
+ if( draggedHierarchicalGroupByControlNode == null )
+ canDrop = false;
+ }
+ }
+ }
+
+ bool returnedValue = ( ( cell != null ) || ( hierarchicalGroupByItem != null ) ) && // ColumnManagerCell or HierarchicalGroupByItem
+ ( canDrop );
+
+
+ return returnedValue;
+ }
+
+ void IDropTarget.DragEnter( UIElement draggedElement )
+ {
+ }
+
+ void IDropTarget.DragOver( UIElement draggedElement, Point mousePosition )
+ {
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ if( cell != null )
+ {
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = this.GetHierarchicalGroupByControlNodeFromColumnManagerCell( cell );
+
+ // It may not be visible
+ if( hierarchicalGroupByControlNode != null )
+ hierarchicalGroupByControlNode.ShowFarDropMark( cell, mousePosition );
+ }
+ else
+ {
+ HierarchicalGroupByItem hierarchicalGroupByItem = draggedElement as HierarchicalGroupByItem;
+ if( hierarchicalGroupByItem == null )
+ return;
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = this.GetHierarchicalGroupByControlNodeFromHierarchicalGroupByItem( hierarchicalGroupByItem );
+
+ Debug.Assert( hierarchicalGroupByControlNode != null, "CanDrop should have returned false" );
+ if( hierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException( "A HierarchicalGroupByControlNode must exist for every level." );
+
+ hierarchicalGroupByControlNode.ShowFarDropMark( mousePosition );
+ }
+ }
+
+ void IDropTarget.DragLeave( UIElement draggedElement )
+ {
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ if( cell != null )
+ {
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = this.GetHierarchicalGroupByControlNodeFromColumnManagerCell( cell );
+
+ // It may not be visible
+ if( hierarchicalGroupByControlNode != null )
+ hierarchicalGroupByControlNode.HideFarDropMark();
+ }
+ else
+ {
+ HierarchicalGroupByItem hierarchicalGroupByItem = draggedElement as HierarchicalGroupByItem;
+ if( hierarchicalGroupByItem == null )
+ return;
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = this.GetHierarchicalGroupByControlNodeFromHierarchicalGroupByItem( hierarchicalGroupByItem );
+
+ Debug.Assert( hierarchicalGroupByControlNode != null, "CanDrop should have returned false" );
+ if( hierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException( "A HierarchicalGroupByControlNode must exist for every level." );
+
+ hierarchicalGroupByControlNode.HideFarDropMark();
+ }
+ }
+
+ void IDropTarget.Drop( UIElement draggedElement )
+ {
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ if( cell == null )
+ return;
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = this.GetHierarchicalGroupByControlNodeFromColumnManagerCell( cell );
+
+ // It may not be visible
+ if( hierarchicalGroupByControlNode != null )
+ hierarchicalGroupByControlNode.HideFarDropMark( cell );
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl parentGrid = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ GroupingHelper.AppendNewGroupFromColumnManagerCell( cell, parentGrid );
+
+ // Notify groups have changed for NoGroupContent
+ this.UpdateHasGroups();
+ }
+
+ #endregion
+
+ #region INotifyPropertyChanged Members
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private void OnNotifyPropertyChanged( string propertyName )
+ {
+ if( this.PropertyChanged != null )
+ this.PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
+ }
+
+ internal void UpdateHasGroups()
+ {
+ this.OnNotifyPropertyChanged( "HasGroups" );
+ }
+
+ #endregion
+
+ private WatermarkAdorner m_watermarkAdorner;
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HierarchicalGroupByControlNode.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HierarchicalGroupByControlNode.cs
new file mode 100644
index 00000000..648dc6b9
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HierarchicalGroupByControlNode.cs
@@ -0,0 +1,913 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Media;
+using Xceed.Utils.Wpf.DragDrop;
+using System.Diagnostics;
+using System.ComponentModel;
+using System.Collections.ObjectModel;
+using Xceed.Utils.Wpf;
+using System.Windows.Data;
+using System.Windows.Documents;
+using Xceed.Wpf.DataGrid.Views;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class HierarchicalGroupByControlNode : ItemsControl, IDropTarget
+ {
+ #region CONSTRUCTORS
+
+ static HierarchicalGroupByControlNode()
+ {
+ // Default binding to HierarchicalGroupByControlNode value
+ FrameworkElementFactory staircaseFactory = new FrameworkElementFactory( typeof( StaircasePanel ) );
+ ItemsPanelTemplate itemsPanelTemplate = new ItemsPanelTemplate( staircaseFactory );
+ RelativeSource ancestorSource = new RelativeSource( RelativeSourceMode.FindAncestor, typeof( HierarchicalGroupByControl ), 1 );
+
+ Binding binding = new Binding();
+ binding.Path = new PropertyPath( HierarchicalGroupByControlNode.ConnectionLineAlignmentProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = ancestorSource;
+ staircaseFactory.SetBinding( StaircasePanel.ConnectionLineAlignmentProperty, binding );
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( HierarchicalGroupByControlNode.ConnectionLineOffsetProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = ancestorSource;
+ staircaseFactory.SetBinding( StaircasePanel.ConnectionLineOffsetProperty, binding );
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( HierarchicalGroupByControlNode.ConnectionLinePenProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = ancestorSource;
+ staircaseFactory.SetBinding( StaircasePanel.ConnectionLinePenProperty, binding );
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( HierarchicalGroupByControlNode.StairHeightProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = ancestorSource;
+ staircaseFactory.SetBinding( StaircasePanel.StairHeightProperty, binding );
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( HierarchicalGroupByControlNode.StairSpacingProperty );
+ binding.Mode = BindingMode.OneWay;
+ binding.RelativeSource = ancestorSource;
+ staircaseFactory.SetBinding( StaircasePanel.StairSpacingProperty, binding );
+
+ itemsPanelTemplate.Seal();
+
+ ItemsControl.ItemsPanelProperty.OverrideMetadata( typeof( HierarchicalGroupByControlNode ), new FrameworkPropertyMetadata( itemsPanelTemplate ) );
+ DataGridControl.ParentDataGridControlPropertyKey.OverrideMetadata( typeof( HierarchicalGroupByControlNode ), new FrameworkPropertyMetadata( new PropertyChangedCallback( HierarchicalGroupByControlNode.ParentGridControlChangedCallback ) ) );
+ FocusableProperty.OverrideMetadata( typeof( HierarchicalGroupByControlNode ), new FrameworkPropertyMetadata( false ) );
+ }
+
+ #endregion
+
+ #region AllowGroupingModification Property
+
+ public static readonly DependencyProperty AllowGroupingModificationProperty =
+ GroupByControl.AllowGroupingModificationProperty.AddOwner( typeof( HierarchicalGroupByControlNode ), new UIPropertyMetadata( true ) );
+
+ public bool AllowGroupingModification
+ {
+ get
+ {
+ return ( bool )this.GetValue( HierarchicalGroupByControlNode.AllowGroupingModificationProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControlNode.AllowGroupingModificationProperty, value );
+ }
+ }
+
+ #endregion AllowGroupingModification Property
+
+ #region AllowSort Property
+
+ public static readonly DependencyProperty AllowSortProperty =
+ ColumnManagerRow.AllowSortProperty.AddOwner( typeof( HierarchicalGroupByControlNode ), new UIPropertyMetadata( true ) );
+
+ public bool AllowSort
+ {
+ get
+ {
+ return ( bool )this.GetValue( HierarchicalGroupByControlNode.AllowSortProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControlNode.AllowSortProperty, value );
+ }
+ }
+
+ #endregion AllowSort Property
+
+ #region ConnectionLineAlignment Property
+
+ public static readonly DependencyProperty ConnectionLineAlignmentProperty =
+ StaircasePanel.ConnectionLineAlignmentProperty.AddOwner( typeof( HierarchicalGroupByControlNode ) );
+
+ public ConnectionLineAlignment ConnectionLineAlignment
+ {
+ get
+ {
+ return ( ConnectionLineAlignment )this.GetValue( HierarchicalGroupByControlNode.ConnectionLineAlignmentProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControlNode.ConnectionLineAlignmentProperty, value );
+ }
+ }
+
+ #endregion ConnectionLineAlignment Property
+
+ #region ConnectionLineOffset Property
+
+ public static readonly DependencyProperty ConnectionLineOffsetProperty =
+ StaircasePanel.ConnectionLineOffsetProperty.AddOwner( typeof( HierarchicalGroupByControlNode ) );
+
+ public double ConnectionLineOffset
+ {
+ get
+ {
+ return ( double )this.GetValue( HierarchicalGroupByControlNode.ConnectionLineOffsetProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControlNode.ConnectionLineOffsetProperty, value );
+ }
+ }
+
+ #endregion ConnectionLineOffset Property
+
+ #region ConnectionLinePen Property
+
+ public static readonly DependencyProperty ConnectionLinePenProperty =
+ StaircasePanel.ConnectionLinePenProperty.AddOwner( typeof( HierarchicalGroupByControlNode ) );
+
+ public Pen ConnectionLinePen
+ {
+ get
+ {
+ return ( Pen )this.GetValue( HierarchicalGroupByControlNode.ConnectionLinePenProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControlNode.ConnectionLinePenProperty, value );
+ }
+ }
+
+ #endregion ConnectionLinePen Property
+
+ #region StairHeight Property
+
+ public static readonly DependencyProperty StairHeightProperty =
+ StaircasePanel.StairHeightProperty.AddOwner( typeof( HierarchicalGroupByControlNode ) );
+
+ public double StairHeight
+ {
+ get
+ {
+ return ( double )this.GetValue( HierarchicalGroupByControlNode.StairHeightProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControlNode.StairHeightProperty, value );
+ }
+ }
+
+ #endregion StairHeight Property
+
+ #region StairSpacing Property
+
+ public static readonly DependencyProperty StairSpacingProperty =
+ StaircasePanel.StairSpacingProperty.AddOwner( typeof( HierarchicalGroupByControlNode ) );
+
+ public double StairSpacing
+ {
+ get
+ {
+ return ( double )this.GetValue( HierarchicalGroupByControlNode.StairSpacingProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControlNode.StairSpacingProperty, value );
+ }
+ }
+
+ #endregion StairSpacing Property
+
+ #region NoGroupContent Property
+
+ public static readonly DependencyProperty NoGroupContentProperty =
+ GroupByControl.NoGroupContentProperty.AddOwner(
+ typeof( HierarchicalGroupByControlNode ),
+ new PropertyMetadata( "Drag a column header here to group by that column." ) );
+
+ public object NoGroupContent
+ {
+ get
+ {
+ return this.GetValue( HierarchicalGroupByControlNode.NoGroupContentProperty );
+ }
+ set
+ {
+ this.SetValue( HierarchicalGroupByControlNode.NoGroupContentProperty, value );
+ }
+ }
+
+ #endregion NoGroupContent Property
+
+ #region Title
+
+ public object Title
+ {
+ get
+ {
+ return ( object )GetValue( TitleProperty );
+ }
+ set
+ {
+ SetValue( TitleProperty, value );
+ }
+ }
+
+ public static readonly DependencyProperty TitleProperty =
+ DependencyProperty.Register( "Title", typeof( object ), typeof( HierarchicalGroupByControlNode ) );
+
+ #endregion
+
+ // INTERNAL PROPERTIES
+
+ #region DataGridContext Property
+
+ internal DataGridContext DataGridContext
+ {
+ get
+ {
+ DataGridContext dataGridContext = this.DataContext as DataGridContext;
+
+ if( dataGridContext != null )
+ return dataGridContext;
+
+ DetailConfiguration configuration = this.DetailConfiguration;
+
+ if( configuration == null )
+ return null;
+
+ // Ensure to get a DataGridContext created with the same DetailConfiguration
+ return HierarchicalGroupByControlNode.GetDataGridContextFromDetailConfiguration( configuration,
+ DataGridControl.GetDataGridContext( this ) );
+ }
+ }
+
+ #endregion
+
+ #region DetailConfiguration Property
+
+ internal DetailConfiguration DetailConfiguration
+ {
+ get
+ {
+ return this.DataContext as DetailConfiguration;
+ }
+ }
+
+ #endregion
+
+ #region Columns Property
+
+ internal ColumnCollection Columns
+ {
+ get
+ {
+ ColumnCollection columnsCollection = null;
+ DetailConfiguration detailConfiguration = this.DetailConfiguration;
+
+ if( detailConfiguration != null )
+ {
+ columnsCollection = detailConfiguration.Columns;
+ }
+ else
+ {
+ DataGridContext dataGridContext = this.DataGridContext;
+
+ if( dataGridContext != null )
+ columnsCollection = dataGridContext.Columns;
+ }
+ Debug.Assert( ( columnsCollection != null ) || ( DesignerProperties.GetIsInDesignMode( this ) ) );
+ return columnsCollection;
+ }
+ }
+
+ #endregion
+
+ #region GroupDescriptions Property
+
+ internal ObservableCollection GroupDescriptions
+ {
+ get
+ {
+ ObservableCollection groupDescriptions = null;
+ DetailConfiguration detailConfiguration = this.DetailConfiguration;
+
+ if( detailConfiguration != null )
+ {
+ groupDescriptions = detailConfiguration.GroupDescriptions;
+ }
+ else
+ {
+ DataGridContext dataGridContext = this.DataGridContext;
+
+ groupDescriptions = dataGridContext.Items.GroupDescriptions;
+ }
+ Debug.Assert( groupDescriptions != null );
+ return groupDescriptions;
+ }
+ }
+
+ #endregion
+
+ #region GroupLevelDescriptions Property
+
+ internal GroupLevelDescriptionCollection GroupLevelDescriptions
+ {
+ get
+ {
+ GroupLevelDescriptionCollection groupLevelDescriptions = null;
+ DetailConfiguration detailConfiguration = this.DetailConfiguration;
+
+ if( detailConfiguration != null )
+ {
+ groupLevelDescriptions = detailConfiguration.GroupLevelDescriptions;
+ }
+ else
+ {
+ DataGridContext dataGridContext = this.DataGridContext;
+
+ groupLevelDescriptions = dataGridContext.GroupLevelDescriptions;
+ }
+
+ if( groupLevelDescriptions == null )
+ throw new DataGridInternalException( "GroupLevelDescriptions cannot be null on " + typeof( HierarchicalGroupByControlNode ).Name + "." );
+
+ return groupLevelDescriptions;
+ }
+ }
+
+ #endregion
+
+ #region SortDescriptions Property
+
+ internal SortDescriptionCollection SortDescriptions
+ {
+ get
+ {
+ SortDescriptionCollection sortDescriptions = null;
+ DetailConfiguration detailConfiguration = this.DetailConfiguration;
+
+ if( detailConfiguration != null )
+ {
+ sortDescriptions = detailConfiguration.SortDescriptions;
+ }
+ else
+ {
+ DataGridContext dataGridContext = this.DataGridContext;
+
+ sortDescriptions = dataGridContext.Items.SortDescriptions;
+ }
+
+ if( sortDescriptions == null )
+ throw new DataGridInternalException( "GroupLevelDescriptions cannot be null on " + typeof( HierarchicalGroupByControlNode ).Name + "." );
+
+ return sortDescriptions;
+ }
+ }
+
+ #endregion
+
+ #region PROTECTED METHODS
+
+ protected internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( HierarchicalGroupByControlNode ) );
+ }
+
+ protected override DependencyObject GetContainerForItemOverride()
+ {
+ return new HierarchicalGroupByItem();
+ }
+
+ protected override bool IsItemItsOwnContainerOverride( object item )
+ {
+ return item is HierarchicalGroupByItem;
+ }
+
+ protected override void PrepareContainerForItemOverride( DependencyObject element, object item )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl grid = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ base.PrepareContainerForItemOverride( element, item );
+
+ if( grid != null )
+ {
+ HierarchicalGroupByItem groupByItem = ( HierarchicalGroupByItem )element;
+ groupByItem.PrepareDefaultStyleKey( grid.GetView() );
+ }
+ }
+
+ #endregion
+
+ #region PRIVATE STATIC METHODS
+
+ private static void ParentGridControlChangedCallback( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ DataGridControl parentDataGrid = e.NewValue as DataGridControl;
+ HierarchicalGroupByControlNode groupByControl = ( HierarchicalGroupByControlNode )sender;
+
+ if( parentDataGrid != null )
+ groupByControl.PrepareDefaultStyleKey( parentDataGrid.GetView() );
+ }
+
+ private static DataGridContext GetDataGridContextFromDetailConfiguration(
+ DetailConfiguration configuration,
+ DataGridContext parentDataGridContext )
+ {
+ if( ( configuration == null ) || ( parentDataGridContext == null ) )
+ return null;
+
+ if( parentDataGridContext.SourceDetailConfiguration == configuration )
+ return parentDataGridContext;
+
+ foreach( DataGridContext childContext in parentDataGridContext.GetChildContexts() )
+ {
+ DataGridContext foundContext =
+ HierarchicalGroupByControlNode.GetDataGridContextFromDetailConfiguration( configuration,
+ childContext );
+
+ if( foundContext != null )
+ return foundContext;
+ }
+
+ return null;
+ }
+
+ #endregion
+
+ #region INTERNAL METHODS
+
+ internal bool IsGroupingModificationAllowed
+ {
+ get
+ {
+ HierarchicalGroupByControl hierarchicalGroupByControl = GroupingHelper.GetHierarchicalGroupByControl( this );
+
+ // By default, we can since DataGridCollectionView.CanGroup always return true
+ // but we rely on the HierarchicalGroupByControl.AllowGroupModification value
+ bool allowGroupingModification = hierarchicalGroupByControl.AllowGroupingModification;
+ if( allowGroupingModification == true )
+ {
+ DataGridContext dataGridContext = this.DataGridContext;
+
+ Debug.Assert( dataGridContext != null );
+
+ if( ( dataGridContext != null ) && ( dataGridContext.SourceDetailConfiguration == null ) )
+ {
+ allowGroupingModification = dataGridContext.Items.CanGroup;
+ }
+ }
+
+ return allowGroupingModification;
+ }
+ }
+
+ internal void ShowFarDropMark( ColumnManagerCell cell, Point mousePosition )
+ {
+ Debug.Assert( cell != null );
+ if( cell == null )
+ return;
+
+ DataGridContext cellDataGridContext = DataGridControl.GetDataGridContext( cell );
+
+ Debug.Assert( cellDataGridContext != null );
+ if( cellDataGridContext == null )
+ throw new DataGridInternalException( "DataGridContext cannot be null on ColumnManagerCell." );
+
+ // We already have GroupLevelDescriptions for this level, we should show DropMark on the last HierarchicalGroupByItem
+ if( cellDataGridContext.GroupLevelDescriptions.Count > 0 )
+ {
+ Debug.Assert( cellDataGridContext.GroupLevelDescriptions == this.GroupLevelDescriptions );
+
+ if( cellDataGridContext.GroupLevelDescriptions != this.GroupLevelDescriptions )
+ return;
+
+ int lastIndex = this.GroupLevelDescriptions.Count - 1;
+
+ // If there
+ if( lastIndex > -1 )
+ {
+ HierarchicalGroupByItem hierarchicalGroupByItem = this.ItemContainerGenerator.ContainerFromItem( this.GroupLevelDescriptions[ lastIndex ] ) as HierarchicalGroupByItem;
+
+ Debug.Assert( hierarchicalGroupByItem != null );
+ if( hierarchicalGroupByItem == null )
+ return;
+
+ hierarchicalGroupByItem.ShowFarDropMark( mousePosition );
+ }
+ else
+ {
+ this.ShowFarDropMark( mousePosition );
+ }
+ }
+ else
+ {
+ this.ShowFarDropMark( mousePosition );
+ }
+ }
+
+ internal void ShowFarDropMark( Point mousePosition )
+ {
+ int itemsCount = this.Items.Count;
+ if( itemsCount < 1 )
+ {
+ if( m_dropMarkAdorner == null )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl grid = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ Pen pen = UIViewBase.GetDropMarkPen( this );
+
+ if( ( pen == null ) && ( grid != null ) )
+ {
+ UIViewBase uiViewBase = grid.GetView() as UIViewBase;
+ pen = uiViewBase.DefaultDropMarkPen;
+ }
+
+ DropMarkOrientation orientation = UIViewBase.GetDropMarkOrientation( this );
+
+ if( ( orientation == DropMarkOrientation.Default ) && ( grid != null ) )
+ {
+ UIViewBase uiViewBase = grid.GetView() as UIViewBase;
+
+ orientation = uiViewBase.DefaultDropMarkOrientation;
+ }
+
+ m_dropMarkAdorner = new DropMarkAdorner( this, pen, orientation );
+
+ AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer( this );
+
+ if( adornerLayer != null )
+ adornerLayer.Add( m_dropMarkAdorner );
+
+ }
+ // We Only want the drop mark to be displayed at the end of the HierarchicalGroupByControlNode
+ m_dropMarkAdorner.ForceAlignment( DropMarkAlignment.Far );
+ }
+ else
+ {
+ HierarchicalGroupByItem hierarchicalGroupByItem = this.ItemContainerGenerator.ContainerFromIndex( itemsCount - 1 ) as HierarchicalGroupByItem;
+
+ Debug.Assert( hierarchicalGroupByItem != null );
+
+ GroupLevelDescription groupLevelDescription = hierarchicalGroupByItem.Content as GroupLevelDescription;
+
+ Debug.Assert( groupLevelDescription != null );
+
+ // Show Far DropMark only if not already grouped
+ if( this.Items.Contains( groupLevelDescription ) == false )
+ hierarchicalGroupByItem.ShowFarDropMark( mousePosition );
+ }
+ }
+
+ internal void HideFarDropMark()
+ {
+ int itemsCount = this.Items.Count;
+ if( itemsCount > 0 )
+ {
+ HierarchicalGroupByItem hierarchicalGroupByItem = null;
+ if( itemsCount > 1 )
+ {
+ // Hide the before last item's DropMark if any in case
+ hierarchicalGroupByItem = this.ItemContainerGenerator.ContainerFromIndex( itemsCount - 2 ) as HierarchicalGroupByItem;
+
+ Debug.Assert( hierarchicalGroupByItem != null );
+
+ if( hierarchicalGroupByItem != null )
+ hierarchicalGroupByItem.HideDropMark();
+ }
+
+ // Hide last item's DropMark if any
+ hierarchicalGroupByItem = this.ItemContainerGenerator.ContainerFromIndex( itemsCount - 1 ) as HierarchicalGroupByItem;
+
+ Debug.Assert( hierarchicalGroupByItem != null );
+
+ if( hierarchicalGroupByItem != null )
+ hierarchicalGroupByItem.HideDropMark();
+ }
+
+ if( m_dropMarkAdorner != null )
+ {
+ AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer( this );
+
+ if( adornerLayer != null )
+ adornerLayer.Remove( m_dropMarkAdorner );
+
+ m_dropMarkAdorner = null;
+ }
+ }
+
+ internal void HideFarDropMark( ColumnManagerCell cell )
+ {
+ Debug.Assert( cell != null );
+ if( cell == null )
+ return;
+
+ DataGridContext cellDataGridContext = DataGridControl.GetDataGridContext( cell );
+
+ Debug.Assert( cellDataGridContext != null );
+ if( cellDataGridContext == null )
+ throw new DataGridInternalException( "DataGridContext cannot be null on ColumnManagerCell." );
+
+ this.HideFarDropMark();
+
+ // We already have GroupLevelDescriptions for this level, we should show DropMark on the last HierarchicalGroupByItem
+ if( cellDataGridContext.GroupLevelDescriptions.Count > 0 )
+ {
+ Debug.Assert( cellDataGridContext.GroupLevelDescriptions == this.GroupLevelDescriptions );
+
+ if( cellDataGridContext.GroupLevelDescriptions != this.GroupLevelDescriptions )
+ return;
+
+ int lastIndex = this.GroupLevelDescriptions.Count - 1;
+
+ if( lastIndex > -1 )
+ {
+ HierarchicalGroupByItem hierarchicalGroupByItem = this.ItemContainerGenerator.ContainerFromItem( this.GroupLevelDescriptions[ lastIndex ] ) as HierarchicalGroupByItem;
+
+ Debug.Assert( hierarchicalGroupByItem != null );
+ if( hierarchicalGroupByItem == null )
+ return;
+
+ hierarchicalGroupByItem.HideDropMark();
+ }
+ }
+ }
+
+ #endregion
+
+ #region IDropTarget Members
+
+ bool IDropTarget.CanDropElement( UIElement draggedElement )
+ {
+ ColumnManagerCell cell = null;
+ HierarchicalGroupByItem hierarchicalGroupByItem = null;
+ bool canDrop = this.AllowGroupingModification;
+
+ if( canDrop )
+ {
+ cell = draggedElement as ColumnManagerCell;
+
+ if( cell != null )
+ {
+ ColumnBase parentColumn = cell.ParentColumn;
+
+ if( ( parentColumn == null ) || ( !parentColumn.AllowGroup ) )
+ return false;
+
+ // Check if already grouped using the cell's DataGridContext
+ canDrop = !GroupingHelper.IsAlreadyGroupedBy( cell );
+
+ if( canDrop )
+ {
+
+ // Get the HierarchicalGroupByControl for this HierarchicalGroupByControlNode
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( this );
+
+ if( parentGBC == null )
+ throw new DataGridInternalException( "The hierarchical group-by control node must be rooted by a HierarchicalGroupByControl." );
+
+ DataGridContext parentGBCDataGridContext = DataGridControl.GetDataGridContext( parentGBC );
+
+ Debug.Assert( parentGBCDataGridContext != null );
+
+ if( parentGBCDataGridContext.Items != null )
+ canDrop = parentGBCDataGridContext.Items.CanGroup;
+
+ if( canDrop )
+ {
+ canDrop = GroupingHelper.IsColumnManagerCellInDataGridContext( parentGBCDataGridContext, cell );
+
+ if( canDrop == true )
+ {
+ canDrop = GroupingHelper.ValidateMaxGroupDescriptions( DataGridControl.GetDataGridContext( draggedElement ) );
+ }
+ }
+ }
+ }
+ else
+ {
+ hierarchicalGroupByItem = draggedElement as HierarchicalGroupByItem;
+
+ if( hierarchicalGroupByItem == null )
+ canDrop = false;
+
+ if( canDrop )
+ {
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( this );
+
+ if( parentGBC == null )
+ throw new DataGridInternalException( "The hierarchical group-by control node must be rooted by a HierarchicalGroupByControl." );
+
+ // Try to get the HierarchicalGroupByControlNode in which this HierarchicalGroupByItem can be added using the parent HierarchicalGroupByControl => null it can't
+ HierarchicalGroupByControlNode draggedHierarchicalGroupByControlNode = parentGBC.GetHierarchicalGroupByControlNodeFromHierarchicalGroupByItem( hierarchicalGroupByItem );
+
+ if( draggedHierarchicalGroupByControlNode == null )
+ canDrop = false;
+ }
+ }
+ }
+
+ bool returnedValue = ( ( cell != null ) || ( hierarchicalGroupByItem != null ) ) &&// ColumnManagerCell or HierarchicalGroupByItem
+ ( canDrop );
+
+
+ return returnedValue;
+ }
+
+ void IDropTarget.DragEnter( UIElement draggedElement )
+ {
+ }
+
+ void IDropTarget.DragOver( UIElement draggedElement, Point mousePosition )
+ {
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ if( cell != null )
+ {
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( this );
+
+ if( parentGBC == null )
+ throw new DataGridInternalException( "The hierarchical group-by control node must be rooted by a HierarchicalGroupByControl." );
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = parentGBC.GetHierarchicalGroupByControlNodeFromColumnManagerCell( cell );
+
+ if( hierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException( "A HierarchicalGroupByControlNode must exist for every level." );
+
+ hierarchicalGroupByControlNode.ShowFarDropMark( cell, mousePosition );
+ }
+ else
+ {
+ HierarchicalGroupByItem hierarchicalGroupByItem = draggedElement as HierarchicalGroupByItem;
+ if( hierarchicalGroupByItem == null )
+ return;
+
+ HierarchicalGroupByControlNode draggedHierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( hierarchicalGroupByItem );
+
+ if( draggedHierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException();
+
+ if( draggedHierarchicalGroupByControlNode.GroupLevelDescriptions == this.GroupLevelDescriptions )
+ {
+ this.ShowFarDropMark( mousePosition );
+ }
+ else
+ {
+ // This HierarchicalGroupByItem does not belong this parent HierarchicalGroupByControlNode, display the DropMark on the correct one
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( this );
+
+ if( parentGBC == null )
+ throw new DataGridInternalException( "The hierarchical group-by control node must be rooted by a HierarchicalGroupByControl." );
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = parentGBC.GetHierarchicalGroupByControlNodeFromHierarchicalGroupByItem( hierarchicalGroupByItem );
+
+ Debug.Assert( hierarchicalGroupByControlNode != null, "CanDrop should have returned false" );
+ if( hierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException( "A HierarchicalGroupByControlNode must exist for every level." );
+
+ hierarchicalGroupByControlNode.ShowFarDropMark( mousePosition );
+ }
+ }
+ }
+
+ void IDropTarget.DragLeave( UIElement draggedElement )
+ {
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ if( cell != null )
+ {
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( this );
+
+ if( parentGBC == null )
+ throw new DataGridInternalException( "The hierarchical group-by control node must be rooted by a HierarchicalGroupByControl." );
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = parentGBC.GetHierarchicalGroupByControlNodeFromColumnManagerCell( cell );
+
+ if( hierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException( "A HierarchicalGroupByControlNode must exist for every level." );
+
+ hierarchicalGroupByControlNode.HideFarDropMark( cell );
+ }
+ else
+ {
+ HierarchicalGroupByItem hierarchicalGroupByItem = draggedElement as HierarchicalGroupByItem;
+ if( hierarchicalGroupByItem == null )
+ return;
+
+ HierarchicalGroupByControlNode draggedHierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( hierarchicalGroupByItem );
+
+ if( draggedHierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException();
+
+ if( draggedHierarchicalGroupByControlNode.GroupLevelDescriptions == this.GroupLevelDescriptions )
+ {
+ this.HideFarDropMark();
+ }
+ else
+ {
+ // This HierarchicalGroupByItem does not belong this parent HierarchicalGroupByControlNode, display the DropMark on the correct one
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( this );
+
+ Debug.Assert( parentGBC != null );
+ if( parentGBC == null )
+ throw new DataGridInternalException( "The hierarchical group-by control node must be rooted by a HierarchicalGroupByControl" );
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = parentGBC.GetHierarchicalGroupByControlNodeFromHierarchicalGroupByItem( hierarchicalGroupByItem );
+
+ Debug.Assert( hierarchicalGroupByControlNode != null, "CanDrop should have returned false" );
+ if( hierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException( "A HierarchicalGroupByControlNode must exist for every level." );
+
+ hierarchicalGroupByControlNode.HideFarDropMark();
+ }
+ }
+ }
+
+ void IDropTarget.Drop( UIElement draggedElement )
+ {
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ if( cell == null )
+ return;
+
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( this );
+
+ if( parentGBC == null )
+ throw new DataGridInternalException( "The hierarchical group-by control node must be rooted by a HierarchicalGroupByControl." );
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl parentGrid = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ GroupingHelper.AppendNewGroupFromColumnManagerCell( cell, parentGrid );
+
+ // Notify groups have changed for NoGroupContent
+ parentGBC.UpdateHasGroups();
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = parentGBC.GetHierarchicalGroupByControlNodeFromColumnManagerCell( cell );
+
+ if( hierarchicalGroupByControlNode == null )
+ return;
+
+ hierarchicalGroupByControlNode.HideFarDropMark( cell );
+
+ this.HideFarDropMark();
+ }
+
+ #endregion
+
+ #region PRIVATE FIELDS
+
+ private DropMarkAdorner m_dropMarkAdorner;
+
+ #endregion
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HierarchicalGroupByItem.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HierarchicalGroupByItem.cs
new file mode 100644
index 00000000..0e77f1c1
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HierarchicalGroupByItem.cs
@@ -0,0 +1,944 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Xceed.Utils.Wpf.DragDrop;
+using System.Windows.Controls.Primitives;
+using System.ComponentModel;
+using System.Windows;
+using System.Windows.Input;
+using System.Collections.ObjectModel;
+using Xceed.Wpf.DataGrid.Views;
+using System.Windows.Resources;
+using System.Windows.Documents;
+using System.Windows.Data;
+using System.Diagnostics;
+using System.Security;
+using System.Windows.Media;
+using Xceed.Utils.Wpf;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class HierarchicalGroupByItem : ButtonBase, INotifyPropertyChanged, IDropTarget
+ {
+ #region CONSTRUCTORS
+
+ static HierarchicalGroupByItem()
+ {
+ // This DefaultStyleKey will only be used in design-time.
+ DefaultStyleKeyProperty.OverrideMetadata( typeof( HierarchicalGroupByItem ), new FrameworkPropertyMetadata( new Markup.ThemeKey( typeof( Views.TableView ), typeof( HierarchicalGroupByItem ) ) ) );
+
+ HierarchicalGroupByItem.IsBeingDraggedProperty = HierarchicalGroupByItem.IsBeingDraggedPropertyKey.DependencyProperty;
+
+ FocusableProperty.OverrideMetadata( typeof( HierarchicalGroupByItem ), new FrameworkPropertyMetadata( false ) );
+ }
+
+ #endregion
+
+ #region IsBeingDragged Read-Only Property
+
+ private static readonly DependencyPropertyKey IsBeingDraggedPropertyKey =
+ DependencyProperty.RegisterReadOnly( "IsBeingDragged", typeof( bool ), typeof( HierarchicalGroupByItem ), new PropertyMetadata( false ) );
+
+ public static readonly DependencyProperty IsBeingDraggedProperty;
+
+ public bool IsBeingDragged
+ {
+ get
+ {
+ return ( bool )this.GetValue( HierarchicalGroupByItem.IsBeingDraggedProperty );
+ }
+ }
+
+ private void SetIsBeingDragged( bool value )
+ {
+ this.SetValue( HierarchicalGroupByItem.IsBeingDraggedPropertyKey, value );
+ }
+
+ #endregion IsBeingDragged Read-Only Property
+
+ #region ParentColumns Property
+
+ internal ColumnCollection ParentColumns
+ {
+ get
+ {
+ ColumnCollection columnCollection = null;
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ Debug.Assert( ( hierarchicalGroupByControlNode != null ) || ( DesignerProperties.GetIsInDesignMode( this ) ) );
+
+ if( hierarchicalGroupByControlNode != null )
+ {
+ columnCollection = hierarchicalGroupByControlNode.Columns;
+ }
+
+ if( ( columnCollection == null ) && ( !DesignerProperties.GetIsInDesignMode( this ) ) )
+ throw new DataGridInternalException( "The " + typeof( HierarchicalGroupByItem ).Name + "'s ParentColumns cannot be null." );
+
+ return columnCollection;
+ }
+ }
+
+ #endregion
+
+ #region ParentGroupDescriptions Property
+
+ internal ObservableCollection ParentGroupDescriptions
+ {
+ get
+ {
+ ObservableCollection groupDescriptions = null;
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ Debug.Assert( hierarchicalGroupByControlNode != null );
+ if( hierarchicalGroupByControlNode != null )
+ {
+ groupDescriptions = hierarchicalGroupByControlNode.GroupDescriptions;
+ }
+
+ if( groupDescriptions == null )
+ throw new DataGridInternalException( "The " + typeof( HierarchicalGroupByItem ).Name + "'s ParentGroupDescriptions cannot be null." );
+
+ return groupDescriptions;
+ }
+ }
+
+ #endregion
+
+ #region ParentGroupLevelDescriptions Property
+
+ private GroupLevelDescriptionCollection ParentGroupLevelDescriptions
+ {
+ get
+ {
+ GroupLevelDescriptionCollection groupLevelDescriptions = null;
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ if( hierarchicalGroupByControlNode != null )
+ {
+ groupLevelDescriptions = hierarchicalGroupByControlNode.GroupLevelDescriptions;
+ }
+ else
+ {
+ DataGridContext dataGridContext = this.ParentDataGridContext;
+ groupLevelDescriptions = dataGridContext.GroupLevelDescriptions;
+ }
+
+ if( groupLevelDescriptions == null )
+ throw new DataGridInternalException( "The " + typeof( HierarchicalGroupByItem ).Name + "'s ParentGroupLevelDescriptions cannot be null." );
+
+ return groupLevelDescriptions;
+ }
+ }
+
+ #endregion
+
+ #region ParentSortDescriptions Property
+
+ private SortDescriptionCollection ParentSortDescriptions
+ {
+ get
+ {
+ SortDescriptionCollection sortDescriptions = null;
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ if( hierarchicalGroupByControlNode != null )
+ {
+ sortDescriptions = hierarchicalGroupByControlNode.SortDescriptions;
+ }
+ else
+ {
+ DataGridContext dataGridContext = this.ParentDataGridContext;
+ sortDescriptions = dataGridContext.Items.SortDescriptions;
+ }
+
+ if( sortDescriptions == null )
+ throw new DataGridInternalException( "The " + typeof( HierarchicalGroupByItem ).Name + "'s ParentSortDescriptions cannot be null." );
+
+ return sortDescriptions;
+ }
+ }
+
+ #endregion
+
+ #region SortDirection Property
+
+ // Only used to bind between Column and us, but we don't want to expose it publicly
+ private static readonly DependencyProperty SortDirectionInternalProperty =
+ DependencyProperty.Register( "SortDirectionInternal", typeof( SortDirection ), typeof( HierarchicalGroupByItem ), new PropertyMetadata( SortDirection.None, new PropertyChangedCallback( HierarchicalGroupByItem.OnSortDirectionInternalChanged ) ) );
+
+ public SortDirection SortDirection
+ {
+ get
+ {
+ return ( SortDirection )this.GetValue( HierarchicalGroupByItem.SortDirectionInternalProperty );
+ }
+ }
+
+ private static void OnSortDirectionInternalChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ HierarchicalGroupByItem groupByItem = ( HierarchicalGroupByItem )sender;
+ groupByItem.OnPropertyChanged( new PropertyChangedEventArgs( "SortDirection" ) );
+ }
+
+ #endregion SortDirection Property
+
+ #region Drag & Drop Manager METHODS
+
+ private void SetupDragManager()
+ {
+ // We do not support DragDrop when there are no AdornerLayer because there wouldn't
+ // be any visual feedback for the operation.
+ if( AdornerLayer.GetAdornerLayer( this ) == null )
+ return;
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl dataGridControl = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ // Can be null in design-time (edition of a style TargetType HierarchicalGroupByItem ).
+ if( dataGridControl == null )
+ return;
+
+ Debug.Assert( m_dragSourceManager == null, "There might be problems when there is already a m_dragSourceManager." );
+
+ if( m_dragSourceManager != null )
+ {
+ m_dragSourceManager.PropertyChanged -= new PropertyChangedEventHandler( DragSourceManager_PropertyChanged );
+ m_dragSourceManager.DragOutsideQueryCursor -= new QueryCursorEventHandler( DragSourceManager_DragOutsideQueryCursor );
+ m_dragSourceManager.DroppedOutside -= new EventHandler( DragSourceManager_DroppedOutside );
+ }
+
+ // The DataGridControl's AdornerDecoratorForDragAndDrop must be used for dragging in order to include the
+ // RenderTransform the DataGridControl may performs. This AdornerDecorator is defined in the ControlTemplate
+ // as PART_DragDropAdornerDecorator
+ if( ( dataGridControl.DragDropAdornerDecorator != null )
+ && ( dataGridControl.DragDropAdornerDecorator.AdornerLayer != null ) )
+ {
+ m_dragSourceManager = new DragSourceManager( this, dataGridControl.DragDropAdornerDecorator.AdornerLayer, dataGridControl );
+ }
+ else
+ {
+ System.Diagnostics.Debug.Assert( false, "The drag and drop functionnality won't be fully working properly: PART_DragDropAdornerDecorator was not found" );
+ m_dragSourceManager = new DragSourceManager( this, null, dataGridControl );
+ }
+
+ m_dragSourceManager.PropertyChanged += new PropertyChangedEventHandler( DragSourceManager_PropertyChanged );
+ m_dragSourceManager.DragOutsideQueryCursor += new QueryCursorEventHandler( DragSourceManager_DragOutsideQueryCursor );
+ m_dragSourceManager.DroppedOutside += new EventHandler( DragSourceManager_DroppedOutside );
+ }
+
+ private void DragSourceManager_DroppedOutside( object sender, EventArgs e )
+ {
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ if( hierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException();
+
+ bool allowGroupingModification = hierarchicalGroupByControlNode.IsGroupingModificationAllowed;
+
+ if( !allowGroupingModification )
+ return;
+
+ ObservableCollection groupDescriptions = this.ParentGroupDescriptions;
+
+ if( groupDescriptions != null )
+ {
+ // Get the HierarchicalGroupByControl before removing us from it
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( this );
+
+ GroupLevelDescription groupLevelDescription = this.Content as GroupLevelDescription;
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl parentDataGridControl = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ GroupingHelper.RemoveGroupDescription( groupDescriptions, groupLevelDescription, parentDataGridControl );
+
+ // Notify groups have changed for NoGroupContent
+ parentGBC.UpdateHasGroups();
+
+ // Update the HasGroups property
+ Debug.Assert( parentGBC != null );
+ if( parentGBC == null )
+ throw new DataGridInternalException( "The hierarchical group-by item must be rooted by a HierarchicalGroupByControl." );
+ }
+ }
+
+ private void DragSourceManager_PropertyChanged( object sender, PropertyChangedEventArgs e )
+ {
+ if( e.PropertyName == "IsDragging" )
+ {
+ this.SetIsBeingDragged( m_dragSourceManager.IsDragging );
+ }
+ }
+
+ private void DragSourceManager_DragOutsideQueryCursor( object sender, QueryCursorEventArgs e )
+ {
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ if( ( hierarchicalGroupByControlNode == null )
+ || ( !hierarchicalGroupByControlNode.IsGroupingModificationAllowed ) )
+ return;
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( ( dataGridContext != null ) && ( dataGridContext.DataGridControl != null ) )
+ {
+ UIViewBase uiViewBase = dataGridContext.DataGridControl.GetView() as UIViewBase;
+
+ e.Cursor = ( uiViewBase != null )
+ ? uiViewBase.RemovingGroupCursor
+ : UIViewBase.DefaultGroupDraggedOutsideCursor;
+ }
+ else
+ {
+ e.Cursor = UIViewBase.DefaultGroupDraggedOutsideCursor;
+ }
+ }
+
+ protected override void OnMouseLeftButtonDown( MouseButtonEventArgs e )
+ {
+ if( this.CaptureMouse() )
+ {
+ if( m_dragSourceManager != null )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext != null )
+ {
+ // Update the DropOutsideCursor since it is defined on the View
+ UIViewBase uiViewBase = dataGridContext.DataGridControl.GetView() as UIViewBase;
+
+ m_dragSourceManager.DropOutsideCursor = ( uiViewBase != null )
+ ? uiViewBase.RemovingGroupCursor
+ : UIViewBase.DefaultGroupDraggedOutsideCursor;
+ }
+
+ m_dragSourceManager.ProcessMouseLeftButtonDown( e );
+ }
+
+ e.Handled = true;
+ }
+
+ base.OnMouseLeftButtonDown( e );
+ }
+
+ protected override void OnMouseMove( MouseEventArgs e )
+ {
+ if( ( this.IsMouseCaptured ) && ( e.LeftButton == MouseButtonState.Pressed ) )
+ {
+ if( m_dragSourceManager != null )
+ m_dragSourceManager.ProcessMouseMove( e );
+ }
+
+ base.OnMouseMove( e );
+ }
+
+ protected override void OnMouseLeftButtonUp( MouseButtonEventArgs e )
+ {
+ bool isMouseCaptured = this.IsMouseCaptured;
+ bool isPressed = this.IsPressed;
+
+ if( m_dragSourceManager != null )
+ m_dragSourceManager.ProcessMouseLeftButtonUp( e );
+
+ if( isMouseCaptured )
+ {
+ bool click = isPressed;
+
+ if( click )
+ {
+ // DataGridCollectionView always return true for CanSort
+ bool allowSort = true;
+
+ // Use the ParentDataGridContext to be sure to get a
+ // DataGridContext of the correct detail level since
+ // all the HierarchicalGroupByItem will share the same DataGridContext
+ // which is the one of the level where the HierarchicalGroupByControl
+ // is located
+ DataGridContext dataGridContext;
+
+ try
+ {
+ dataGridContext = this.ParentDataGridContext;
+ }
+ catch( DataGridInternalException )
+ {
+ dataGridContext = null;
+ }
+
+ if( ( dataGridContext != null ) && ( dataGridContext.SourceDetailConfiguration == null ) )
+ {
+ allowSort = dataGridContext.Items.CanSort;
+ }
+
+ if( allowSort )
+ {
+ ColumnCollection columns = this.ParentColumns;
+ GroupLevelDescription groupInfo = this.Content as GroupLevelDescription;
+
+ Debug.Assert( ( columns != null ) && ( groupInfo != null ) );
+
+ if( ( columns != null ) && ( groupInfo != null ) )
+ {
+ ColumnBase column = columns[ groupInfo.FieldName ];
+
+ if( ( column != null ) && ( column.AllowSort ) )
+ {
+ SortingHelper.ToggleColumnSort(
+ dataGridContext, this.ParentSortDescriptions,
+ columns, column, ( ( Keyboard.Modifiers & ModifierKeys.Shift ) != ModifierKeys.Shift ) );
+
+ e.Handled = true;
+ }
+ }
+ }
+ }
+ }
+
+ base.OnMouseLeftButtonUp( e );
+ }
+
+ protected override void OnLostMouseCapture( MouseEventArgs e )
+ {
+ if( m_dragSourceManager != null )
+ m_dragSourceManager.ProcessLostMouseCapture( e );
+
+ base.OnLostMouseCapture( e );
+ }
+
+ internal void ShowFarDropMark( Point mousePosition )
+ {
+ this.ShowDropMark( mousePosition, DropMarkAlignment.Far, true );
+ }
+
+ private void ShowDropMark( Point mousePosition )
+ {
+ this.ShowDropMark( mousePosition, DropMarkAlignment.Far, false );
+ }
+
+ private void ShowDropMark( Point mousePosition, DropMarkAlignment defaultAlignment, bool forceDefaultAlignment )
+ {
+ if( m_dropMarkAdorner == null )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl grid = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ Pen pen = UIViewBase.GetDropMarkPen( this );
+
+ if( ( pen == null ) && ( grid != null ) )
+ {
+ UIViewBase uiViewBase = grid.GetView() as UIViewBase;
+ pen = uiViewBase.DefaultDropMarkPen;
+ }
+
+ DropMarkOrientation orientation = Xceed.Wpf.DataGrid.Views.UIViewBase.GetDropMarkOrientation( this );
+
+ if( ( orientation == DropMarkOrientation.Default ) && ( grid != null ) )
+ {
+ UIViewBase uiViewBase = grid.GetView() as UIViewBase;
+
+ orientation = uiViewBase.DefaultDropMarkOrientation;
+ }
+
+ m_dropMarkAdorner = new DropMarkAdorner( this, pen, orientation );
+
+ AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer( this );
+
+ if( adornerLayer != null )
+ adornerLayer.Add( m_dropMarkAdorner );
+ }
+
+ if( forceDefaultAlignment )
+ {
+ m_dropMarkAdorner.ForceAlignment( defaultAlignment );
+ }
+ else
+ {
+ m_dropMarkAdorner.UpdateAlignment( mousePosition );
+ }
+ }
+
+ internal void HideDropMark()
+ {
+ if( m_dropMarkAdorner != null )
+ {
+ AdornerLayer adornerLayer = AdornerLayer.GetAdornerLayer( this );
+
+ if( adornerLayer != null )
+ adornerLayer.Remove( m_dropMarkAdorner );
+
+ m_dropMarkAdorner = null;
+ }
+ }
+
+ #endregion
+
+ #region INotifyPropertyChanged Members
+
+ protected virtual void OnPropertyChanged( PropertyChangedEventArgs e )
+ {
+ if( this.PropertyChanged != null )
+ this.PropertyChanged( this, e );
+ }
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ #endregion
+
+ #region PROTECTED METHDOS
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+ this.InitSortDirection();
+ this.SetupDragManager();
+ }
+
+ #endregion
+
+ #region PROTECTED INTERNAL METHODS
+
+ protected internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( HierarchicalGroupByItem ) );
+ }
+
+ #endregion
+
+ #region PRIVATE METHODS
+
+ private void InitSortDirection()
+ {
+ ColumnCollection columns = this.ParentColumns;
+ GroupLevelDescription groupInfo = this.Content as GroupLevelDescription;
+
+ Debug.Assert( ( columns != null ) && ( groupInfo != null ) || ( DesignerProperties.GetIsInDesignMode( this ) ) );
+ if( ( columns != null ) && ( groupInfo != null ) )
+ {
+ ColumnBase column = columns[ groupInfo.FieldName ];
+
+ if( column != null )
+ {
+ Binding sortBinding = new Binding();
+ sortBinding.Path = new PropertyPath( ColumnBase.SortDirectionProperty );
+ sortBinding.Mode = BindingMode.OneWay;
+ sortBinding.NotifyOnSourceUpdated = true;
+ sortBinding.Source = column;
+ sortBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
+
+ BindingOperations.SetBinding( this, HierarchicalGroupByItem.SortDirectionInternalProperty, sortBinding );
+ }
+ }
+ }
+
+ #endregion
+
+ #region INTERNAL STATIC METHODS
+
+ internal static HierarchicalGroupByControlNode GetParentHierarchicalGroupByControlNode( UIElement element )
+ {
+ DependencyObject parent = TreeHelper.GetParent( element );
+
+ while( parent != null )
+ {
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = parent as HierarchicalGroupByControlNode;
+ if( hierarchicalGroupByControlNode != null )
+ break;
+
+ parent = TreeHelper.GetParent( parent );
+ }
+
+ return parent as HierarchicalGroupByControlNode;
+ }
+
+ #endregion
+
+ #region ParentDataGridContext Property
+
+ internal DataGridContext ParentDataGridContext
+ {
+ get
+ {
+ DataGridContext dataGridContext = null;
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ if( hierarchicalGroupByControlNode != null )
+ dataGridContext = hierarchicalGroupByControlNode.DataGridContext;
+
+ if( dataGridContext == null )
+ throw new DataGridInternalException( "The DataGridContext property must be set on a " + typeof( HierarchicalGroupByControlNode ) + "." );
+
+ return dataGridContext;
+ }
+ }
+
+ #endregion
+
+ #region ParentDetailConfiguration Property
+
+ internal DetailConfiguration ParentDetailConfiguration
+ {
+ get
+ {
+ DetailConfiguration detailConfiguration = null;
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ if( hierarchicalGroupByControlNode != null )
+ detailConfiguration = hierarchicalGroupByControlNode.DetailConfiguration;
+
+ // Can return null if the master level is reached: the DataContext
+ // will be a DataGridContext instead of a DetailConfiguration and
+ // the properties must be fetched via the DataGridContext instead
+ return detailConfiguration;
+ }
+ }
+
+ #endregion
+
+ #region IDropTarget Members
+
+ bool IDropTarget.CanDropElement( UIElement draggedElement )
+ {
+ bool canDrop = true;
+
+ // Check if this HierarchicalGroupByItem parent HierarchicalGroupByControlNode allows grouping modifications, default yes
+ HierarchicalGroupByControlNode parentHierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ if( parentHierarchicalGroupByControlNode != null )
+ canDrop = parentHierarchicalGroupByControlNode.IsGroupingModificationAllowed;
+
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ HierarchicalGroupByItem hierarchicalGroupByItem = null;
+
+ if( canDrop )
+ {
+ if( cell != null )
+ {
+ ColumnBase parentColumn = cell.ParentColumn;
+
+ if( ( parentColumn == null ) || ( !parentColumn.AllowGroup ) )
+ return false;
+
+ // Check if already grouped using the cell's DataGridContext
+ canDrop = !GroupingHelper.IsAlreadyGroupedBy( cell );
+
+ if( canDrop )
+ {
+ // Get the HierarchicalGroupByControl for this HierarchicalGroupByItem
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( parentHierarchicalGroupByControlNode );
+
+ if( parentGBC == null )
+ throw new DataGridInternalException( "The hierarchical group-by item must be rooted by a HierarchicalGroupByControl." );
+
+ DataGridContext parentGBCDataGridContext = DataGridControl.GetDataGridContext( parentGBC );
+
+ if( parentGBCDataGridContext.Items != null )
+ canDrop = parentGBCDataGridContext.Items.CanGroup;
+
+ if( canDrop )
+ {
+ canDrop = GroupingHelper.IsColumnManagerCellInDataGridContext( parentGBCDataGridContext, cell );
+
+ if( canDrop == true )
+ canDrop = GroupingHelper.ValidateMaxGroupDescriptions( DataGridControl.GetDataGridContext( draggedElement ) );
+ }
+ }
+ }
+ else
+ {
+ hierarchicalGroupByItem = draggedElement as HierarchicalGroupByItem;
+
+ if( hierarchicalGroupByItem == null )
+ canDrop = false;
+
+ if( canDrop )
+ {
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( parentHierarchicalGroupByControlNode );
+
+ if( parentGBC == null )
+ throw new DataGridInternalException( "The hierarchical group-by item must be rooted by a HierarchicalGroupByControl." );
+
+ // Try to get the HierarchicalGroupByControlNode in which this HierarchicalGroupByItem can be added using the parent HierarchicalGroupByControl => null it can't
+ HierarchicalGroupByControlNode draggedHierarchicalGroupByControlNode = parentGBC.GetHierarchicalGroupByControlNodeFromHierarchicalGroupByItem( hierarchicalGroupByItem );
+
+ if( draggedHierarchicalGroupByControlNode == null )
+ canDrop = false;
+ }
+ }
+ }
+
+
+ bool returnedValue = ( ( cell != null ) || ( hierarchicalGroupByItem != null ) ) && // ColumnManagerCell or HierarchicalGroupByItem
+ ( draggedElement != this ) &&
+ ( canDrop );
+
+
+ return returnedValue;
+ }
+
+ void IDropTarget.DragEnter( UIElement draggedElement )
+ {
+ }
+
+ void IDropTarget.DragOver( UIElement draggedElement, Point mousePosition )
+ {
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ if( cell != null )
+ {
+ DataGridContext draggedCellDataGridContext = DataGridControl.GetDataGridContext( cell );
+
+ HierarchicalGroupByControlNode draggedOverHierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ if( draggedCellDataGridContext == null )
+ throw new DataGridInternalException( "A ColumnManagerCell must have a DataGridContext." );
+
+ if( draggedOverHierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException();
+
+ if( draggedOverHierarchicalGroupByControlNode.GroupLevelDescriptions == draggedCellDataGridContext.GroupLevelDescriptions )
+ {
+ this.ShowDropMark( mousePosition );
+ }
+ else
+ {
+ // This ColumnManagerCell does not belong this parent HierarchicalGroupByControlNode, display the DropMark on the correct one
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( draggedOverHierarchicalGroupByControlNode );
+
+ if( parentGBC == null )
+ throw new DataGridInternalException( "The hierarchical group-by item must be rooted by a HierarchicalGroupByControl." );
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = parentGBC.GetHierarchicalGroupByControlNodeFromColumnManagerCell( cell );
+
+ Debug.Assert( hierarchicalGroupByControlNode != null, "CanDrop should have returned false" );
+ if( hierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException( "A HierarchicalGroupByControlNode must exist for every level." );
+
+ hierarchicalGroupByControlNode.ShowFarDropMark( cell, mousePosition );
+ }
+ }
+ else
+ {
+ HierarchicalGroupByItem hierarchicalGroupByItem = draggedElement as HierarchicalGroupByItem;
+
+ if( hierarchicalGroupByItem == null )
+ return;
+
+ HierarchicalGroupByControlNode draggedHierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( hierarchicalGroupByItem );
+
+ HierarchicalGroupByControlNode draggedOverHierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ if( draggedHierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException();
+
+ if( draggedOverHierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException();
+
+ if( draggedHierarchicalGroupByControlNode.GroupLevelDescriptions == draggedOverHierarchicalGroupByControlNode.GroupLevelDescriptions )
+ {
+ this.ShowDropMark( mousePosition );
+ }
+ else
+ {
+ // This HierarchicalGroupByItem does not belong this parent HierarchicalGroupByControlNode, display the DropMark on the correct one
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( draggedOverHierarchicalGroupByControlNode );
+
+ if( parentGBC == null )
+ throw new DataGridInternalException( "A hierarchical group-by item must be rooted by a HierarchicalGroupByControl." );
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = parentGBC.GetHierarchicalGroupByControlNodeFromHierarchicalGroupByItem( hierarchicalGroupByItem );
+
+ Debug.Assert( hierarchicalGroupByControlNode != null, "CanDrop should have returned false" );
+ if( hierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException( "A HierarchicalGroupByControlNode must exist for every level." );
+
+ hierarchicalGroupByControlNode.ShowFarDropMark( mousePosition );
+ }
+ }
+ }
+
+ void IDropTarget.DragLeave( UIElement draggedElement )
+ {
+ ColumnManagerCell cell = draggedElement as ColumnManagerCell;
+
+ if( cell != null )
+ {
+ DataGridContext draggedCellDataGridContext = DataGridControl.GetDataGridContext( cell );
+
+ HierarchicalGroupByControlNode draggedOverHierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ if( draggedOverHierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException( "We should never be dragged over and not contained inside a HierarchicalGroupByControlNode." );
+
+ if( draggedOverHierarchicalGroupByControlNode.GroupLevelDescriptions == draggedCellDataGridContext.GroupLevelDescriptions )
+ {
+ this.HideDropMark();
+ }
+ else
+ {
+ // This ColumnManagerCell does not belong this parent HierarchicalGroupByControlNode, display the DropMark on the correct one
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( draggedOverHierarchicalGroupByControlNode );
+
+ if( parentGBC == null )
+ throw new DataGridInternalException( "A hierarchical group-by item must be rooted by a HierarchicalGroupByControl." );
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = parentGBC.GetHierarchicalGroupByControlNodeFromColumnManagerCell( cell );
+
+ Debug.Assert( hierarchicalGroupByControlNode != null, "CanDrop should have returned false" );
+ if( hierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException( "A HierarchicalGroupByControlNode must exist for every level." );
+
+ hierarchicalGroupByControlNode.HideFarDropMark( cell );
+ }
+ }
+ else
+ {
+ HierarchicalGroupByItem hierarchicalGroupByItem = draggedElement as HierarchicalGroupByItem;
+
+ if( hierarchicalGroupByItem == null )
+ return;
+
+ HierarchicalGroupByControlNode draggedHierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( hierarchicalGroupByItem );
+
+ HierarchicalGroupByControlNode draggedOverHierarchicalGroupByControlNode = HierarchicalGroupByItem.GetParentHierarchicalGroupByControlNode( this );
+
+ if( draggedHierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException();
+
+ if( draggedOverHierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException();
+
+ if( draggedHierarchicalGroupByControlNode.GroupLevelDescriptions == draggedOverHierarchicalGroupByControlNode.GroupLevelDescriptions )
+ {
+ this.HideDropMark();
+ }
+ else
+ {
+ // This HierarchicalGroupByItem does not belong this parent HierarchicalGroupByControlNode, display the DropMark on the correct one
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( draggedOverHierarchicalGroupByControlNode );
+
+ Debug.Assert( parentGBC != null );
+ if( parentGBC == null )
+ throw new DataGridInternalException( "A hierarchical group-by item must be rooted by a HierarchicalGroupByControl." );
+
+ HierarchicalGroupByControlNode hierarchicalGroupByControlNode = parentGBC.GetHierarchicalGroupByControlNodeFromHierarchicalGroupByItem( hierarchicalGroupByItem );
+
+ Debug.Assert( hierarchicalGroupByControlNode != null, "CanDrop should have returned false" );
+ if( hierarchicalGroupByControlNode == null )
+ throw new DataGridInternalException( "A HierarchicalGroupByControlNode must exist for every level." );
+
+ hierarchicalGroupByControlNode.HideFarDropMark();
+ }
+ }
+ }
+
+ void IDropTarget.Drop( UIElement draggedElement )
+ {
+ ColumnManagerCell draggedColumnManagerCell = draggedElement as ColumnManagerCell;
+
+ if( m_dropMarkAdorner != null )
+ {
+ GroupLevelDescription draggedOverGroupLevelDescription = this.Content as GroupLevelDescription;
+
+ DropMarkAlignment alignment = m_dropMarkAdorner.Alignment;
+ this.HideDropMark();
+
+ if( draggedColumnManagerCell != null )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl parentGrid = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ GroupingHelper.AddNewGroupFromColumnManagerCell( draggedColumnManagerCell, draggedOverGroupLevelDescription, alignment, parentGrid );
+ }
+ else
+ {
+ HierarchicalGroupByItem draggedGroupByItem = draggedElement as HierarchicalGroupByItem;
+
+ if( draggedGroupByItem == null )
+ return;
+
+ GroupLevelDescription draggedGroupLevelDescription = draggedGroupByItem.Content as GroupLevelDescription;
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl parentDataGridControl = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ GroupLevelDescription destinationGroupLevelDescription = this.Content as GroupLevelDescription;
+
+ GroupingHelper.MoveGroupDescription( this.ParentColumns, this.ParentGroupDescriptions,
+ destinationGroupLevelDescription, alignment,
+ draggedGroupLevelDescription, parentDataGridControl );
+ }
+ }
+ else
+ {
+ // We try to add a new Group which is not in the current GroupLevelDescriptions
+ if( draggedColumnManagerCell == null )
+ return;
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl parentGrid = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ GroupingHelper.AppendNewGroupFromColumnManagerCell( draggedColumnManagerCell, parentGrid );
+ }
+
+ HierarchicalGroupByControl parentGBC = GroupingHelper.GetHierarchicalGroupByControl( this );
+
+ Debug.Assert( parentGBC != null );
+ if( parentGBC == null )
+ throw new DataGridInternalException( "A hierarchical group-by item must be rooted by a HierarchicalGroupByControl." );
+
+ // Notify groups have changed for NoGroupContent
+ parentGBC.UpdateHasGroups();
+ }
+
+ #endregion
+
+ #region PRIVATE FIELDS
+
+ // Will remain null when no AdornerLayer is found.
+ private DragSourceManager m_dragSourceManager;
+ private DropMarkAdorner m_dropMarkAdorner;
+
+ #endregion
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HierarchicalGroupLevelIndicatorPane.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HierarchicalGroupLevelIndicatorPane.cs
new file mode 100644
index 00000000..675c4e7e
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/HierarchicalGroupLevelIndicatorPane.cs
@@ -0,0 +1,214 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class HierarchicalGroupLevelIndicatorPane : Control, IWeakEventListener
+ {
+ static HierarchicalGroupLevelIndicatorPane()
+ {
+ // This DefaultStyleKey will only be used in design-time.
+ DefaultStyleKeyProperty.OverrideMetadata( typeof( HierarchicalGroupLevelIndicatorPane ), new FrameworkPropertyMetadata( new Markup.ThemeKey( typeof( Views.TableView ), typeof( HierarchicalGroupLevelIndicatorPane ) ) ) );
+
+ FocusableProperty.OverrideMetadata( typeof( HierarchicalGroupLevelIndicatorPane ), new FrameworkPropertyMetadata( false ) );
+
+ DataGridControl.ParentDataGridControlPropertyKey.OverrideMetadata( typeof( HierarchicalGroupLevelIndicatorPane ), new FrameworkPropertyMetadata( new PropertyChangedCallback( OnParentDataGridControlChanged ) ) );
+ }
+
+ #region GroupLevelIndicatorPaneHost Read-Only Property
+
+ private Panel GroupLevelIndicatorPaneHost
+ {
+ get
+ {
+ //if there is no local storage for the host panel, try to retrieve and store the value
+ if( m_storedGroupLevelIndicatorPaneHost == null )
+ {
+ m_storedGroupLevelIndicatorPaneHost = this.RetrieveGroupLevelIndicatorPaneHostPanel();
+ }
+
+ return m_storedGroupLevelIndicatorPaneHost;
+ }
+ }
+
+ private Panel m_storedGroupLevelIndicatorPaneHost = null;
+
+ #endregion GroupLevelIndicatorPaneHost Read-Only Property
+
+ #region GroupLevelIndicatorPaneNeedsRefresh Private Property
+
+ private bool GroupLevelIndicatorPaneNeedsRefresh
+ {
+ get
+ {
+ Panel panel = this.GroupLevelIndicatorPaneHost;
+ if( panel == null )
+ return false;
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+ if( dataGridContext == null )
+ return false;
+
+ //skip the "current" DataGridContext
+ dataGridContext = dataGridContext.ParentDataGridContext;
+
+ int expectedIndicatorsCount = 0;
+ for( ; dataGridContext != null; dataGridContext = dataGridContext.ParentDataGridContext )
+ {
+ //a group indicator and a detail indicator should exist per level
+ expectedIndicatorsCount += 2;
+ }
+
+ return ( panel.Children.Count != expectedIndicatorsCount );
+ }
+ }
+
+ #endregion
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ //whenever the template gets "applied" I want to invalidate the stored Panel.
+ m_storedGroupLevelIndicatorPaneHost = null;
+ }
+
+ internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( HierarchicalGroupLevelIndicatorPane ) );
+ }
+
+ protected override Size MeasureOverride( Size availableSize )
+ {
+ Panel panel = this.GroupLevelIndicatorPaneHost;
+
+ if( panel != null )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext != null )
+ {
+ if( this.GroupLevelIndicatorPaneNeedsRefresh )
+ {
+ //clear all the panel's children!
+ panel.Children.Clear();
+
+ DataGridContext previousContext = dataGridContext;
+ DataGridContext runningDataGridContext = dataGridContext.ParentDataGridContext;
+
+ while( runningDataGridContext != null )
+ {
+ //create a GroupLevelIndicator to create indentation between the GLIPs
+ FrameworkElement newGroupMargin = null;
+ newGroupMargin = new DetailIndicator();
+ newGroupMargin.DataContext = dataGridContext;
+
+ object bindingSource = dataGridContext.GetDefaultDetailConfigurationForContext();
+ if( bindingSource == null )
+ bindingSource = dataGridContext.SourceDetailConfiguration;
+
+ //Bind the GroupLevelIndicator`s style to the running DataGridContext`s GroupLevelIndicatorStyle.
+ Binding groupLevelIndicatorStyleBinding = new Binding();
+ groupLevelIndicatorStyleBinding.Path = new PropertyPath( DetailConfiguration.DetailIndicatorStyleProperty );
+ groupLevelIndicatorStyleBinding.Source = bindingSource;
+ newGroupMargin.SetBinding( StyleProperty, groupLevelIndicatorStyleBinding );
+
+ //insert the Spacer GroupLevelIndicator in the panel
+ panel.Children.Insert( 0, newGroupMargin );
+
+ //then create the GLIP for the running DataGridContext
+ GroupLevelIndicatorPane newSubGLIP = new GroupLevelIndicatorPane();
+ DataGridControl.SetDataGridContext( newSubGLIP, runningDataGridContext );
+ newSubGLIP.SetIsLeaf( false );
+ GroupLevelIndicatorPane.SetGroupLevel( newSubGLIP, -1 );
+
+ //and insert it in the panel.
+ panel.Children.Insert( 0, newSubGLIP );
+
+ previousContext = runningDataGridContext;
+ runningDataGridContext = runningDataGridContext.ParentDataGridContext;
+ } //end of the loop to cycle through the parent contexts.
+ } // end if GroupLevelIndicatorPaneNeedsRefresh
+ } // end if dataGridContext != null
+ } //end if panel is not null
+
+ return base.MeasureOverride( availableSize );
+ }
+
+ private static void OnParentDataGridControlChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ HierarchicalGroupLevelIndicatorPane self = sender as HierarchicalGroupLevelIndicatorPane;
+ if( self != null )
+ {
+ DataGridControl grid = e.OldValue as DataGridControl;
+
+ //unsubsribe from the old DataGridControl (the GLIP was disconnected)
+ if( grid != null )
+ {
+ DetailsChangedEventManager.RemoveListener( grid, self );
+ }
+
+ grid = e.NewValue as DataGridControl;
+
+ //register to the parent grid control's Items Collection GroupDescriptions Changed event
+ if( grid != null )
+ {
+ self.PrepareDefaultStyleKey( grid.GetView() );
+
+ DetailsChangedEventManager.AddListener( grid, self );
+ self.InvalidateMeasure();
+ }
+ }
+ }
+
+ private Panel RetrieveGroupLevelIndicatorPaneHostPanel()
+ {
+ //get the template part
+ return this.GetTemplateChild( "PART_GroupLevelIndicatorHost" ) as Panel;
+ }
+
+ #region IWeakEventListener Members
+
+ bool IWeakEventListener.ReceiveWeakEvent( Type managerType, object sender, EventArgs e )
+ {
+ return this.OnReceiveWeakEvent( managerType, sender, e );
+ }
+
+ protected virtual bool OnReceiveWeakEvent( Type managerType, object sender, EventArgs e )
+ {
+ if( managerType == typeof( DetailsChangedEventManager ) )
+ {
+ this.InvalidateMeasure();
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+ }
+}
+
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ICustomVirtualizingPanel.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ICustomVirtualizingPanel.cs
new file mode 100644
index 00000000..bb328fe2
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ICustomVirtualizingPanel.cs
@@ -0,0 +1,30 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public interface ICustomVirtualizingPanel
+ {
+ void BringIntoView( int index );
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/IDataGridContextVisitable.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/IDataGridContextVisitable.cs
new file mode 100644
index 00000000..aeaae6f6
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/IDataGridContextVisitable.cs
@@ -0,0 +1,33 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal interface IDataGridContextVisitable
+ {
+ void AcceptVisitor( IDataGridContextVisitor visitor, out bool visitWasStopped );
+ void AcceptVisitor( int minIndex, int maxIndex, IDataGridContextVisitor visitor, out bool visitWasStopped );
+ void AcceptVisitor( int minIndex, int maxIndex, IDataGridContextVisitor visitor, DataGridContextVisitorType visitorType, out bool visitWasStopped );
+ void AcceptVisitor( int minIndex, int maxIndex, IDataGridContextVisitor visitor, DataGridContextVisitorType visitorType, bool visitDetails, out bool visitWasStopped );
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/IDataGridContextVisitor.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/IDataGridContextVisitor.cs
new file mode 100644
index 00000000..1083a3fe
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/IDataGridContextVisitor.cs
@@ -0,0 +1,37 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Data;
+using System.Windows;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal interface IDataGridContextVisitor
+ {
+ void Visit( DataGridContext sourceContext, ref bool stopVisit );
+ void Visit( DataGridContext sourceContext, int sourceDataItemIndex, object item, ref bool stopVisit );
+ void Visit( DataGridContext sourceContext, int startSourceDataItemIndex, int endSourceDataItemIndex, ref bool stopVisit );
+ void Visit( DataGridContext sourceContext, CollectionViewGroup group, object[] namesTree, int groupLevel, bool isExpanded, bool isComputedExpanded, ref bool stopVisit );
+ void Visit( DataGridContext sourceContext, DataTemplate headerFooter, ref bool stopVisit );
+ void Visit( DataGridContext sourceContext, GroupHeaderFooterItem groupHeaderFooter, ref bool stopVisit );
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/IDataGridItemContainer.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/IDataGridItemContainer.cs
new file mode 100644
index 00000000..e2eae20b
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/IDataGridItemContainer.cs
@@ -0,0 +1,29 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal interface IDataGridItemContainer
+ {
+ void PrepareContainer( DataGridContext dataGridContext, object item );
+ void ClearContainer();
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/IDeferableScrollInfoRefresh.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/IDeferableScrollInfoRefresh.cs
new file mode 100644
index 00000000..f5884e51
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/IDeferableScrollInfoRefresh.cs
@@ -0,0 +1,31 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Controls;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal interface IDeferableScrollInfoRefresh
+ {
+ IDisposable DeferScrollInfoRefresh( Orientation orientation );
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/InnerCellContentPresenter.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/InnerCellContentPresenter.cs
new file mode 100644
index 00000000..03762567
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/InnerCellContentPresenter.cs
@@ -0,0 +1,65 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Diagnostics;
+using System.Windows.Threading;
+using System.Windows.Data;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class InnerCellContentPresenter : ContentPresenter
+ {
+ static InnerCellContentPresenter()
+ {
+ Binding trimmingBinding = new Binding();
+ trimmingBinding.Path = new PropertyPath( "(0).(1).(2)",
+ Cell.ParentCellProperty,
+ Cell.ParentColumnProperty,
+ ColumnBase.TextTrimmingProperty );
+ trimmingBinding.Mode = BindingMode.OneWay;
+ trimmingBinding.RelativeSource = new RelativeSource( RelativeSourceMode.Self );
+
+ Binding wrappingBinding = new Binding();
+ wrappingBinding.Path = new PropertyPath( "(0).(1).(2)",
+ Cell.ParentCellProperty,
+ Cell.ParentColumnProperty,
+ ColumnBase.TextWrappingProperty );
+ wrappingBinding.Mode = BindingMode.OneWay;
+ wrappingBinding.RelativeSource = new RelativeSource( RelativeSourceMode.Self );
+
+ m_sTextBlockStyle = new Style( typeof( TextBlock ) );
+ m_sTextBlockStyle.Setters.Add( new Setter( TextBlock.TextTrimmingProperty, trimmingBinding ) );
+ m_sTextBlockStyle.Setters.Add( new Setter( TextBlock.TextWrappingProperty, wrappingBinding ) );
+ }
+
+ public InnerCellContentPresenter()
+ {
+ //ContentPresenter.ContentProperty.OverrideMetadata( typeof( InnerCellContentPresenter ), new PropertyMetadata( defaultContent ) );
+ this.Resources.Add( typeof( TextBlock ), m_sTextBlockStyle );
+ }
+
+ private static Style m_sTextBlockStyle;
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ItemPropertiesChangedEventManager.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ItemPropertiesChangedEventManager.cs
new file mode 100644
index 00000000..e6d65391
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ItemPropertiesChangedEventManager.cs
@@ -0,0 +1,79 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows;
+using System.Collections.Specialized;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class ItemPropertiesChangedEventManager : WeakEventManager
+ {
+ private ItemPropertiesChangedEventManager()
+ {
+ }
+
+ public static void AddListener( DataGridItemPropertyCollection source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedAddListener( source, listener );
+ }
+
+ public static void RemoveListener( DataGridItemPropertyCollection source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedRemoveListener( source, listener );
+ }
+
+ protected override void StartListening( object source )
+ {
+ DataGridItemPropertyCollection itemProperties = source as DataGridItemPropertyCollection;
+ itemProperties.CollectionChanged += new NotifyCollectionChangedEventHandler( ItemProperties_CollectionChanged );
+ }
+
+ protected override void StopListening( object source )
+ {
+ DataGridItemPropertyCollection itemProperties = source as DataGridItemPropertyCollection;
+ itemProperties.CollectionChanged -= new NotifyCollectionChangedEventHandler( ItemProperties_CollectionChanged );
+ }
+
+ private static ItemPropertiesChangedEventManager CurrentManager
+ {
+ get
+ {
+ Type managerType = typeof( ItemPropertiesChangedEventManager );
+ ItemPropertiesChangedEventManager currentManager = ( ItemPropertiesChangedEventManager )WeakEventManager.GetCurrentManager( managerType );
+
+ if( currentManager == null )
+ {
+ currentManager = new ItemPropertiesChangedEventManager();
+ WeakEventManager.SetCurrentManager( managerType, currentManager );
+ }
+
+ return currentManager;
+ }
+ }
+
+ private void ItemProperties_CollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
+ {
+ this.DeliverEvent( sender, e );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ItemsHostUIElementCollection.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ItemsHostUIElementCollection.cs
new file mode 100644
index 00000000..8bee67f2
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ItemsHostUIElementCollection.cs
@@ -0,0 +1,238 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Windows;
+using System.Windows.Media;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal sealed class ItemsHostUIElementCollection : IList
+ {
+ #region Constructors
+
+ public ItemsHostUIElementCollection( UIElement visualParent )
+ {
+ if( visualParent == null )
+ throw new ArgumentNullException( "visualParent" );
+
+ m_visualCollection = new VisualCollection( visualParent );
+ m_visualCollectionLookupDictionary = new Dictionary();
+ m_visualParent = visualParent;
+ }
+
+ #endregion
+
+ #region IList Members
+
+ public int IndexOf( UIElement element )
+ {
+ if( !m_visualCollectionLookupDictionary.ContainsKey( element ) )
+ return -1;
+
+ return m_visualCollection.IndexOf( element );
+ }
+
+ public void Insert( int index, UIElement element )
+ {
+ if( element == null )
+ throw new ArgumentNullException( "element" );
+
+ m_visualCollection.Insert( index, element );
+ m_visualCollectionLookupDictionary.Add( element, null );
+ m_visualParent.InvalidateMeasure();
+ }
+
+ public void RemoveAt( int index )
+ {
+ UIElement element = m_visualCollection[ index ] as UIElement;
+
+ if( element != null )
+ {
+ m_visualCollectionLookupDictionary.Remove( element );
+ }
+
+ m_visualCollection.RemoveAt( index );
+ m_visualParent.InvalidateMeasure();
+ }
+
+ public UIElement this[ int index ]
+ {
+ get
+ {
+ return ( m_visualCollection[ index ] as UIElement );
+ }
+ set
+ {
+ if( value == null )
+ throw new ArgumentNullException( "value" );
+
+ // Ensure to contain the value in the lookup dictionary
+ if( !m_visualCollectionLookupDictionary.ContainsKey( value ) )
+ {
+ m_visualCollectionLookupDictionary.Add( value, null );
+ }
+
+ if( m_visualCollection[ index ] == value )
+ return;
+
+ m_visualCollection[ index ] = value;
+
+ m_visualParent.InvalidateMeasure();
+ }
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ public void Add( UIElement element )
+ {
+ if( element == null )
+ throw new ArgumentNullException( "element" );
+
+ m_visualCollection.Add( element );
+ m_visualCollectionLookupDictionary.Add( element, null );
+ m_visualParent.InvalidateMeasure();
+ }
+
+ public void Clear()
+ {
+ if( m_visualCollection.Count == 0 )
+ return;
+
+ m_visualCollection.Clear();
+ m_visualCollectionLookupDictionary.Clear();
+ m_visualParent.InvalidateMeasure();
+ }
+
+ public bool Contains( UIElement element )
+ {
+ return m_visualCollectionLookupDictionary.ContainsKey( element );
+ }
+
+ public void CopyTo( UIElement[] array, int index )
+ {
+ m_visualCollection.CopyTo( array, index );
+ }
+
+ public int Count
+ {
+ get
+ {
+ return m_visualCollection.Count;
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public bool Remove( UIElement element )
+ {
+ m_visualCollection.Remove( element );
+ m_visualCollectionLookupDictionary.Remove( element );
+ m_visualParent.InvalidateMeasure();
+
+ return true;
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ public IEnumerator GetEnumerator()
+ {
+ return new Enumerator( m_visualCollection.GetEnumerator() );
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return m_visualCollection.GetEnumerator();
+ }
+
+ #endregion
+
+ #region Enumerator Private Class
+
+ private class Enumerator : IEnumerator
+ {
+ public Enumerator( IEnumerator nonGenericEnumerator )
+ {
+ if( nonGenericEnumerator == null )
+ throw new ArgumentNullException( "nonGenericEnumerator" );
+
+ m_enumerator = nonGenericEnumerator;
+ }
+
+ UIElement IEnumerator.Current
+ {
+ get
+ {
+ return m_enumerator.Current as UIElement;
+ }
+ }
+
+ void IDisposable.Dispose()
+ {
+ // Nothing to do
+ }
+
+ object IEnumerator.Current
+ {
+ get
+ {
+ return m_enumerator.Current;
+ }
+ }
+
+ bool IEnumerator.MoveNext()
+ {
+ return m_enumerator.MoveNext();
+ }
+
+ void IEnumerator.Reset()
+ {
+ m_enumerator.Reset();
+ }
+
+ IEnumerator m_enumerator;
+ }
+
+ #endregion
+
+ #region Private Fields
+
+ private readonly Dictionary m_visualCollectionLookupDictionary;
+ private readonly VisualCollection m_visualCollection;
+ private readonly UIElement m_visualParent;
+
+ #endregion
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ItemsSourceChangedEventManager.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ItemsSourceChangedEventManager.cs
new file mode 100644
index 00000000..32920a29
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ItemsSourceChangedEventManager.cs
@@ -0,0 +1,75 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Windows;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class ItemsSourceChangeCompletedEventManager : WeakEventManager
+ {
+ private ItemsSourceChangeCompletedEventManager()
+ {
+ }
+
+ public static void AddListener( DataGridControl source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedAddListener( source, listener );
+ }
+
+ public static void RemoveListener( DataGridControl source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedRemoveListener( source, listener );
+ }
+
+ protected override void StartListening( object source )
+ {
+ DataGridControl dataGridControl = ( DataGridControl )source;
+ dataGridControl.ItemsSourceChangeCompleted += new EventHandler( this.OnItemsSourceChanged );
+ }
+
+ protected override void StopListening( object source )
+ {
+ DataGridControl dataGridControl = ( DataGridControl )source;
+ dataGridControl.ItemsSourceChangeCompleted -= new EventHandler( this.OnItemsSourceChanged );
+ }
+
+ private static ItemsSourceChangeCompletedEventManager CurrentManager
+ {
+ get
+ {
+ Type managerType = typeof( ItemsSourceChangeCompletedEventManager );
+ ItemsSourceChangeCompletedEventManager currentManager = ( ItemsSourceChangeCompletedEventManager )WeakEventManager.GetCurrentManager( managerType );
+
+ if( currentManager == null )
+ {
+ currentManager = new ItemsSourceChangeCompletedEventManager();
+ WeakEventManager.SetCurrentManager( managerType, currentManager );
+ }
+
+ return currentManager;
+ }
+ }
+
+ private void OnItemsSourceChanged( object sender, EventArgs args )
+ {
+ this.DeliverEvent( sender, args );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ItemsSourceHelper.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ItemsSourceHelper.cs
new file mode 100644
index 00000000..2300d036
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ItemsSourceHelper.cs
@@ -0,0 +1,1854 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Xml;
+using System.Xml.XPath;
+using System.Globalization;
+using System.ComponentModel;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+
+using Xceed.Utils;
+using Xceed.Wpf.DataGrid.Converters;
+using System.Collections.ObjectModel;
+using System.Diagnostics;
+using System.Data;
+using System.Collections.Specialized;
+using System.Reflection;
+using Xceed.Wpf.DataGrid.ValidationRules;
+using System.Data.Objects.DataClasses;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class ItemsSourceHelper
+ {
+ #region PUBLIC STATIC METHODS
+
+ public static bool IsSourceSupportingChangeNotification( object obj )
+ {
+ if( obj == null )
+ throw new ArgumentNullException( "obj" );
+
+ if( obj is INotifyCollectionChanged )
+ return true;
+
+ IBindingList bindingList = obj as IBindingList;
+
+ return ( ( bindingList != null )
+ && ( bindingList.SupportsChangeNotification ) );
+ }
+
+ public static bool IsSourceSupportingDBNull( IEnumerable toCheck )
+ {
+ if( toCheck is DataGridCollectionView )
+ {
+ // false because all the DBNull conversion should have been done in the DataGridCollectionView.ItemProperties converter.
+ return false;
+ }
+
+ if( toCheck is DataView )
+ return true;
+
+ CollectionView cv = toCheck as CollectionView;
+
+ if( cv != null )
+ return ItemsSourceHelper.IsSourceSupportingDBNull( cv.SourceCollection );
+
+ return false;
+ }
+
+ public static bool IsItemSupportingDBNull( object toCheck )
+ {
+ return ( toCheck is System.Data.DataRow ) || ( toCheck is System.Data.DataRowView );
+ }
+
+ public static bool IsDataView( IEnumerable toCheck )
+ {
+ if( toCheck is DataGridCollectionView )
+ return false;
+
+ if( toCheck is DataView )
+ return true;
+
+ CollectionView cv = toCheck as CollectionView;
+
+ if( ( cv != null ) && ( cv.SourceCollection is DataView ) )
+ return true;
+
+ return false;
+ }
+
+ [Obsolete( "You should use 'DataGridContext.ItemsSourceCollection as DataGridCollectionViewBase'" )]
+ public static DataGridCollectionViewBase TryGetDataGridCollectionViewBase( IEnumerable itemsSource )
+ {
+ // This method is intended to return null if not using a DataGridCollectionViewBase.
+ DataGridCollectionViewBase dataGridCollectionViewBase = itemsSource as DataGridCollectionViewBase;
+
+ // This is to ensure that this is not the master Generator ( and this CollectionView is the ItemsCollection )
+ if( dataGridCollectionViewBase == null )
+ {
+ CollectionView collectionView = itemsSource as CollectionView;
+
+ dataGridCollectionViewBase = ( collectionView == null ) ?
+ null : collectionView.SourceCollection as DataGridCollectionViewBase;
+ }
+
+ return dataGridCollectionViewBase;
+ }
+
+ public static IList TryGetIList( IEnumerable itemsSourceCollection )
+ {
+
+ CollectionView collectionView = itemsSourceCollection as ItemCollection;
+
+ if( collectionView != null )
+ {
+ IList list = collectionView.SourceCollection as IList;
+
+ if( list != null )
+ return list;
+ }
+
+ return itemsSourceCollection as IList;
+ }
+
+ public static bool IsValueType( Type type )
+ {
+ bool isValueType = type.IsValueType;
+
+ return ( ( ( isValueType ) && ( type.IsPrimitive ) ) ||
+ ( ( isValueType ) &&
+ ( ( type == typeof( decimal ) ) ||
+ ( type == typeof( DateTime ) ) ||
+ ( type == typeof( TimeSpan ) ) ||
+ ( type == typeof( Guid ) ) ) ) ||
+ ( type == typeof( string ) ) );
+ }
+
+ public static bool IsEntityObjectLoadable( EntityObject entityObject )
+ {
+ return
+ ( ( entityObject.EntityState & EntityState.Added ) != EntityState.Added ) &&
+ ( ( entityObject.EntityState & EntityState.Detached ) != EntityState.Detached );
+ }
+
+ public static bool IsEntityFramework( Type type )
+ {
+ // The EntityFramework assembly is not loaded. We are running on the client framework.
+ if( EntityObjectType == null )
+ return false;
+
+ return EntityObjectType.IsAssignableFrom( type );
+ }
+
+ public static bool IsEntityFramework( object o )
+ {
+ if( o == null )
+ return false;
+
+ return ItemsSourceHelper.IsEntityFramework( o.GetType() );
+ }
+
+ public static object GetFirstItemByEnumerable( IEnumerable enumerable )
+ {
+ if( enumerable == null )
+ return null;
+
+ object current = null;
+ IList list = enumerable as IList;
+
+ if( list != null )
+ {
+ return ( ( list.Count > 0 ) ? list[ 0 ] : null );
+ }
+
+ try
+ {
+ IEnumerator enumerator = enumerable.GetEnumerator();
+
+
+ if( enumerator.MoveNext() )
+ current = enumerator.Current;
+
+ }
+ catch( NotSupportedException )
+ {
+ current = null;
+ }
+
+ return current;
+ }
+
+ public static object AddNewDataItem(
+ IEnumerable itemsSourceCollection,
+ DataGridControl dataGridControl,
+ out int itemIndex )
+ {
+ DataGridCollectionViewBase dataGridCollectionViewBase = itemsSourceCollection as DataGridCollectionViewBase;
+
+ if( dataGridCollectionViewBase != null )
+ {
+ if( !dataGridCollectionViewBase.CanAddNew )
+ throw new InvalidOperationException( "An attempt was made to add a new data item to a source that does not support insertion." );
+
+ itemIndex = dataGridCollectionViewBase.Count;
+ return dataGridCollectionViewBase.AddNew();
+ }
+
+ if( ( dataGridControl != null ) && ( dataGridControl.ItemsSource == null ) )
+ {
+ //unbound
+#pragma warning disable 618
+ AddingNewDataItemEventArgs eventArgs = new AddingNewDataItemEventArgs();
+ dataGridControl.OnAddingNewDataItem( eventArgs );
+ object newItem = eventArgs.DataItem;
+#pragma warning restore 618
+
+ if( newItem == null )
+ throw new InvalidOperationException( "The AddingNewDataItem event did not return a new data item because the grid is not bound to a data source." );
+
+ itemIndex = dataGridControl.Items.Add( newItem );
+ return newItem;
+ }
+
+ DataView dataView = itemsSourceCollection as DataView;
+
+ if( dataView == null )
+ {
+ CollectionView collectionView = itemsSourceCollection as CollectionView;
+
+ dataView = ( collectionView == null ) ?
+ null : collectionView.SourceCollection as DataView;
+ }
+
+ if( dataView != null )
+ {
+ itemIndex = dataView.Count;
+ return dataView.AddNew();
+ }
+
+ IBindingList bindingList = itemsSourceCollection as IBindingList;
+
+ if( bindingList == null )
+ {
+ CollectionView collectionView = itemsSourceCollection as CollectionView;
+
+ bindingList = ( collectionView == null ) ?
+ null : collectionView.SourceCollection as IBindingList;
+ }
+
+ if( ( bindingList != null ) && ( bindingList.AllowNew ) )
+ {
+ itemIndex = bindingList.Count;
+ return bindingList.AddNew();
+ }
+
+ Type itemType = ItemsSourceHelper.GetItemTypeFromEnumeration( itemsSourceCollection );
+
+ if( itemType == null )
+ throw new InvalidOperationException( "An attempt was made to use a source whose item type cannot be determined." );
+
+ try
+ {
+ itemIndex = -1;
+ return Activator.CreateInstance( itemType );
+ }
+ catch( MissingMethodException exception )
+ {
+ throw new InvalidOperationException( "An attempt was made to use a source whose item type does not have a default constructor.", exception );
+ }
+ catch( Exception exception )
+ {
+ throw new InvalidOperationException( "An unsuccessful attempt was made to create an instance of the source's item type using the item's default constructor.", exception );
+ }
+ }
+
+ public static void CancelNewDataItem(
+ IEnumerable itemsSourceCollection,
+ DataGridControl dataGridControl,
+ object newItem,
+ int newItemIndex )
+ {
+ DataGridCollectionViewBase dataGridCollectionViewBase = itemsSourceCollection as DataGridCollectionViewBase;
+
+ if( dataGridCollectionViewBase != null )
+ {
+ if( dataGridCollectionViewBase.CurrentAddItem == newItem )
+ {
+ // The DataGridCollectionViewBAse's CancelNew will take care of calling
+ // the item's CancelEdit if it must do so.
+ dataGridCollectionViewBase.CancelNew();
+ }
+
+ return;
+ }
+
+ if( ( dataGridControl != null ) && ( dataGridControl.ItemsSource == null ) )
+ {
+ //unbound
+ return;
+ }
+
+ ICancelAddNew cancelAddNew = itemsSourceCollection as ICancelAddNew;
+
+ if( cancelAddNew == null )
+ {
+ CollectionView collectionView = itemsSourceCollection as CollectionView;
+
+ cancelAddNew = ( collectionView == null ) ?
+ null : collectionView.SourceCollection as ICancelAddNew;
+ }
+
+ if( cancelAddNew != null )
+ {
+ cancelAddNew.CancelNew( newItemIndex );
+ return;
+ }
+
+ IEditableObject editableObject = ItemsSourceHelper.GetEditableObject( newItem );
+
+ // editableObject can be an xceed datarow when directly inserted as Items in the DataGridControl.
+ if( ( editableObject != null ) && ( !( editableObject is Xceed.Wpf.DataGrid.DataRow ) ) )
+ {
+ editableObject.CancelEdit();
+ return;
+ }
+
+ if( newItemIndex != -1 )
+ {
+ IList list = itemsSourceCollection as IList;
+
+ if( list == null )
+ {
+ CollectionView collectionView = itemsSourceCollection as CollectionView;
+
+ list = ( collectionView == null ) ?
+ null : collectionView.SourceCollection as IList;
+ }
+
+ if( ( list != null ) && ( !list.IsFixedSize ) )
+ {
+ list.RemoveAt( newItemIndex );
+ }
+ }
+ }
+
+ public static void EndNewDataItem(
+ IEnumerable itemsSourceCollection,
+ DataGridControl dataGridControl,
+ object newItem,
+ ref int newItemIndex )
+ {
+ DataGridCollectionViewBase dataGridCollectionViewBase = itemsSourceCollection as DataGridCollectionViewBase;
+
+ if( dataGridCollectionViewBase != null )
+ {
+ if( dataGridCollectionViewBase.CurrentAddItem == newItem )
+ {
+ // The DataGridCollectionViewBase's EndNew will take care of calling
+ // the item's EndEdit if it must do so.
+ dataGridCollectionViewBase.CommitNew();
+ }
+
+ return;
+ }
+
+ if( ( dataGridControl != null ) && ( dataGridControl.ItemsSource == null ) )
+ {
+ //unbound
+ return;
+ }
+
+ ICancelAddNew cancelAddNew = itemsSourceCollection as ICancelAddNew;
+
+ if( cancelAddNew == null )
+ {
+ CollectionView collectionView = itemsSourceCollection as CollectionView;
+
+ cancelAddNew = ( collectionView == null ) ?
+ null : collectionView.SourceCollection as ICancelAddNew;
+ }
+
+ if( cancelAddNew != null )
+ {
+ cancelAddNew.EndNew( newItemIndex );
+ return;
+ }
+
+ IEditableObject editableObject = ItemsSourceHelper.GetEditableObject( newItem );
+
+ // editableObject can be a datarow when directly inserted as Items in the DataGridControl.
+ if( ( editableObject != null ) && ( !( editableObject is Xceed.Wpf.DataGrid.DataRow ) ) )
+ {
+ editableObject.EndEdit();
+ return;
+ }
+
+ IBindingList bindingList = itemsSourceCollection as IBindingList;
+
+ if( bindingList == null )
+ {
+ CollectionView collectionView = itemsSourceCollection as CollectionView;
+
+ bindingList = ( collectionView == null ) ?
+ null : collectionView.SourceCollection as IBindingList;
+ }
+
+ if( ( bindingList != null ) && ( bindingList.AllowNew ) )
+ {
+ // In that case the item is already added into the list by IBindingList.AddNew.
+ return;
+ }
+
+ IList list = itemsSourceCollection as IList;
+
+ if( list == null )
+ {
+ CollectionView collectionView = itemsSourceCollection as CollectionView;
+
+ list = ( collectionView == null ) ?
+ null : collectionView.SourceCollection as IList;
+ }
+
+ if( ( list == null ) || ( list.IsFixedSize ) )
+ throw new InvalidOperationException( "An attempt was made to insert an item into a source that does not implement the IList interface or that has a fixed size." );
+
+ newItemIndex = list.Count;
+ list.Add( newItem );
+ }
+
+ public static Dictionary GetFieldDescriptors(
+ PropertyDescriptorCollection properties,
+ bool supportDBNull )
+ {
+ int fieldCount = properties.Count;
+
+ Dictionary fieldDescriptors =
+ new Dictionary( fieldCount );
+
+ ItemsSourceHelper.ExtractFieldDescriptors( string.Empty, properties, supportDBNull, fieldDescriptors );
+
+ return fieldDescriptors;
+ }
+
+ public static void ExtractFieldDescriptors(
+ string namePrefix,
+ PropertyDescriptorCollection properties,
+ bool supportDBNull,
+ Dictionary fieldDescriptors )
+ {
+ int propertyCount = properties.Count;
+
+ for( int i = 0; i < propertyCount; i++ )
+ {
+ PropertyDescriptor propertyDescriptor = properties[ i ];
+ string name = propertyDescriptor.Name;
+
+ if( ( fieldDescriptors.ContainsKey( name ) ) && ( !string.IsNullOrEmpty( namePrefix ) ) )
+ name = namePrefix + "." + name;
+
+ Type type = propertyDescriptor.PropertyType;
+
+ DataGridForeignKeyDescription foreignKeyDescription = null;
+
+ // Try to retreive the ForeignKeyDescription if the field is an Enum
+ if( ( type != null ) && ( type.IsEnum ) )
+ {
+ foreignKeyDescription = ItemsSourceHelper.GetDataGridForeignKeyDescriptionForEnum( type );
+ }
+
+ fieldDescriptors[ name ] = new FieldDescriptor( name,
+ propertyDescriptor.DisplayName,
+ type,
+ propertyDescriptor,
+ null,
+ null,
+ propertyDescriptor.IsReadOnly,
+ false,
+ supportDBNull,
+ propertyDescriptor.IsBrowsable,
+ ItemsSourceHelper.IsASubRelationship( propertyDescriptor.PropertyType ),
+ false,
+ foreignKeyDescription );
+ }
+ }
+
+ public static void GenerateColumnsFromItemsSourceFields(
+ ColumnCollection columns,
+ IDictionary defaultCellEditors,
+ Dictionary fields,
+ bool autoCreateForeignKeyConfigurations )
+ {
+ DataGridControl dataGridControl = columns.DataGridControl;
+
+ using( columns.DeferColumnAdditionMessages() )
+ {
+ foreach( FieldDescriptor field in fields.Values )
+ {
+ string fieldName = field.Name;
+ ColumnBase column = columns[ fieldName ];
+ Column dataColumn = column as Column;
+ Type dataType = field.DataType;
+
+ if( column == null )
+ {
+ dataColumn = ItemsSourceHelper.CreateColumnFromItemsSourceField(
+ dataGridControl, defaultCellEditors, field, autoCreateForeignKeyConfigurations );
+
+ if( dataColumn != null )
+ {
+ columns.Add( dataColumn );
+ ItemsSourceHelper.ApplySettingsRepositoryToColumn( dataColumn );
+ }
+ }
+ else if( dataColumn != null )
+ {
+ if( field.ReadOnly )
+ {
+ if( dataColumn.ReadLocalValue( Column.ReadOnlyProperty ) == DependencyProperty.UnsetValue )
+ {
+ dataColumn.ReadOnly = field.ReadOnly;
+ }
+ }
+
+ if( field.OverrideReadOnlyForInsertion )
+ {
+ if( dataColumn.ReadLocalValue( ColumnBase.OverrideReadOnlyForInsertionProperty ) == DependencyProperty.UnsetValue )
+ {
+ dataColumn.OverrideReadOnlyForInsertion = field.OverrideReadOnlyForInsertion;
+ }
+ }
+
+ if( dataColumn.ReadLocalValue( Column.TitleProperty ) == DependencyProperty.UnsetValue )
+ {
+ dataColumn.Title = field.DisplayName;
+ }
+
+ if( dataColumn.ReadLocalValue( Column.CellEditorProperty ) == DependencyProperty.UnsetValue )
+ {
+ CellEditor cellEditor = null;
+
+ if( defaultCellEditors != null )
+ {
+ defaultCellEditors.TryGetValue( dataType, out cellEditor );
+ }
+
+ if( cellEditor == null )
+ {
+ object descriptionItemsSource = null;
+ object configurationItemsSource = null;
+ ForeignKeyConfiguration configuration = dataColumn.ForeignKeyConfiguration;
+
+ if( field.ForeignKeyDescription != null )
+ {
+ descriptionItemsSource = field.ForeignKeyDescription.ItemsSource;
+ }
+
+ if( configuration != null )
+ {
+ configurationItemsSource = configuration.ItemsSource;
+
+ if( configurationItemsSource == null )
+ {
+ configurationItemsSource = dataColumn.ReadLocalValue( Column.ForeignKeyConfigurationProperty );
+ }
+ }
+
+ // A foreign key ItemsSource is set and we can auto-create configuration
+ // OR
+ // if the foreign key ItemsSource was found in the ForeignKeyConfiguration
+ //
+ // use the Default ForeignKey CellEditor.
+ if( ( ( descriptionItemsSource != null ) && ( autoCreateForeignKeyConfigurations ) )
+ || ( configurationItemsSource != null ) )
+ {
+ cellEditor = DefaultCellEditorSelector.ForeignKeyCellEditor;
+ }
+ }
+
+ if( cellEditor == null )
+ {
+ cellEditor = DefaultCellEditorSelector.SelectCellEditor( dataType );
+ }
+
+ dataColumn.CellEditor = cellEditor;
+ }
+
+ if( ( field.ForeignKeyDescription != null )
+ && ( field.ForeignKeyDescription.ItemsSource != null )
+ && ( autoCreateForeignKeyConfigurations ) )
+ {
+ // Update the ForeignKeyConfiguration from the ForeignKeyDescription
+ // found on the FieldDescriptor
+ ForeignKeyConfiguration.SynchronizeForeignKeyConfigurationFromForeignKeyDescription(
+ dataColumn,
+ field.ForeignKeyDescription,
+ autoCreateForeignKeyConfigurations );
+ }
+
+ // Disable warning for DisplayMemberBinding when internaly used
+#pragma warning disable 618
+
+ if( dataColumn.DisplayMemberBinding == null )
+ {
+ dataColumn.DisplayMemberBinding = ItemsSourceHelper.CreateDefaultBinding(
+ false, field.Name, field,
+ false, ( dataColumn.ReadOnly && !dataColumn.OverrideReadOnlyForInsertion ), dataType );
+
+ //mark the Column's Binding as AutoCreated.
+ dataColumn.IsBindingAutoCreated = true;
+ dataColumn.IsBoundToDataGridUnboundItemProperty = field.IsDataGridUnboundItemProperty;
+ }
+
+#pragma warning restore 618
+ }
+ }
+ } //end using
+ }
+
+ public static Column CreateColumnFromItemsSourceField(
+ DataGridControl dataGridControl,
+ IDictionary defaultCellEditors,
+ FieldDescriptor field,
+ bool autoCreateForeignKeyConfigurations )
+ {
+ if( ( field.IsASubRelationship ) || ( !field.Browsable ) )
+ return null;
+
+ string fieldName = field.Name;
+ Type dataType = field.DataType;
+
+ Column dataColumn = new Column();
+ dataColumn.IsAutoCreated = true;
+ dataColumn.FieldName = fieldName;
+
+ bool readOnly = field.ReadOnly;
+ bool overrideReadOnlyForInsertion = field.OverrideReadOnlyForInsertion;
+
+ // We only set ReadOnly when the value is true in order for the inheritence chain to work.
+ // Otherwise, the column value is always used instead of the row or grid value.
+ if( readOnly )
+ dataColumn.ReadOnly = readOnly;
+
+ dataColumn.OverrideReadOnlyForInsertion = overrideReadOnlyForInsertion;
+ dataColumn.Title = field.DisplayName;
+
+ // Disable warning for DisplayMemberBinding when internaly used
+#pragma warning disable 618
+
+ dataColumn.DisplayMemberBinding = ItemsSourceHelper.CreateDefaultBinding(
+ false, field.Name, field,
+ false, ( readOnly && !overrideReadOnlyForInsertion ), dataType );
+
+#pragma warning restore 618
+
+ //mark the Column's Binding as AutoCreated.
+ dataColumn.IsBindingAutoCreated = true;
+ dataColumn.IsBoundToDataGridUnboundItemProperty = field.IsDataGridUnboundItemProperty;
+
+ CellEditor cellEditor = null;
+
+ if( defaultCellEditors != null )
+ {
+ defaultCellEditors.TryGetValue( dataType, out cellEditor );
+ }
+
+ if( ( field.ForeignKeyDescription != null )
+ && ( field.ForeignKeyDescription.ItemsSource != null )
+ && ( autoCreateForeignKeyConfigurations ) )
+ {
+ // We will only use the default foreign key CellEditor
+ // if:
+ // - a ForeignKey ItemsSource was detected
+ // - the grid allows the auto-creation of the ForeignKeyConfigurations
+ // else, use the default CellEditor
+ if( cellEditor == null )
+ {
+ cellEditor = DefaultCellEditorSelector.ForeignKeyCellEditor;
+ }
+
+ // Update the ForeignKeyConfiguration from the ForeignKeyDescription
+ // found on the FieldDescriptor
+ ForeignKeyConfiguration.SynchronizeForeignKeyConfigurationFromForeignKeyDescription(
+ dataColumn,
+ field.ForeignKeyDescription,
+ autoCreateForeignKeyConfigurations );
+ }
+
+ if( cellEditor == null )
+ {
+ cellEditor = DefaultCellEditorSelector.SelectCellEditor( dataType );
+ }
+
+ dataColumn.CellEditor = cellEditor;
+ return dataColumn;
+ }
+
+ private static void ApplySettingsRepositoryToColumn( ColumnBase column )
+ {
+ }
+
+ public static System.Windows.Data.Binding CreateDefaultBinding(
+ bool dataItemIsDataRow,
+ string fieldName,
+ FieldDescriptor sourceField,
+ bool supportDBNull,
+ bool readOnly,
+ Type dataType )
+ {
+ DataGridBindingInfo bindingInfo = new DataGridBindingInfo();
+
+ PropertyDescriptor propertyDescriptor = null;
+ string bindingPath = null;
+ string bindingXPath = null;
+
+ if( sourceField != null )
+ {
+ dataType = sourceField.DataType;
+ supportDBNull = sourceField.SupportDBNull;
+ readOnly |= ( sourceField.ReadOnly && !sourceField.OverrideReadOnlyForInsertion );
+ propertyDescriptor = sourceField.PropertyDescriptor;
+ bindingXPath = sourceField.BindingXPath;
+ bindingPath = sourceField.BindingPath;
+ }
+
+ if( dataItemIsDataRow )
+ {
+ bindingInfo.Path = new PropertyPath( "Cells[" + fieldName + "].Content" );
+ }
+ else
+ {
+ // We always use Path and XPath to be able to write back to a binding on self (".").
+ // Because the "propertyDescriptor" received is readonly, we must use the normal binding path to pass over that.
+ if( !string.IsNullOrEmpty( bindingXPath ) )
+ {
+ bindingInfo.XPath = bindingXPath;
+
+ if( !string.IsNullOrEmpty( bindingPath ) )
+ bindingInfo.Path = new PropertyPath( bindingPath, ItemsSourceHelper.EmptyObjectArray );
+ }
+ else
+ {
+ bindingInfo.Path = ItemsSourceHelper.CreatePropertyPath( fieldName, bindingPath, propertyDescriptor );
+ }
+ }
+
+ bindingInfo.ReadOnly = readOnly;
+ bindingInfo.Converter = new SourceDataConverter( supportDBNull );
+
+ bindingInfo.ValidationRules.Add( new SourceDataConverterValidationRule( supportDBNull, dataType ) );
+
+ return bindingInfo.GetBinding();
+ }
+
+ public static PropertyPath CreatePropertyPath( string fieldName, string bindingPath, PropertyDescriptor propertyDescriptor )
+ {
+ PropertyPath propertyPath = null;
+
+ if( !string.IsNullOrEmpty( bindingPath ) )
+ {
+ // Using user defined column binding info.
+ propertyPath = new PropertyPath( bindingPath, ItemsSourceHelper.EmptyObjectArray );
+ }
+ else
+ {
+ if( propertyDescriptor != null )
+ {
+ if( propertyDescriptor is DataGridItemPropertyBase.PropertyDescriptorFromItemPropertyBase )
+ {
+ // Using a DataGridItemPropertyBase.
+ propertyPath = new PropertyPath( "(0)", propertyDescriptor );
+ }
+ else
+ {
+ // Not using our CollectionView.
+ propertyPath = new PropertyPath( "(0).(1)", EmptyDataItemSafePropertyDescriptor.Singleton, propertyDescriptor );
+ }
+ }
+ else
+ {
+ propertyPath = new PropertyPath( fieldName, ItemsSourceHelper.EmptyObjectArray );
+ }
+ }
+
+ return propertyPath;
+ }
+
+ public static Dictionary GetFields( IEnumerable itemsSource, Type itemType )
+ {
+ DataView dataView = itemsSource as DataView;
+
+ if( dataView != null )
+ return ItemsSourceHelper.GetFieldsForDataView( dataView );
+
+ DataGridCollectionViewBase dataGridCollectionViewBase = itemsSource as DataGridCollectionViewBase;
+
+ if( dataGridCollectionViewBase != null )
+ return ItemsSourceHelper.GetFieldsForDataGridCollectionViewBase( dataGridCollectionViewBase, dataGridCollectionViewBase.ItemProperties );
+
+ bool supportsDBNull = ItemsSourceHelper.IsSourceSupportingDBNull( itemsSource );
+ ITypedList typedList = itemsSource as ITypedList;
+
+ if( typedList != null )
+ return ItemsSourceHelper.GetFieldDescriptors( typedList.GetItemProperties( null ), supportsDBNull );
+
+ if( itemType == null )
+ itemType = ItemsSourceHelper.GetItemTypeFromEnumeration( itemsSource );
+
+ return ItemsSourceHelper.GetFieldsForItemType( itemsSource, itemType, supportsDBNull );
+ }
+
+ private static Dictionary GetFieldsForItemType( IEnumerable itemsSource, Type itemType, bool supportsDBNull )
+ {
+ if( itemType == null )
+ return new Dictionary();
+
+ if( typeof( XmlNode ).IsAssignableFrom( itemType ) )
+ return new Dictionary();
+
+ if( itemType.IsArray )
+ return ItemsSourceHelper.GetFieldsForJaggedArray( itemType, itemsSource );
+
+ if( itemType.IsInterface )
+ return ItemsSourceHelper.GetFieldsForInterface( itemType );
+
+ if( ItemsSourceHelper.IsEntityFramework( itemType ) )
+ return ItemsSourceHelper.GetFieldsForEntityFramework( itemType );
+
+ TypeDescriptionProvider typeDescriptionProvider = TypeDescriptor.GetProvider( itemType );
+ object firstItem = ItemsSourceHelper.GetFirstItemByEnumerable( itemsSource );
+
+ ICustomTypeDescriptor customTypeDescriptor = firstItem as ICustomTypeDescriptor;
+
+ if( customTypeDescriptor == null )
+ customTypeDescriptor = typeDescriptionProvider.GetTypeDescriptor( itemType, firstItem );
+
+ if( ItemsSourceHelper.IsValueType( itemType ) )
+ {
+ Dictionary fieldDescriptors = new Dictionary();
+
+ fieldDescriptors.Add( ".", new FieldDescriptor( ".",
+ string.Empty,
+ itemType,
+ null,
+ null,
+ ".",
+ true,
+ false,
+ supportsDBNull,
+ true,
+ ItemsSourceHelper.IsASubRelationship( itemType ),
+ false,
+ null ) );
+
+ return fieldDescriptors;
+ }
+ else if( customTypeDescriptor != null )
+ {
+ string className = customTypeDescriptor.GetClassName();
+
+ Type customType = ( className == null ) ? null : Type.GetType( className );
+
+ if( ( customType != null ) && ( ItemsSourceHelper.IsValueType( customType ) ) )
+ {
+ Dictionary fieldDescriptors = new Dictionary();
+
+ DataGridForeignKeyDescription foreignKeyDescription = null;
+
+ // Try to retreive the ForeignKeyDescription if the field is an Enum
+ if( ( itemType != null ) && ( itemType.IsEnum ) )
+ {
+ foreignKeyDescription = ItemsSourceHelper.GetDataGridForeignKeyDescriptionForEnum( itemType );
+ }
+
+ fieldDescriptors.Add( ".", new FieldDescriptor( ".",
+ string.Empty,
+ itemType,
+ null,
+ null,
+ ".",
+ true,
+ false,
+ supportsDBNull,
+ true,
+ ItemsSourceHelper.IsASubRelationship( itemType ),
+ false,
+ foreignKeyDescription ) );
+
+ return fieldDescriptors;
+ }
+ }
+
+ if( customTypeDescriptor != null )
+ return ItemsSourceHelper.GetFieldDescriptors( customTypeDescriptor.GetProperties(), supportsDBNull );
+
+ return ItemsSourceHelper.GetFieldDescriptors( TypeDescriptor.GetProperties( itemType ), supportsDBNull );
+ }
+
+ public static Type GetItemTypeFromEnumeration( IEnumerable source )
+ {
+ if( source == null )
+ return null;
+
+ Type listType = source.GetType();
+
+ if( typeof( Array ).IsAssignableFrom( listType ) )
+ return listType.GetElementType();
+
+ Type itemType = ItemsSourceHelper.GetTypedListIndexerType( listType );
+
+ if( itemType != null )
+ return itemType;
+
+ itemType = ItemsSourceHelper.GetTypedEnumerationItemType( listType );
+
+ if( itemType != null )
+ return itemType;
+
+ object item = ItemsSourceHelper.GetFirstItemByEnumerable( source );
+
+ if( item != null )
+ return item.GetType();
+
+ return typeof( object );
+ }
+
+ public static IEditableObject GetEditableObject( object item )
+ {
+ System.Data.DataRow dataRow = item as System.Data.DataRow;
+
+ if( dataRow != null )
+ return new DataRowEditableWrapper( dataRow );
+
+ return item as IEditableObject;
+ }
+
+ public static bool IsASubRelationship( Type dataType )
+ {
+ if( ( !dataType.IsValueType ) && ( dataType != typeof( string ) ) )
+ {
+ if( typeof( IEnumerable ).IsAssignableFrom( dataType ) )
+ {
+ if( typeof( byte[] ).IsAssignableFrom( dataType ) )
+ return false;
+
+ return true;
+ }
+
+ if( typeof( IListSource ).IsAssignableFrom( dataType ) )
+ return true;
+ }
+
+ return false;
+ }
+
+ public static object TryGetDataRowFromDataItem( object dataItem )
+ {
+ DataRowView dataRowView = dataItem as DataRowView;
+
+ if( dataRowView != null )
+ {
+ return dataRowView.Row;
+ }
+
+ return dataItem;
+ }
+
+ public static System.Data.DataView TryGetDataViewFromDataGridContext( DataGridContext context )
+ {
+ if( context == null )
+ return null;
+
+ System.Data.DataView dataView = context.ItemsSourceCollection as System.Data.DataView;
+
+ // Maybe the context is a detail
+ if( dataView == null )
+ {
+ DataGridCollectionViewBase collectionViewBase =
+ context.ItemsSourceCollection as DataGridCollectionViewBase;
+
+ if( collectionViewBase != null )
+ {
+ dataView = collectionViewBase.SourceCollection as System.Data.DataView;
+ }
+ }
+
+ return dataView;
+ }
+
+ public static Type GetColumnDataType( System.Data.DataColumn column )
+ {
+ Type columnType;
+
+ if( column.AllowDBNull )
+ {
+ if( column.DataType == typeof( Boolean ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( Boolean ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( Byte ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( Char ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( DateTime ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( Decimal ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( Double ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( Int16 ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( Int32 ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( Int64 ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( SByte ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( Single ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( TimeSpan ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( UInt16 ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( UInt32 ) )
+ columnType = typeof( Nullable );
+ else if( column.DataType == typeof( UInt64 ) )
+ columnType = typeof( Nullable );
+ else
+ columnType = column.DataType;
+ }
+ else
+ columnType = column.DataType;
+ return columnType;
+ }
+
+ public static void CleanUpColumns( ColumnCollection columns, bool deleteAutoCreatedColumn )
+ {
+ ColumnBase[] tempColumns = new ColumnBase[ columns.Count ];
+ columns.CopyTo( tempColumns, 0 );
+
+ foreach( ColumnBase column in tempColumns )
+ {
+ Column dataColumn = column as Column;
+
+ if( dataColumn == null )
+ continue;
+
+ if( ( deleteAutoCreatedColumn ) && ( dataColumn.IsAutoCreated ) )
+ {
+ columns.Remove( column );
+ }
+ else if( dataColumn.IsBindingAutoCreated )
+ {
+ // Disable warning for DisplayMemberBinding when internaly used
+#pragma warning disable 618
+
+ //clear the display member binding.
+ dataColumn.DisplayMemberBinding = null;
+
+#pragma warning restore 618
+
+ dataColumn.IsBindingAutoCreated = false;
+ dataColumn.IsBoundToDataGridUnboundItemProperty = false;
+ }
+ }
+ }
+
+ public static FieldDescriptor CreateFieldFromDataGridItemProperty( DataGridItemPropertyBase itemProperty )
+ {
+ DataGridItemPropertyBase.PropertyDescriptorFromItemPropertyBase propertyDescriptor =
+ itemProperty.GetPropertyDescriptorForBinding();
+
+ string name = propertyDescriptor.Name;
+ Type type = propertyDescriptor.PropertyType;
+
+ // If an ItemProperty is in the public ItemProperties of the collection view,
+ // we always want to consider that property is not a detail and browsable.
+ return new FieldDescriptor( name,
+ propertyDescriptor.DisplayName,
+ propertyDescriptor.PropertyType,
+ propertyDescriptor,
+ null,
+ null,
+ itemProperty.IsReadOnly,
+ itemProperty.OverrideReadOnlyForInsertion.GetValueOrDefault( false ),
+ false,
+ true,
+ false,
+ itemProperty is DataGridUnboundItemProperty,
+ itemProperty.ForeignKeyDescription );
+ }
+
+ public static void UpdateColumnsOnItemsPropertiesChanged(
+ DataGridControl dataGridControl,
+ ColumnCollection columns,
+ bool autoCreateForeignKeyConfigurations,
+ NotifyCollectionChangedEventArgs e,
+ DataGridItemPropertyCollection itemProperties )
+ {
+ if( dataGridControl == null )
+ return;
+
+ switch( e.Action )
+ {
+ case NotifyCollectionChangedAction.Add:
+ {
+ foreach( DataGridItemPropertyBase itemProperty in e.NewItems )
+ {
+ string name = itemProperty.Name;
+
+ if( columns[ name ] == null )
+ {
+ Column column = ItemsSourceHelper.CreateColumnFromItemsSourceField(
+ dataGridControl, dataGridControl.DefaultCellEditors,
+ ItemsSourceHelper.CreateFieldFromDataGridItemProperty( itemProperty ),
+ autoCreateForeignKeyConfigurations );
+
+ if( column != null )
+ {
+ columns.Add( column );
+ ItemsSourceHelper.ApplySettingsRepositoryToColumn( column );
+ }
+ }
+ }
+ }
+
+ break;
+
+ case NotifyCollectionChangedAction.Remove:
+ {
+ foreach( DataGridItemPropertyBase itemProperty in e.OldItems )
+ {
+ string name = itemProperty.Name;
+ Column column = columns[ name ] as Column;
+
+ if( ( column != null ) && ( column.IsAutoCreated ) )
+ {
+ columns.Remove( column );
+ }
+ }
+
+ break;
+ }
+
+ case NotifyCollectionChangedAction.Replace:
+ {
+ foreach( DataGridItemPropertyBase itemProperty in e.OldItems )
+ {
+ string name = itemProperty.Name;
+ Column column = columns[ name ] as Column;
+
+ if( ( column != null ) && ( column.IsAutoCreated ) )
+ {
+ columns.Remove( column );
+ }
+ }
+
+ foreach( DataGridItemPropertyBase itemProperty in e.NewItems )
+ {
+ string name = itemProperty.Name;
+
+ if( columns[ name ] == null )
+ {
+ Column column = ItemsSourceHelper.CreateColumnFromItemsSourceField(
+ dataGridControl, dataGridControl.DefaultCellEditors,
+ ItemsSourceHelper.CreateFieldFromDataGridItemProperty( itemProperty ),
+ autoCreateForeignKeyConfigurations );
+
+ if( column != null )
+ {
+ columns.Add( column );
+ ItemsSourceHelper.ApplySettingsRepositoryToColumn( column );
+ }
+ }
+ }
+
+ break;
+ }
+
+ case NotifyCollectionChangedAction.Reset:
+ {
+ for( int i = columns.Count - 1; i >= 0; i-- )
+ {
+ Column dataColumn = columns[ i ] as Column;
+
+ if( ( dataColumn != null ) && ( dataColumn.IsAutoCreated ) )
+ {
+ columns.Remove( dataColumn );
+ }
+ }
+
+ foreach( DataGridItemPropertyBase itemProperty in itemProperties )
+ {
+ string name = itemProperty.Name;
+
+ if( columns[ name ] == null )
+ {
+ Column column = ItemsSourceHelper.CreateColumnFromItemsSourceField(
+ dataGridControl, dataGridControl.DefaultCellEditors,
+ ItemsSourceHelper.CreateFieldFromDataGridItemProperty( itemProperty ),
+ autoCreateForeignKeyConfigurations );
+
+ if( column != null )
+ {
+ columns.Add( column );
+ ItemsSourceHelper.ApplySettingsRepositoryToColumn( column );
+ }
+ }
+ }
+
+ break;
+ }
+
+ //case NotifyCollectionChangedAction.Move:
+ default:
+ break;
+
+ }
+ }
+
+ #endregion PUBLIC STATIC METHODS
+
+ #region PRIVATE STATIC METHODS
+
+ private static Type GetTypedListIndexerType( Type listType )
+ {
+ if( ( !typeof( IList ).IsAssignableFrom( listType ) && !typeof( ITypedList ).IsAssignableFrom( listType ) ) && !typeof( IListSource ).IsAssignableFrom( listType ) )
+ return null;
+
+ PropertyInfo info = null;
+ PropertyInfo[] properties = listType.GetProperties( BindingFlags.Public | BindingFlags.Instance );
+
+ for( int i = 0; i < properties.Length; i++ )
+ {
+ if( ( properties[ i ].GetIndexParameters().Length > 0 ) && ( properties[ i ].PropertyType != typeof( object ) ) )
+ {
+ info = properties[ i ];
+
+ if( info.Name == "Item" )
+ return info.PropertyType;
+ }
+ }
+
+ if( info != null )
+ return info.PropertyType;
+
+ return null;
+ }
+
+ private static Type GetTypedEnumerationItemType( Type listType )
+ {
+ foreach( Type interfaceType in listType.GetInterfaces() )
+ {
+ if( ( interfaceType.IsGenericType ) && ( interfaceType.GetGenericTypeDefinition() == typeof( IEnumerable<> ) ) )
+ return interfaceType.GetGenericArguments()[ 0 ];
+ }
+
+ return null;
+ }
+
+ private static Dictionary GetFieldsForDataView( DataView dataView )
+ {
+ PropertyDescriptorCollection itemProperties = ( ( ITypedList )dataView ).GetItemProperties( null );
+ int fieldCount = itemProperties.Count;
+ Dictionary fieldDescriptors = new Dictionary( fieldCount );
+
+ Dictionary foreignKeyConstraints =
+ ItemsSourceHelper.GetForeignKeyConstraints( dataView.Table.Constraints );
+
+ DataColumnCollection columns = dataView.Table.Columns;
+
+ for( int i = 0; i < fieldCount; i++ )
+ {
+ PropertyDescriptor propertyDescriptor = itemProperties[ i ];
+
+ string name = propertyDescriptor.Name;
+ DataColumn column = columns[ name ];
+
+ string displayName =
+ ( column == null ) ?
+ propertyDescriptor.DisplayName : column.Caption;
+
+ bool allowBDNull =
+ ( column == null ) ?
+ false : column.AllowDBNull;
+
+ ForeignKeyConstraint foreignKeyConstraint = null;
+ foreignKeyConstraints.TryGetValue( name, out foreignKeyConstraint );
+
+ DataTableForeignKeyDescription foreignKeyDescription =
+ ItemsSourceHelper.GetDataGridForeignKeyDescriptionForForeignKeyConstraint( foreignKeyConstraint );
+
+ fieldDescriptors[ name ] = new FieldDescriptor( name,
+ displayName,
+ propertyDescriptor.PropertyType,
+ propertyDescriptor,
+ null,
+ name,
+ propertyDescriptor.IsReadOnly,
+ false,
+ allowBDNull,
+ propertyDescriptor.IsBrowsable,
+ ( column == null ) && ItemsSourceHelper.IsASubRelationship( propertyDescriptor.PropertyType ),
+ false,
+ foreignKeyDescription );
+
+ Debug.Assert( ( column != null ) || ( ItemsSourceHelper.IsASubRelationship( propertyDescriptor.PropertyType ) ),
+ "If we don't have a column that corresponds to the property of a DataRowView, it is safe to assume that we have a sub relation." );
+ }
+
+ return fieldDescriptors;
+ }
+
+ private static Dictionary GetFieldsForDataTable( DataTable dataTable )
+ {
+ DataColumnCollection columns = dataTable.Columns;
+ int fieldCount = columns.Count;
+
+ Dictionary fieldDescriptors = new Dictionary( fieldCount );
+ Dictionary foreignKeyConstraints =
+ ItemsSourceHelper.GetForeignKeyConstraints( dataTable.Constraints );
+
+ for( int i = 0; i < fieldCount; i++ )
+ {
+ DataColumn column = columns[ i ];
+
+ string name = column.Caption;
+
+ ForeignKeyConstraint foreignKeyConstraint = null;
+ foreignKeyConstraints.TryGetValue( name, out foreignKeyConstraint );
+
+ DataTableForeignKeyDescription foreignKeyDescription =
+ ItemsSourceHelper.GetDataGridForeignKeyDescriptionForForeignKeyConstraint( foreignKeyConstraint );
+
+ fieldDescriptors[ name ] = new FieldDescriptor( name,
+ name,
+ column.DataType,
+ null,
+ null,
+ "[" + name + "]",
+ column.ReadOnly,
+ false,
+ column.AllowDBNull,
+ true,
+ false,
+ false,
+ foreignKeyDescription );
+ }
+
+ return fieldDescriptors;
+ }
+
+ private static Dictionary GetFieldsForDataGridCollectionViewBase(
+ DataGridCollectionViewBase dataGridCollectionViewBase,
+ DataGridItemPropertyCollection itemProperties )
+ {
+ int fieldCount = itemProperties.Count;
+
+ Dictionary fieldDescriptors =
+ new Dictionary( fieldCount );
+
+ for( int i = 0; i < fieldCount; i++ )
+ {
+ DataGridItemPropertyBase itemProperty = itemProperties[ i ];
+ fieldDescriptors[ itemProperty.Name ] = ItemsSourceHelper.CreateFieldFromDataGridItemProperty( itemProperty );
+ }
+
+ return fieldDescriptors;
+ }
+
+ private static Dictionary GetFieldsForInterface( Type itemType )
+ {
+ Dictionary fieldDescriptors = new Dictionary();
+ ItemsSourceHelper.ExtractFieldDescriptors( string.Empty, TypeDescriptor.GetProperties( itemType ), false, fieldDescriptors );
+
+ foreach( Type interfaceType in itemType.GetInterfaces() )
+ {
+ ItemsSourceHelper.ExtractFieldDescriptors( interfaceType.FullName, TypeDescriptor.GetProperties( interfaceType ), false, fieldDescriptors );
+ }
+
+ return fieldDescriptors;
+ }
+
+ private static Dictionary GetFieldsForJaggedArray(
+ Type itemType,
+ IEnumerable jaggedArray )
+ {
+ int fieldCount = 0;
+ IEnumerator enumerator = jaggedArray.GetEnumerator();
+ enumerator.MoveNext();
+
+ try
+ {
+ Array arrayItem = enumerator.Current as Array;
+ fieldCount = arrayItem.GetLength( 0 );
+ }
+ catch
+ {
+ }
+
+ Dictionary fieldDescriptors =
+ new Dictionary( fieldCount );
+
+ Type fieldType = itemType.GetElementType();
+
+ for( int i = 0; i < fieldCount; i++ )
+ {
+ string name = ".[" + i.ToString( CultureInfo.InvariantCulture ) + "]";
+
+ DataGridForeignKeyDescription foreignKeyDescription = null;
+
+ // Try to retreive the ForeignKeyDescription if the field is an Enum
+ if( ( fieldType != null ) && ( fieldType.IsEnum ) )
+ {
+ foreignKeyDescription = ItemsSourceHelper.GetDataGridForeignKeyDescriptionForEnum( fieldType );
+ }
+
+ fieldDescriptors[ name ] = new FieldDescriptor( name,
+ name,
+ fieldType,
+ new JaggedArrayPropertyDescriptor( i, fieldType ),
+ null,
+ null,
+ false,
+ false,
+ false,
+ true,
+ ItemsSourceHelper.IsASubRelationship( fieldType ),
+ false,
+ foreignKeyDescription );
+ }
+
+ return fieldDescriptors;
+ }
+
+ private static Dictionary GetFieldsForEntityFramework( Type itemType )
+ {
+ PropertyDescriptorCollection propertyDescriptors = TypeDescriptor.GetProperties( itemType );
+ Dictionary fieldDescriptors = new Dictionary( propertyDescriptors.Count );
+
+ foreach( PropertyDescriptor propertyDescriptor in propertyDescriptors )
+ {
+ bool isEntityKey = false;
+ bool allowNull = false;
+
+ EdmScalarPropertyAttribute attribute = propertyDescriptor.Attributes[ typeof( EdmScalarPropertyAttribute ) ] as EdmScalarPropertyAttribute;
+
+ if( attribute != null )
+ {
+ isEntityKey = attribute.EntityKeyProperty;
+ allowNull = attribute.IsNullable;
+ }
+
+ string name = propertyDescriptor.Name;
+
+ fieldDescriptors[ name ] = new FieldDescriptor(
+ name,
+ propertyDescriptor.DisplayName,
+ propertyDescriptor.PropertyType,
+ propertyDescriptor,
+ null,
+ name,
+ ( propertyDescriptor.IsReadOnly || isEntityKey ), // A column must be read-only if it's an EntityKey (except for the InsertionRow)
+ ( !propertyDescriptor.IsReadOnly && isEntityKey ),
+ allowNull,
+ propertyDescriptor.IsBrowsable,
+ ItemsSourceHelper.IsASubRelationship( propertyDescriptor.PropertyType ),
+ false,
+ null );
+ }
+
+ return fieldDescriptors;
+ }
+
+ private static Dictionary GetForeignKeyConstraints(
+ ConstraintCollection constraints )
+ {
+ Dictionary foreignKeyConstraints =
+ new Dictionary();
+
+ // Detect every ForeignKeyConstraints
+ foreach( Constraint constraint in constraints )
+ {
+ ForeignKeyConstraint foreignKeyConstraint = constraint as ForeignKeyConstraint;
+
+ // Not a ForeignKeyConstraint
+ if( foreignKeyConstraint == null )
+ continue;
+
+ // We only support auto-detection when the ForeignKey is composed of
+ // a single column
+ if( ( foreignKeyConstraint.Columns != null )
+ && ( foreignKeyConstraint.Columns.Length == 1 ) )
+ {
+ foreignKeyConstraints.Add( foreignKeyConstraint.Columns[ 0 ].ColumnName, foreignKeyConstraint );
+ }
+ }
+
+ return foreignKeyConstraints;
+ }
+
+ internal static DataGridForeignKeyDescription GetDataGridForeignKeyDescriptionForEnum( Type enumType )
+ {
+ DataGridForeignKeyDescription foreignKeyDescription = null;
+
+ if( ( enumType != null ) && ( enumType.IsEnum ) )
+ {
+ foreignKeyDescription = new DataGridForeignKeyDescription();
+
+ // Using "." as default value path will revert to Self when used as
+ // SelectedValuePath when bound to a DataGridForeignKeyDictionary or
+ // ComboBox (default editor)
+ foreignKeyDescription.ValuePath = ".";
+ foreignKeyDescription.ItemsSource = Enum.GetValues( enumType );
+ foreignKeyDescription.IsAutoCreated = true;
+ }
+
+ return foreignKeyDescription;
+ }
+
+ private static DataTableForeignKeyDescription GetDataGridForeignKeyDescriptionForForeignKeyConstraint(
+ ForeignKeyConstraint foreignKeyConstraint )
+ {
+ DataTableForeignKeyDescription foreignKeyDescription = null;
+
+ if( foreignKeyConstraint != null )
+ {
+ if( ( foreignKeyConstraint.Columns != null )
+ && ( foreignKeyConstraint.Columns.Length == 1 ) )
+ {
+ foreignKeyDescription = new DataTableForeignKeyDescription();
+ ( ( DataTableForeignKeyDescription )foreignKeyDescription ).ForeignKeyConstraint = foreignKeyConstraint;
+ foreignKeyDescription.IsAutoCreated = true;
+ }
+ }
+
+ return foreignKeyDescription;
+ }
+
+ #endregion PRIVATE STATIC METHODS
+
+ #region PRIVATE FIELDS
+
+ private static readonly object[] EmptyObjectArray = new object[ 0 ];
+
+ private static Type EntityObjectType = Type.GetType(
+ "System.Data.Objects.DataClasses.EntityObject, System.Data.Entity, Version=" + _XceedVersionInfo.FrameworkVersion + ", Culture=neutral, PublicKeyToken=b77a5c561934e089",
+ false, false );
+
+ #endregion PRIVATE FIELDS
+
+ #region FieldDescriptor NESTED CLASSES
+
+ internal class FieldDescriptor
+ {
+ #region CONSTRUCTORS
+
+ public FieldDescriptor(
+ string name,
+ string displayName,
+ Type dataType,
+ PropertyDescriptor propertyDescriptor,
+ string bindingXPath,
+ string bindingPath,
+ bool readOnly,
+ bool overrideReadOnlyForInsertion,
+ bool supportDBNull,
+ bool browsable,
+ bool isASubRelationship,
+ bool isDataGridUnboundItemProperty,
+ DataGridForeignKeyDescription foreignKeyDescription )
+ {
+ if( name == null )
+ throw new ArgumentNullException( "name" );
+
+ if( displayName == null )
+ throw new ArgumentNullException( "displayName" );
+
+ if( dataType == null )
+ throw new ArgumentNullException( "dataType" );
+
+ this.ReadOnly = readOnly;
+ this.OverrideReadOnlyForInsertion = overrideReadOnlyForInsertion;
+ m_name = name;
+ m_displayName = displayName;
+ m_dataType = dataType;
+ m_propertyDescriptor = propertyDescriptor;
+ m_bindingPath = bindingPath;
+ m_bindingXPath = bindingXPath;
+ this.SupportDBNull = supportDBNull;
+ this.Browsable = browsable;
+ this.IsASubRelationship = isASubRelationship;
+ this.IsDataGridUnboundItemProperty = isDataGridUnboundItemProperty;
+ this.ForeignKeyDescription = foreignKeyDescription;
+ }
+
+ #endregion CONSTRUCTORS
+
+ #region Name PROPERTY
+
+ public string Name
+ {
+ get
+ {
+ return m_name;
+ }
+ }
+
+ private string m_name; // = null
+
+ #endregion Name PROPERTY
+
+ #region DisplayName PROPERTY
+
+ public string DisplayName
+ {
+ get
+ {
+ return m_displayName;
+ }
+ }
+
+ private string m_displayName; // = null
+
+ #endregion DisplayName PROPERTY
+
+ #region BindingPath PROPERTY
+
+ public string BindingPath
+ {
+ get
+ {
+ return m_bindingPath;
+ }
+ }
+
+ private string m_bindingPath; // = null
+
+ #endregion BindingPath PROPERTY
+
+ #region BindingXPath PROPERTY
+
+ public string BindingXPath
+ {
+ get
+ {
+ return m_bindingXPath;
+ }
+ }
+
+ private string m_bindingXPath; // = null
+
+ #endregion BindingXPath PROPERTY
+
+ #region DataType PROPERTY
+
+ public Type DataType
+ {
+ get
+ {
+ return m_dataType;
+ }
+ }
+
+ private Type m_dataType; // = null
+
+ #endregion DataType PROPERTY
+
+ #region PropertyDescriptor PROPERTY
+
+ public PropertyDescriptor PropertyDescriptor
+ {
+ get
+ {
+ return m_propertyDescriptor;
+ }
+ }
+
+ private PropertyDescriptor m_propertyDescriptor; // = null
+
+ #endregion PropertyDescriptor PROPERTY
+
+ #region ReadOnly PROPERTY
+
+ public bool ReadOnly
+ {
+ get
+ {
+ return m_flags[ ( int )FieldDescriptorFlags.ReadOnly ];
+ }
+ private set
+ {
+ m_flags[ ( int )FieldDescriptorFlags.ReadOnly ] = value;
+ }
+ }
+
+ #endregion ReadOnly PROPERTY
+
+ #region OverrideReadOnlyForInsertion Property
+
+ public bool OverrideReadOnlyForInsertion
+ {
+ get
+ {
+ return m_flags[ ( int )FieldDescriptorFlags.OverrideReadOnlyForInsertion ];
+ }
+ private set
+ {
+ m_flags[ ( int )FieldDescriptorFlags.OverrideReadOnlyForInsertion ] = value;
+ }
+ }
+
+ #endregion OverrideReadOnlyForInsertion Property
+
+ #region SupportDBNull PROPERTY
+
+ public bool SupportDBNull
+ {
+ get
+ {
+ return m_flags[ ( int )FieldDescriptorFlags.SupportDBNull ];
+ }
+ private set
+ {
+ m_flags[ ( int )FieldDescriptorFlags.SupportDBNull ] = value;
+ }
+ }
+
+ #endregion SupportDBNull PROPERTY
+
+ #region Browsable Property
+
+ public bool Browsable
+ {
+ get
+ {
+ return m_flags[ ( int )FieldDescriptorFlags.Browsable ];
+ }
+ private set
+ {
+ m_flags[ ( int )FieldDescriptorFlags.Browsable ] = value;
+ }
+ }
+
+ #endregion Browsable Property
+
+ #region IsASubRelationship Property
+
+ public bool IsASubRelationship
+ {
+ get
+ {
+ return m_flags[ ( int )FieldDescriptorFlags.IsASubRelationship ];
+ }
+ private set
+ {
+ m_flags[ ( int )FieldDescriptorFlags.IsASubRelationship ] = value;
+ }
+ }
+
+ #endregion IsASubRelationship Property
+
+ #region IsDataGridUnboundItemProperty Property
+
+ public bool IsDataGridUnboundItemProperty
+ {
+ get
+ {
+ return m_flags[ ( int )FieldDescriptorFlags.IsDataGridUnboundItemProperty ];
+ }
+ private set
+ {
+ m_flags[ ( int )FieldDescriptorFlags.IsDataGridUnboundItemProperty ] = value;
+ }
+ }
+
+ #endregion IsDataGridUnboundItemProperty Property
+
+ #region ForeignKeyDescription
+
+ public DataGridForeignKeyDescription ForeignKeyDescription
+ {
+ get;
+ private set;
+ }
+
+ #endregion
+
+ #region Private Fields
+
+ private BitVector32 m_flags = new BitVector32();
+
+ #endregion
+
+ #region FieldDescriptor Flags
+
+ [Flags]
+ private enum FieldDescriptorFlags
+ {
+ ReadOnly = 1,
+ SupportDBNull = 2,
+ Browsable = 4,
+ IsASubRelationship = 8,
+ IsForeignKey = 16,
+ OverrideReadOnlyForInsertion = 32,
+ IsDataGridUnboundItemProperty = 64
+ }
+
+ #endregion
+ }
+
+ #endregion NESTED CLASSES
+
+ internal static System.Windows.Data.Binding AutoCreateDisplayMemberBinding( Column column, DataGridContext dataGridContext, object dataItem, out bool isDataGridUnboundItemProperty )
+ {
+ System.Windows.Data.Binding displayMemberBinding = null;
+
+ if( column == null )
+ throw new ArgumentNullException( "column" );
+
+ string name = column.FieldName;
+
+ // Don't create the default binding if FieldName is null and we're in design-time.
+ if( !string.IsNullOrEmpty( name ) )
+ {
+ ItemsSourceHelper.FieldDescriptor fieldDescriptor;
+ dataGridContext.ItemsSourceFieldDescriptors.TryGetValue( name, out fieldDescriptor );
+
+ isDataGridUnboundItemProperty = ( fieldDescriptor == null ) ? false :
+ fieldDescriptor.IsDataGridUnboundItemProperty;
+
+ displayMemberBinding = ItemsSourceHelper.CreateDefaultBinding(
+ dataItem is DataRow, name, fieldDescriptor,
+ false, ( column.ReadOnly && !column.OverrideReadOnlyForInsertion ), typeof( object ) );
+ }
+ else
+ {
+ isDataGridUnboundItemProperty = false;
+ }
+
+ return displayMemberBinding;
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/KeyActivationGesture.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/KeyActivationGesture.cs
new file mode 100644
index 00000000..6e7e526d
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/KeyActivationGesture.cs
@@ -0,0 +1,117 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Input;
+using System.Windows;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public sealed class KeyActivationGesture : ActivationGesture
+ {
+ #region Key Property
+
+ public static readonly DependencyProperty KeyProperty = DependencyProperty.Register(
+ "Key",
+ typeof( Key ),
+ typeof( KeyActivationGesture ),
+ new FrameworkPropertyMetadata( Key.None ) );
+
+ public Key Key
+ {
+ get
+ {
+ return ( Key )this.GetValue( KeyActivationGesture.KeyProperty );
+ }
+ set
+ {
+ this.SetValue( KeyActivationGesture.KeyProperty, value );
+ }
+ }
+
+ #endregion
+
+ #region SystemKey Property
+
+ public static readonly DependencyProperty SystemKeyProperty = DependencyProperty.Register(
+ "SystemKey",
+ typeof( Key ),
+ typeof( KeyActivationGesture ),
+ new FrameworkPropertyMetadata( Key.None ) );
+
+ public Key SystemKey
+ {
+ get
+ {
+ return ( Key )this.GetValue( KeyActivationGesture.SystemKeyProperty );
+ }
+ set
+ {
+ this.SetValue( KeyActivationGesture.SystemKeyProperty, value );
+ }
+ }
+
+ #endregion
+
+ #region Modifiers Property
+
+ public static readonly DependencyProperty ModifiersProperty = DependencyProperty.Register(
+ "Modifiers",
+ typeof( ModifierKeys ),
+ typeof( KeyActivationGesture ),
+ new FrameworkPropertyMetadata( ( ModifierKeys )ModifierKeys.None ) );
+
+ public ModifierKeys Modifiers
+ {
+ get
+ {
+ return ( ModifierKeys )this.GetValue( KeyActivationGesture.ModifiersProperty );
+ }
+ set
+ {
+ this.SetValue( KeyActivationGesture.ModifiersProperty, value );
+ }
+ }
+
+ #endregion
+
+ public bool IsActivationKey( Key key, Key systemKey, ModifierKeys modifiers)
+ {
+ if( key == Key.System )
+ {
+ return
+ ( ( systemKey == this.SystemKey ) &&
+ ( modifiers == this.Modifiers ) );
+ }
+ else
+ {
+ return
+ ( ( key == this.Key ) &&
+ ( modifiers == this.Modifiers ) );
+ }
+ }
+
+ protected override Freezable CreateInstanceCore()
+ {
+ return new KeyActivationGesture();
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/LevelGroupConfigurationSelector.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/LevelGroupConfigurationSelector.cs
new file mode 100644
index 00000000..2aa26b70
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/LevelGroupConfigurationSelector.cs
@@ -0,0 +1,56 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Markup;
+using System.Collections.ObjectModel;
+using System.Windows.Data;
+
+namespace Xceed.Wpf.DataGrid
+{
+ [ContentProperty( "SelectorItems" )]
+ public class LevelGroupConfigurationSelector : GroupConfigurationSelector
+ {
+ private LevelGroupConfigurationSelectorItemCollection m_groupConfigurationSelectorItems = new LevelGroupConfigurationSelectorItemCollection();
+
+ public ObservableCollection SelectorItems
+ {
+ get
+ {
+ return m_groupConfigurationSelectorItems;
+ }
+ }
+
+ public override GroupConfiguration SelectGroupConfiguration(int groupLevel, CollectionViewGroup collectionViewGroup, System.ComponentModel.GroupDescription groupDescription)
+ {
+ if (m_groupConfigurationSelectorItems.Count == 0)
+ return base.SelectGroupConfiguration( groupLevel, collectionViewGroup, groupDescription );
+
+ LevelGroupConfigurationSelectorItem levelGroupConfig = m_groupConfigurationSelectorItems.GetGroupConfigurationSelectorItem( groupLevel );
+ if( levelGroupConfig != null )
+ {
+ return levelGroupConfig.GroupConfiguration;
+ }
+
+ return base.SelectGroupConfiguration( groupLevel, collectionViewGroup, groupDescription );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/LevelGroupConfigurationSelectorItem.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/LevelGroupConfigurationSelectorItem.cs
new file mode 100644
index 00000000..f1c595b8
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/LevelGroupConfigurationSelectorItem.cs
@@ -0,0 +1,89 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Markup;
+
+namespace Xceed.Wpf.DataGrid
+{
+ [ContentProperty("GroupConfiguration" )]
+ public class LevelGroupConfigurationSelectorItem
+ {
+ #region Level Property
+
+ public int Level
+ {
+ get
+ {
+ return m_level;
+ }
+ set
+ {
+ if( m_isSealed == true )
+ throw new InvalidOperationException( "An attempt was made to modify the level of a LevelGroupConfigurationSelectorItem that has already been added to a LevelGroupConfigurationSelector." );
+
+ m_level = value;
+ }
+ }
+
+ private int m_level;
+
+ #endregion
+
+ #region GroupConfiguration Property
+
+ public GroupConfiguration GroupConfiguration
+ {
+ get
+ {
+ return m_groupConfig;
+ }
+ set
+ {
+ m_groupConfig = value;
+ }
+ }
+
+ private GroupConfiguration m_groupConfig;
+
+ #endregion
+
+ #region IsSealed Property
+
+ private bool m_isSealed = false;
+
+ public bool IsSealed
+ {
+ get
+ {
+ return m_isSealed;
+ }
+ }
+
+ internal void Seal()
+ {
+ m_isSealed = true;
+ }
+
+ #endregion
+
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/LevelGroupConfigurationSelectorItemCollection.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/LevelGroupConfigurationSelectorItemCollection.cs
new file mode 100644
index 00000000..1534a547
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/LevelGroupConfigurationSelectorItemCollection.cs
@@ -0,0 +1,73 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Collections.ObjectModel;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal sealed class LevelGroupConfigurationSelectorItemCollection : ObservableCollection
+ {
+ internal LevelGroupConfigurationSelectorItemCollection()
+ : base()
+ {
+ }
+
+ public LevelGroupConfigurationSelectorItem GetGroupConfigurationSelectorItem( int level )
+ {
+ LevelGroupConfigurationSelectorItem retval = null;
+
+ foreach( LevelGroupConfigurationSelectorItem levelGroupConfig in this )
+ {
+ if( levelGroupConfig.Level == level )
+ {
+ retval = levelGroupConfig;
+ break;
+ }
+ }
+
+ return retval;
+ }
+
+ protected override void InsertItem( int index, LevelGroupConfigurationSelectorItem item )
+ {
+ if( this.GetGroupConfigurationSelectorItem( item.Level ) != null )
+ throw new InvalidOperationException( "An attempt was made to insert a LevelGroupConfigurationSelectorItem that specifies the same level as an existing LevelGroupConfigurationSelectorItem." );
+
+ base.InsertItem( index, item );
+
+ item.Seal();
+ }
+
+ protected override void SetItem( int index, LevelGroupConfigurationSelectorItem item )
+ {
+ LevelGroupConfigurationSelectorItem oldLevelGroupConfig = this[ index ];
+ LevelGroupConfigurationSelectorItem failingLevelGroupConfig = this.GetGroupConfigurationSelectorItem( item.Level );
+
+ if( ( failingLevelGroupConfig != null ) && ( oldLevelGroupConfig != failingLevelGroupConfig ) )
+ throw new InvalidOperationException( "An attempt was made to set a LevelGroupConfigurationSelectorItem that specifies the same level as an existing LevelGroupConfigurationSelectorItem." );
+
+ base.SetItem( index, item );
+
+ item.Seal();
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Log.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Log.cs
new file mode 100644
index 00000000..694ecfe7
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Log.cs
@@ -0,0 +1,172 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.IO;
+using System.Threading;
+using System.Diagnostics;
+using System.Runtime.ExceptionServices;
+
+namespace Xceed.Wpf.DataGrid
+{
+ static class Log
+ {
+#if LOG
+ static Log()
+ {
+ m_path = Environment.GetEnvironmentVariable( "AstoriaLogs" );
+
+ if( string.IsNullOrEmpty( m_path ) )
+ m_path = Path.Combine( Environment.GetFolderPath( Environment.SpecialFolder.ApplicationData ), @"XceedLog" );
+
+ Directory.CreateDirectory( m_path );
+ Log.WriteLine( "" );
+ Log.WriteLine( null, "*** App starting - Version : " + typeof( Log ).Assembly.GetName().Version.ToString() + " - Start time : " + DateTime.Now.ToString( "yy-MM-dd HH:mm:ss" ) );
+ AppDomain.CurrentDomain.FirstChanceException += new EventHandler( CurrentDomain_FirstChanceException );
+ }
+
+ static void CurrentDomain_FirstChanceException( object sender, FirstChanceExceptionEventArgs e )
+ {
+ Exception exception = e.Exception;
+
+ if( exception != null )
+ {
+ StackTrace stackTrace = new StackTrace( 1 );
+ string stackTraceString = stackTrace.ToString();
+
+ if( stackTraceString.IndexOf( "xceed", StringComparison.InvariantCultureIgnoreCase ) >= 0 )
+ {
+ lock( m_unknowSource )
+ {
+ Log.WriteLine( m_unknowSource, exception.ToString() );
+ Log.WriteLine( "--------------------" );
+ Log.WriteLine( stackTraceString );
+ }
+ }
+ }
+ else
+ {
+ Log.WriteLine( "FirstChanceException with a null/no exception." );
+ }
+ }
+
+ private static string m_path;
+ private static string m_fileName = Environment.UserName + "-" + Environment.MachineName + "-" + DateTime.Now.ToString( "yyMMdd-HHmmss-" ) + Guid.NewGuid().ToString();
+ private static int m_indentation = 0;
+ private static UnknowSource m_unknowSource = new UnknowSource();
+
+ public static void Assert( object source, bool condition, string message )
+ {
+ if( !condition )
+ {
+ Log.WriteLine( source, "# - " + message );
+
+ StackTrace stackTrace = new StackTrace();
+ Log.WriteLine( stackTrace.ToString() );
+ }
+ }
+
+ public static void Start( object source, string message )
+ {
+ if( source == null )
+ source = m_unknowSource;
+
+ message = DateTime.Now.ToString( "HHmmss" ) + " | " +
+ Thread.CurrentThread.GetHashCode().ToString() + " : " +
+ source.GetHashCode().ToString( "0000000000" ) + " : " +
+ new string( ' ', m_indentation * 2 ) +
+ message + " - Start";
+
+ Log.WriteLine( message );
+ m_indentation++;
+ }
+
+ public static void End( object source, string message )
+ {
+ if( source == null )
+ source = m_unknowSource;
+
+ m_indentation--;
+
+ if( m_indentation < 0 )
+ m_indentation = 0;
+
+ message = DateTime.Now.ToString( "HHmmss" ) + " | " +
+ Thread.CurrentThread.GetHashCode().ToString() + " : " +
+ source.GetHashCode().ToString( "0000000000" ) + " : " +
+ new string( ' ', m_indentation * 2 ) +
+ message + " - End";
+
+ Log.WriteLine( message );
+ }
+
+ public static void WriteLine( object source, string message )
+ {
+ if( source == null )
+ source = m_unknowSource;
+
+ message = DateTime.Now.ToString( "HHmmss" ) + " | " +
+ Thread.CurrentThread.GetHashCode().ToString() + " : " +
+ source.GetHashCode().ToString( "0000000000" ) + " : " +
+ new string( ' ', m_indentation * 2 ) +
+ message;
+
+ Log.WriteLine( message );
+ }
+
+ private static void WriteLine( string message )
+ {
+ lock( m_unknowSource )
+ {
+ using( TextWriter tw = new StreamWriter( m_path + "\\" + m_fileName + ".txt", true ) )
+ {
+ tw.WriteLine( message );
+ }
+ }
+ }
+
+ private class UnknowSource
+ {
+ public override int GetHashCode()
+ {
+ return -1;
+ }
+
+ public override bool Equals( object obj )
+ {
+ return base.Equals( obj );
+ }
+ }
+#endif
+
+ public static void NotToolkit( string featureName )
+ {
+ Console.WriteLine( Log.NotToolkitStr( featureName ) );
+ }
+
+ public static string NotToolkitStr( string featureName )
+ {
+ return string.Format( "{0} feature not available in the toolkit version of the DataGrid", featureName );
+ }
+
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/CellContentBindingExtension.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/CellContentBindingExtension.cs
new file mode 100644
index 00000000..eab09d28
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/CellContentBindingExtension.cs
@@ -0,0 +1,177 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Markup;
+using System.Windows.Data;
+using System.Windows;
+using System.Windows.Controls;
+using System.Globalization;
+using System.Collections.Specialized;
+using System.ComponentModel;
+
+namespace Xceed.Wpf.DataGrid.Markup
+{
+ [MarkupExtensionReturnType( typeof( object ) )]
+ [Obsolete( "The CellContentBinding markup extension is obsolete and has been replaced by the CellContentPresenter class.", false )]
+ [Browsable( false )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ public class CellContentBindingExtension : MarkupExtension
+ {
+ public CellContentBindingExtension()
+ {
+ m_cellBinding.RelativeSource = new RelativeSource( RelativeSourceMode.TemplatedParent );
+ m_cellBinding.Mode = BindingMode.OneWay;
+ m_cellBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
+
+ m_cellBinding.Path = new PropertyPath( Cell.ContentProperty );
+ }
+
+ #region Converter Property
+
+ public IValueConverter Converter
+ {
+ get
+ {
+ return m_cellBinding.Converter;
+ }
+ set
+ {
+ if( this.ConverterInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the Converter property when it has already been initialized." );
+
+ m_cellBinding.Converter = value;
+ this.ConverterInitialized = true;
+ }
+ }
+
+ #endregion Converter Property
+
+ #region ConverterParameter Property
+
+ public object ConverterParameter
+ {
+ get
+ {
+ return m_cellBinding.ConverterParameter;
+ }
+ set
+ {
+ if( this.ConverterParameterInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the ConverterParameter property when it has already been initialized." );
+
+ m_cellBinding.ConverterParameter = value;
+ this.ConverterParameterInitialized = true;
+ }
+ }
+
+ #endregion ConverterParameter Property
+
+ #region ConverterCulture Property
+
+ public CultureInfo ConverterCulture
+ {
+ get
+ {
+ return m_cellBinding.ConverterCulture;
+ }
+ set
+ {
+ if( this.ConverterCultureInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the ConverterCulture property when it has already been initialized." );
+
+ m_cellBinding.ConverterCulture = value;
+ this.ConverterCultureInitialized = true;
+ }
+ }
+
+ #endregion ConverterCulture Property
+
+ #region PUBLIC METHODS
+
+ public sealed override object ProvideValue( IServiceProvider serviceProvider )
+ {
+ return m_cellBinding.ProvideValue( serviceProvider );
+ }
+
+ #endregion PUBLIC METHODS
+
+ #region PRIVATE PROPERTIES
+
+ private bool ConverterCultureInitialized
+ {
+ get
+ {
+ return m_flags[ ( int )CellContentBindingExtensionFlags.ConverterCultureInitialized ];
+ }
+ set
+ {
+ m_flags[ ( int )CellContentBindingExtensionFlags.ConverterCultureInitialized ] = value;
+ }
+ }
+
+
+ private bool ConverterInitialized
+ {
+ get
+ {
+ return m_flags[ ( int )CellContentBindingExtensionFlags.ConverterInitialized ];
+ }
+ set
+ {
+ m_flags[ ( int )CellContentBindingExtensionFlags.ConverterInitialized ] = value;
+ }
+ }
+
+ private bool ConverterParameterInitialized
+ {
+ get
+ {
+ return m_flags[ ( int )CellContentBindingExtensionFlags.ConverterParameterInitialized ];
+ }
+ set
+ {
+ m_flags[ ( int )CellContentBindingExtensionFlags.ConverterParameterInitialized ] = value;
+ }
+ }
+
+ #endregion PRIVATE PROPERTIES
+
+ #region PRIVATE FIELDS
+
+ private Binding m_cellBinding = new Binding();
+ private BitVector32 m_flags = new BitVector32();
+
+ #endregion PRIVATE FIELDS
+
+ #region NESTED CLASSES
+
+ [Flags]
+ private enum CellContentBindingExtensionFlags
+ {
+ ConverterCultureInitialized = 1,
+ ConverterInitialized = 2,
+ ConverterParameterInitialized = 4
+ }
+
+ #endregion NESTED CLASSES
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/CellEditorBindingExtension.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/CellEditorBindingExtension.cs
new file mode 100644
index 00000000..634fad49
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/CellEditorBindingExtension.cs
@@ -0,0 +1,276 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+using System.Windows.Markup;
+using System.Windows.Data;
+using System.Windows;
+using System.Windows.Controls;
+
+using Xceed.Wpf.DataGrid.Converters;
+using System.Collections.Specialized;
+
+namespace Xceed.Wpf.DataGrid.Markup
+{
+ [MarkupExtensionReturnType( typeof( object ) )]
+ public class CellEditorBindingExtension : MarkupExtension
+ {
+ public CellEditorBindingExtension()
+ {
+ m_cellBinding.RelativeSource = new RelativeSource( RelativeSourceMode.Self );
+ m_cellBinding.Mode = BindingMode.TwoWay;
+ m_cellBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
+ m_cellBinding.Converter = new DefaultDataConverter();
+
+ m_cellBinding.Path = new PropertyPath(
+ "(0).(1)",
+ Cell.ParentCellProperty, Cell.ContentProperty );
+ }
+
+ #region Converter Property
+
+ public IValueConverter Converter
+ {
+ get
+ {
+ return m_cellBinding.Converter;
+ }
+ set
+ {
+ if( this.ConverterInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the Converter property when it has already been initialized." );
+
+ m_cellBinding.Converter = value;
+ this.ConverterInitialized = true;
+ }
+ }
+
+ #endregion Converter Property
+
+ #region ConverterParameter Property
+
+ public object ConverterParameter
+ {
+ get
+ {
+ return m_cellBinding.ConverterParameter;
+ }
+ set
+ {
+ if( this.ConverterParameterInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the ConverterParameter property when it has already been initialized." );
+
+ m_cellBinding.ConverterParameter = value;
+ this.ConverterParameterInitialized = true;
+ }
+ }
+
+ #endregion ConverterParameter Property
+
+ #region ConverterCulture Property
+
+ public CultureInfo ConverterCulture
+ {
+ get
+ {
+ return m_cellBinding.ConverterCulture;
+ }
+ set
+ {
+ if( this.ConverterCultureInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the ConverterCulture property when it has already been initialized." );
+
+ m_cellBinding.ConverterCulture = value;
+ this.ConverterCultureInitialized = true;
+ }
+ }
+
+ #endregion ConverterCulture Property
+
+ #region NotifyOnSourceUpdated Property
+
+ public bool NotifyOnSourceUpdated
+ {
+ get
+ {
+ return m_cellBinding.NotifyOnSourceUpdated;
+ }
+ set
+ {
+ if( this.NotifyOnSourceUpdatedInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the NotifyOnSourceUpdated property when it has already been initialized." );
+
+ m_cellBinding.NotifyOnSourceUpdated = value;
+ this.NotifyOnSourceUpdatedInitialized = true;
+ }
+ }
+
+ #endregion NotifyOnSourceUpdated Property
+
+ #region NotifyOnTargetUpdated Property
+
+ public bool NotifyOnTargetUpdated
+ {
+ get
+ {
+ return m_cellBinding.NotifyOnTargetUpdated;
+ }
+ set
+ {
+ if( this.NotifyOnTargetUpdatedInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the NotifyOnTargetUpdated property when it has already been initialized." );
+
+ m_cellBinding.NotifyOnTargetUpdated = value;
+ this.NotifyOnTargetUpdatedInitialized = true;
+ }
+ }
+
+ #endregion NotifyOnTargetUpdated Property
+
+ #region NotifyOnValidationError Property
+
+ public bool NotifyOnValidationError
+ {
+ get
+ {
+ return m_cellBinding.NotifyOnValidationError;
+ }
+ set
+ {
+ if( this.NotifyOnValidationErrorInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the NotifyOnValidationError property when it has already been initialized." );
+
+ m_cellBinding.NotifyOnValidationError = value;
+ this.NotifyOnValidationErrorInitialized = true;
+ }
+ }
+
+ #endregion NotifyOnValidationError Property
+
+ #region PUBLIC METHODS
+
+ public sealed override object ProvideValue( IServiceProvider serviceProvider )
+ {
+ return m_cellBinding.ProvideValue( serviceProvider );
+ }
+
+ #endregion PUBLIC METHODS
+
+ #region PRIVATE PROPERTIES
+
+ private bool ConverterInitialized
+ {
+ get
+ {
+ return m_flags[ ( int )CellEditorBindingExtensionFlags.ConverterInitialized ];
+ }
+ set
+ {
+ m_flags[ ( int )CellEditorBindingExtensionFlags.ConverterInitialized ] = value;
+ }
+ }
+
+ private bool ConverterParameterInitialized
+ {
+ get
+ {
+ return m_flags[ ( int )CellEditorBindingExtensionFlags.ConverterParameterInitialized ];
+ }
+ set
+ {
+ m_flags[ ( int )CellEditorBindingExtensionFlags.ConverterParameterInitialized ] = value;
+ }
+ }
+
+ private bool ConverterCultureInitialized
+ {
+ get
+ {
+ return m_flags[ ( int )CellEditorBindingExtensionFlags.ConverterCultureInitialized ];
+ }
+ set
+ {
+ m_flags[ ( int )CellEditorBindingExtensionFlags.ConverterCultureInitialized ] = value;
+ }
+ }
+
+ private bool NotifyOnSourceUpdatedInitialized
+ {
+ get
+ {
+ return m_flags[ ( int )CellEditorBindingExtensionFlags.NotifyOnSourceUpdatedInitialized ];
+ }
+ set
+ {
+ m_flags[ ( int )CellEditorBindingExtensionFlags.NotifyOnSourceUpdatedInitialized ] = value;
+ }
+ }
+
+ private bool NotifyOnTargetUpdatedInitialized
+ {
+ get
+ {
+ return m_flags[ ( int )CellEditorBindingExtensionFlags.NotifyOnTargetUpdatedInitialized ];
+ }
+ set
+ {
+ m_flags[ ( int )CellEditorBindingExtensionFlags.NotifyOnTargetUpdatedInitialized ] = value;
+ }
+ }
+
+ private bool NotifyOnValidationErrorInitialized
+ {
+ get
+ {
+ return m_flags[ ( int )CellEditorBindingExtensionFlags.NotifyOnValidationErrorInitialized ];
+ }
+ set
+ {
+ m_flags[ ( int )CellEditorBindingExtensionFlags.NotifyOnValidationErrorInitialized ] = value;
+ }
+ }
+
+ #endregion PRIVATE PROPERTIES
+
+ #region PRIVATE FIELDS
+
+ private Binding m_cellBinding = new Binding();
+ private BitVector32 m_flags = new BitVector32();
+
+ #endregion PRIVATE FIELDS
+
+ #region NESTED CLASSES
+
+ [Flags]
+ private enum CellEditorBindingExtensionFlags
+ {
+ ConverterInitialized = 1,
+ ConverterParameterInitialized = 2,
+ ConverterCultureInitialized = 4,
+ NotifyOnSourceUpdatedInitialized = 8,
+ NotifyOnTargetUpdatedInitialized = 16,
+ NotifyOnValidationErrorInitialized = 32
+ }
+
+ #endregion NESTED CLASSES
+ }
+}
diff --git a/ExtendedWPFToolkitSolution_35/Src/WPFToolkit.Extended/PropertyGrid/Implementation/Editors/DateTimeUpDownEditor.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ClearGroupLevelConfigurations.cs
similarity index 66%
rename from ExtendedWPFToolkitSolution_35/Src/WPFToolkit.Extended/PropertyGrid/Implementation/Editors/DateTimeUpDownEditor.cs
rename to ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ClearGroupLevelConfigurations.cs
index 1aafa787..c3c3cf4b 100644
--- a/ExtendedWPFToolkitSolution_35/Src/WPFToolkit.Extended/PropertyGrid/Implementation/Editors/DateTimeUpDownEditor.cs
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ClearGroupLevelConfigurations.cs
@@ -17,17 +17,17 @@
**********************************************************************/
-namespace Xceed.Wpf.Toolkit.PropertyGrid.Editors
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Xceed.Wpf.DataGrid.Views;
+using System.ComponentModel;
+
+namespace Xceed.Wpf.DataGrid.Markup
{
- public class DateTimeUpDownEditor : TypeEditor
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ [Obsolete( "The ClearGroupLevelConfigurations class is obsolete.", true )]
+ public sealed class ClearGroupLevelConfigurations : GroupLevelConfiguration
{
- protected override void SetControlProperties()
- {
- Editor.BorderThickness = new System.Windows.Thickness( 0 );
- }
- protected override void SetValueDependencyProperty()
- {
- ValueProperty = DateTimeUpDown.ValueProperty;
- }
}
}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ClearHeadersFooters.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ClearHeadersFooters.cs
new file mode 100644
index 00000000..1633d449
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ClearHeadersFooters.cs
@@ -0,0 +1,31 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Windows;
+using System.ComponentModel;
+
+namespace Xceed.Wpf.DataGrid.Markup
+{
+ [EditorBrowsable( EditorBrowsableState.Never)]
+ [Obsolete( "The ClearHeadersFooters class is obsolete and has been replaced by the ViewBase.UseDefaultHeadersFooters, GroupConfiguration.UseDefaultHeadersFooters, and DetailConfiguration.UseDefaultHeadersFooters properties.", true )]
+ public sealed class ClearHeadersFooters : DataTemplate
+ {
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ThemeConverter.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ThemeConverter.cs
new file mode 100644
index 00000000..67a0a959
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ThemeConverter.cs
@@ -0,0 +1,113 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Text;
+using Xceed.Wpf.DataGrid.Views;
+
+namespace Xceed.Wpf.DataGrid.Markup
+{
+ public class ThemeConverter : TypeConverter
+ {
+ public override bool CanConvertFrom( ITypeDescriptorContext context, Type sourceType )
+ {
+ if( sourceType == typeof( string ) )
+ return true;
+
+ return base.CanConvertFrom( context, sourceType );
+ }
+
+ public override bool CanConvertTo( ITypeDescriptorContext context, Type destinationType )
+ {
+ // We support conversion from string but we don't want to convert back to string.
+ // Otherwise, the DataGrid designer would persist in XAML using the attribute
+ // syntax which would fail for ThemePack themes.
+ if( destinationType == typeof( string ) )
+ return false;
+
+ return base.CanConvertTo( context, destinationType );
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1807:AvoidUnnecessaryStringCreation", MessageId = "stringValue" ), System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1807:AvoidUnnecessaryStringCreation", MessageId = "value" )]
+ public override object ConvertFrom( ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value )
+ {
+ string stringValue = value as string;
+
+ if( stringValue != null )
+ {
+ string themeName = stringValue.ToLowerInvariant();
+ Theme theme = null;
+
+ switch( themeName )
+ {
+ case "classic.systemcolor":
+ case "classicsystemcolortheme":
+ theme = s_classicSystemColorTheme;
+ break;
+ case "luna.normalcolor":
+ case "lunanormalcolortheme":
+ theme = s_lunaNormalColorTheme;
+ break;
+ case "luna.homestead":
+ case "lunahomesteadtheme":
+ theme = s_lunaHomesteadTheme;
+ break;
+ case "luna.metallic":
+ case "lunametallictheme":
+ theme = s_lunaMetallicTheme;
+ break;
+ case "aero.normalcolor":
+ case "aeronormalcolortheme":
+ theme = s_aeroNormalColorTheme;
+ break;
+ case "royale.normalcolor":
+ case "royalenormalcolortheme":
+ theme = s_royaleNormalColorTheme;
+ break;
+ case "zune.normalcolor":
+ case "zunenormalcolortheme":
+ theme = s_zuneNormalColorTheme;
+ break;
+ case "windows7":
+ case "windows7theme":
+ theme = s_windows7Theme;
+ break;
+ }
+
+ if( theme == null )
+ throw new ArgumentException( "The specified theme is invalid.", "value" );
+
+ return theme;
+ }
+
+ return base.ConvertFrom( context, culture, value );
+ }
+
+ private static readonly Theme s_classicSystemColorTheme = new ClassicSystemColorTheme();
+ private static readonly Theme s_lunaNormalColorTheme = new LunaNormalColorTheme();
+ private static readonly Theme s_lunaHomesteadTheme = new LunaHomesteadTheme();
+ private static readonly Theme s_lunaMetallicTheme = new LunaMetallicTheme();
+ private static readonly Theme s_aeroNormalColorTheme = new AeroNormalColorTheme();
+ private static readonly Theme s_royaleNormalColorTheme = new RoyaleNormalColorTheme();
+ private static readonly Theme s_zuneNormalColorTheme = new ZuneNormalColorTheme();
+ private static readonly Theme s_windows7Theme = new Windows7Theme();
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ThemeKey.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ThemeKey.cs
new file mode 100644
index 00000000..1c301ed7
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ThemeKey.cs
@@ -0,0 +1,174 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using System.Text;
+using System.Windows;
+using System.Windows.Markup;
+using Xceed.Wpf.DataGrid.Views;
+
+namespace Xceed.Wpf.DataGrid.Markup
+{
+ [MarkupExtensionReturnType( typeof( ThemeKey ) )]
+ public class ThemeKey : ResourceKey
+ {
+ public ThemeKey()
+ {
+ }
+
+ public ThemeKey( Type targetViewType, Type themeType, Type targetElementType )
+ {
+ if( targetViewType == null )
+ throw new ArgumentNullException( "targetViewType" );
+
+ if( !typeof( ViewBase ).IsAssignableFrom( targetViewType ) )
+ throw new ArgumentException( "The specified view type must derive from ViewBase.", "targetViewType" );
+
+ m_targetViewType = targetViewType;
+ m_targetViewTypeInitialized = true;
+ m_themeType = themeType;
+ m_themeTypeInitialized = true;
+ m_targetElementType = targetElementType;
+ m_targetElementTypeInitialized = true;
+ }
+
+ public ThemeKey( Type targetViewType, Type targetElementType )
+ : this( targetViewType, null, targetElementType )
+ {
+ }
+
+ #region TargetViewType Property
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly" )]
+ public Type TargetViewType
+ {
+ get
+ {
+ return m_targetViewType;
+ }
+ set
+ {
+ if( value == null )
+ throw new ArgumentNullException( "TargetViewType" );
+
+ if( !typeof( ViewBase ).IsAssignableFrom( value ) )
+ throw new ArgumentException( "The specified view type must derive from ViewBase.", "TargetViewType" );
+
+ if( m_targetViewTypeInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the TargetViewType property when it has already been initialized." );
+
+ m_targetViewType = value;
+ m_targetViewTypeInitialized = true;
+ }
+ }
+
+ private Type m_targetViewType; // = null;
+ private bool m_targetViewTypeInitialized; // = false;
+
+ #endregion TargetViewType Property
+
+ #region ThemeType Property
+
+ public Type ThemeType
+ {
+ get
+ {
+ return m_themeType;
+ }
+ set
+ {
+ if( m_themeTypeInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the ThemeType property when it has already been initialized." );
+
+ m_themeType = value;
+ m_themeTypeInitialized = true;
+ }
+ }
+
+ private Type m_themeType; // = null;
+ private bool m_themeTypeInitialized; // = false;
+
+ #endregion ThemeType Property
+
+ #region TargetElementType Property
+
+ public Type TargetElementType
+ {
+ get
+ {
+ return m_targetElementType;
+ }
+ set
+ {
+ if( m_targetElementTypeInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the TargetElementType property when it has already been initialized." );
+
+ m_targetElementType = value;
+ m_targetElementTypeInitialized = true;
+ }
+ }
+
+ private Type m_targetElementType; // = null;
+ private bool m_targetElementTypeInitialized; // = false;
+
+ #endregion TargetElementType Property
+
+ public override Assembly Assembly
+ {
+ get
+ {
+ if( m_themeType != null )
+ return m_themeType.Assembly;
+
+ if( m_targetViewType != null )
+ return m_targetViewType.Assembly;
+
+ return null;
+ }
+ }
+
+ public override bool Equals( object obj )
+ {
+ ThemeKey key = obj as ThemeKey;
+
+ if( key == null )
+ return false;
+
+ if( !( ( key.TargetViewType != null ) ? key.TargetViewType.Equals( this.TargetViewType ) : ( this.TargetViewType == null ) ) )
+ return false;
+
+ if( !( ( key.ThemeType != null ) ? key.ThemeType.Equals( this.ThemeType ) : ( this.ThemeType == null ) ) )
+ return false;
+
+ if( !( ( key.TargetElementType != null ) ? key.TargetElementType.Equals( this.TargetElementType ) : ( this.TargetElementType == null ) ) )
+ return false;
+
+ return true;
+ }
+
+ public override int GetHashCode()
+ {
+ return ( ( ( this.TargetViewType != null ) ? this.TargetViewType.GetHashCode() : 0 )
+ ^ ( ( this.ThemeType != null ) ? this.ThemeType.GetHashCode() : 0 )
+ ^ ( ( this.TargetElementType != null ) ? this.TargetElementType.GetHashCode() : 0 ) );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ViewBindingExtension.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ViewBindingExtension.cs
new file mode 100644
index 00000000..5f2b3d3a
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ViewBindingExtension.cs
@@ -0,0 +1,246 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Data;
+using System.Windows.Markup;
+using System.Windows;
+using System.Globalization;
+using System.Collections.Specialized;
+
+namespace Xceed.Wpf.DataGrid.Markup
+{
+ [MarkupExtensionReturnType( typeof( Binding ) )]
+ public class ViewBindingExtension : MarkupExtension
+ {
+ public ViewBindingExtension() : base()
+ {
+ }
+
+ public ViewBindingExtension( string path ) : base()
+ {
+ if( path == null )
+ throw new ArgumentNullException( "path" );
+
+ m_path = path;
+ }
+
+ #region Path Property
+
+ private string m_path; // = null;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly" )]
+ public string Path
+ {
+ get
+ {
+ return m_path;
+ }
+ set
+ {
+ if( value == null )
+ throw new ArgumentNullException( "Path" );
+
+ if( m_path != null )
+ throw new InvalidOperationException( "An attempt was made to set the Path property when it has already been initialized.s" );
+
+ m_path = value;
+ }
+ }
+
+ #endregion Path Property
+
+ #region Mode Property
+
+ private BindingMode m_bindingMode = BindingMode.OneWay;
+
+ public BindingMode Mode
+ {
+ get
+ {
+ return m_bindingMode;
+ }
+ set
+ {
+ m_bindingMode = value;
+ }
+ }
+
+ #endregion Mode Property
+
+ #region Converter Property
+
+ private IValueConverter m_converter; // = null;
+
+ public IValueConverter Converter
+ {
+ get
+ {
+ return m_converter;
+ }
+ set
+ {
+ if( this.ConverterInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the Converter property when it has already been initialized." );
+
+ m_converter = value;
+ this.ConverterInitialized = true;
+ }
+ }
+
+ #endregion Converter Property
+
+ #region ConverterParameter Property
+
+ private object m_converterParameter; // = null;
+
+ public object ConverterParameter
+ {
+ get
+ {
+ return m_converterParameter;
+ }
+ set
+ {
+ if( this.ConverterParameterInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the ConverterParameter property when it has already been initialized." );
+
+ m_converterParameter = value;
+ this.ConverterParameterInitialized = true;
+ }
+ }
+
+ #endregion ConverterParameter Property
+
+ #region ConverterCulture Property
+
+ private CultureInfo m_converterCulture; // = null;
+
+ public CultureInfo ConverterCulture
+ {
+ get
+ {
+ return m_converterCulture;
+ }
+ set
+ {
+ if( this.ConverterCultureInitialized )
+ throw new InvalidOperationException( "An attempt was made to set the ConverterCulture property when it has already been initialized." );
+
+ m_converterCulture = value;
+ this.ConverterCultureInitialized = true;
+ }
+ }
+
+ #endregion ConverterCulture Property
+
+ #region PUBLIC METHODS
+
+ public override object ProvideValue( IServiceProvider serviceProvider )
+ {
+ return this.CreateBinding();
+ }
+
+ #endregion PUBLIC METHODS
+
+ #region PRIVATE METHODS
+
+ private Binding CreateBinding()
+ {
+ if( m_path == null )
+ throw new InvalidOperationException( "An attempt was made to create a binding without a Path." );
+
+ Binding binding;
+
+ binding = new Binding();
+ binding.Path = new PropertyPath( "(0)." + m_path, DataGridControl.DataGridContextProperty );
+ binding.RelativeSource = new RelativeSource( RelativeSourceMode.Self );
+
+ binding.Mode = m_bindingMode;
+ binding.Converter = m_converter;
+ binding.ConverterParameter = m_converterParameter;
+ binding.ConverterCulture = m_converterCulture;
+
+ return binding;
+ }
+
+ #endregion PRIVATE METHODS
+
+ #region PRIVATE PROPERTIES
+
+ private bool ConverterCultureInitialized
+ {
+ get
+ {
+ return m_flags[ ( int )ViewBindingExtensionFlags.ConverterCultureInitialized ];
+ }
+ set
+ {
+ m_flags[ ( int )ViewBindingExtensionFlags.ConverterCultureInitialized ] = value;
+ }
+ }
+
+
+ private bool ConverterInitialized
+ {
+ get
+ {
+ return m_flags[ ( int )ViewBindingExtensionFlags.ConverterInitialized ];
+ }
+ set
+ {
+ m_flags[ ( int )ViewBindingExtensionFlags.ConverterInitialized ] = value;
+ }
+ }
+
+ private bool ConverterParameterInitialized
+ {
+ get
+ {
+ return m_flags[ ( int )ViewBindingExtensionFlags.ConverterParameterInitialized ];
+ }
+ set
+ {
+ m_flags[ ( int )ViewBindingExtensionFlags.ConverterParameterInitialized ] = value;
+ }
+ }
+
+ #endregion PRIVATE PROPERTIES
+
+ #region PRIVATE FIELDS
+
+ private BitVector32 m_flags = new BitVector32();
+
+ #endregion PRIVATE FIELDS
+
+ #region NESTED CLASSES
+
+ [Flags]
+ private enum ViewBindingExtensionFlags
+ {
+ ConverterCultureInitialized = 1,
+ ConverterInitialized = 2,
+ ConverterParameterInitialized = 4
+ }
+
+ #endregion NESTED CLASSES
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ViewConverter.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ViewConverter.cs
new file mode 100644
index 00000000..fa202fe5
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Markup/ViewConverter.cs
@@ -0,0 +1,106 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.ComponentModel;
+using System.Globalization;
+using Xceed.Wpf.DataGrid.Views;
+
+namespace Xceed.Wpf.DataGrid.Markup
+{
+ public class ViewConverter : TypeConverter
+ {
+ public override bool CanConvertFrom( ITypeDescriptorContext context, Type sourceType )
+ {
+ if( sourceType == typeof( string ) )
+ return true;
+
+ return base.CanConvertFrom( context, sourceType );
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1807:AvoidUnnecessaryStringCreation", MessageId = "viewName" )]
+ public override object ConvertFrom( ITypeDescriptorContext context, CultureInfo culture, object value )
+ {
+ string stringValue = value as string;
+
+ if( stringValue != null )
+ {
+ ViewBase toReturn = null;
+
+ string completeName = stringValue;
+ int pointIndex = completeName.IndexOf( "." );
+ string viewName = string.Empty;
+ string themeName = string.Empty;
+
+ if(pointIndex != -1)
+ {
+ viewName = completeName.Substring( 0, pointIndex );
+ themeName = completeName.Substring( pointIndex + 1 );
+ }
+ else
+ {
+ viewName = completeName;
+ }
+
+ viewName = viewName.ToLowerInvariant();
+
+ switch( viewName )
+ {
+ case "tableview":
+ toReturn = new TableView();
+ break;
+ case "tableflowview":
+ toReturn = new TableflowView();
+ break;
+ }
+
+ if( toReturn == null )
+ {
+ throw new ArgumentException( "The specified view is invalid.", "value" );
+ }
+ else
+ {
+ if( !string.IsNullOrEmpty( themeName ) )
+ {
+ ThemeConverter themeConverter = new ThemeConverter();
+ toReturn.Theme = (Theme)themeConverter.ConvertFromString(themeName);
+ }
+ }
+
+ return toReturn;
+ }
+
+ return base.ConvertFrom( context, culture, value );
+ }
+
+ public override object ConvertTo( ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType )
+ {
+ if( ( destinationType != null ) && ( value is ViewBase ) )
+ {
+ if( destinationType == typeof( string ) )
+ {
+ return value.GetType().Name;
+ }
+ }
+
+ return base.ConvertTo( context, culture, value, destinationType );
+ }
+
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/MaxGroupLevelsChangedEventManager.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/MaxGroupLevelsChangedEventManager.cs
new file mode 100644
index 00000000..cd79b235
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/MaxGroupLevelsChangedEventManager.cs
@@ -0,0 +1,103 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class MaxGroupLevelsChangedEventManager : WeakEventManager
+ {
+ private MaxGroupLevelsChangedEventManager()
+ {
+ }
+
+ public static void AddListener( object source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedAddListener( source, listener );
+ }
+
+ public static void RemoveListener( object source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedRemoveListener( source, listener );
+ }
+
+ protected override void StartListening( object source )
+ {
+ DataGridControl dataGridControl = source as DataGridControl;
+ if( dataGridControl != null )
+ {
+ dataGridControl.MaxGroupLevelsChanged += new EventHandler( this.OnMaxGroupLevelsChanged );
+ return;
+ }
+
+ DetailConfiguration detailConfiguration = source as DetailConfiguration;
+ if( detailConfiguration != null )
+ {
+ detailConfiguration.MaxGroupLevelsChanged += new EventHandler( this.OnMaxGroupLevelsChanged );
+ return;
+ }
+
+ throw new ArgumentException( "The specified source must be a DataGridControl or a DetailConfiguration.", "source" );
+ }
+
+ protected override void StopListening( object source )
+ {
+ DataGridControl dataGridControl = source as DataGridControl;
+ if( dataGridControl != null )
+ {
+ dataGridControl.MaxGroupLevelsChanged -= new EventHandler( this.OnMaxGroupLevelsChanged );
+ return;
+ }
+
+ DetailConfiguration detailConfiguration = source as DetailConfiguration;
+ if( detailConfiguration != null )
+ {
+ detailConfiguration.MaxGroupLevelsChanged -= new EventHandler( this.OnMaxGroupLevelsChanged );
+ return;
+ }
+
+ throw new ArgumentException( "The specified source must be a DataGridControl or a DetailConfiguration.", "source" );
+ }
+
+ private static MaxGroupLevelsChangedEventManager CurrentManager
+ {
+ get
+ {
+ Type managerType = typeof( MaxGroupLevelsChangedEventManager );
+ MaxGroupLevelsChangedEventManager currentManager = ( MaxGroupLevelsChangedEventManager )WeakEventManager.GetCurrentManager( managerType );
+
+ if( currentManager == null )
+ {
+ currentManager = new MaxGroupLevelsChangedEventManager();
+ WeakEventManager.SetCurrentManager( managerType, currentManager );
+ }
+
+ return currentManager;
+ }
+ }
+
+ private void OnMaxGroupLevelsChanged( object sender, EventArgs args )
+ {
+ this.DeliverEvent( sender, args );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/MaxSortLevelsChangedEventManager.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/MaxSortLevelsChangedEventManager.cs
new file mode 100644
index 00000000..9b671922
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/MaxSortLevelsChangedEventManager.cs
@@ -0,0 +1,103 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class MaxSortLevelsChangedEventManager : WeakEventManager
+ {
+ private MaxSortLevelsChangedEventManager()
+ {
+ }
+
+ public static void AddListener( object source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedAddListener( source, listener );
+ }
+
+ public static void RemoveListener( object source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedRemoveListener( source, listener );
+ }
+
+ protected override void StartListening( object source )
+ {
+ DataGridControl dataGridControl = source as DataGridControl;
+ if( dataGridControl != null )
+ {
+ dataGridControl.MaxSortLevelsChanged += new EventHandler( this.OnMaxSortLevelsChanged );
+ return;
+ }
+
+ DetailConfiguration detailConfiguration = source as DetailConfiguration;
+ if( detailConfiguration != null )
+ {
+ detailConfiguration.MaxSortLevelsChanged += new EventHandler( this.OnMaxSortLevelsChanged );
+ return;
+ }
+
+ throw new ArgumentException( "The specified source must be a DataGridControl or a DetailConfiguration.", "source" );
+ }
+
+ protected override void StopListening( object source )
+ {
+ DataGridControl dataGridControl = source as DataGridControl;
+ if( dataGridControl != null )
+ {
+ dataGridControl.MaxSortLevelsChanged -= new EventHandler( this.OnMaxSortLevelsChanged );
+ return;
+ }
+
+ DetailConfiguration detailConfiguration = source as DetailConfiguration;
+ if( detailConfiguration != null )
+ {
+ detailConfiguration.MaxSortLevelsChanged -= new EventHandler( this.OnMaxSortLevelsChanged );
+ return;
+ }
+
+ throw new ArgumentException( "The specified source must be a DataGridControl or a DetailConfiguration.", "source" );
+ }
+
+ private static MaxSortLevelsChangedEventManager CurrentManager
+ {
+ get
+ {
+ Type managerType = typeof( MaxSortLevelsChangedEventManager );
+ MaxSortLevelsChangedEventManager currentManager = ( MaxSortLevelsChangedEventManager )WeakEventManager.GetCurrentManager( managerType );
+
+ if( currentManager == null )
+ {
+ currentManager = new MaxSortLevelsChangedEventManager();
+ WeakEventManager.SetCurrentManager( managerType, currentManager );
+ }
+
+ return currentManager;
+ }
+ }
+
+ private void OnMaxSortLevelsChanged( object sender, EventArgs args )
+ {
+ this.DeliverEvent( sender, args );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/NoDrop.cur b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/NoDrop.cur
new file mode 100644
index 00000000..7df3b590
Binary files /dev/null and b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/NoDrop.cur differ
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ObjectComparer.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ObjectComparer.cs
new file mode 100644
index 00000000..fdae61d7
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ObjectComparer.cs
@@ -0,0 +1,76 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Collections;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class ObjectComparer : IComparer
+ {
+ private ObjectComparer()
+ {
+ }
+
+ #region Singleton Property
+
+ public static ObjectComparer Singleton
+ {
+ get
+ {
+ if( _singleton == null )
+ _singleton = new ObjectComparer();
+
+ return _singleton;
+ }
+ }
+
+ private static ObjectComparer _singleton;
+
+ #endregion Singleton Property
+
+ public int Compare( object xData, object yData )
+ {
+ // Code in there should be indentical to ObjectDataStore.CompareData
+
+ if( ( xData == null ) || ( xData == DBNull.Value ) )
+ {
+ if( ( yData != null ) && ( yData != DBNull.Value ) )
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ if( ( yData == null ) || ( yData == DBNull.Value ) )
+ return 1;
+
+ IComparable xDataComparer = xData as IComparable;
+
+ if( xDataComparer != null )
+ return xDataComparer.CompareTo( yData );
+ }
+
+ return 0;
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Print/IPrintInfo.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Print/IPrintInfo.cs
new file mode 100644
index 00000000..ca5cff43
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Print/IPrintInfo.cs
@@ -0,0 +1,32 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System.Collections.Generic;
+
+namespace Xceed.Wpf.DataGrid.Print
+{
+ internal interface IPrintInfo
+ {
+ double GetPageRightOffset( double horizontalOffset, double viewportWidth );
+
+ void UpdateElementVisibility( double horizontalOffset, double viewportWidth, object state );
+
+ object CreateElementVisibilityState();
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Properties/AssemblyInfo.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Properties/AssemblyInfo.cs
new file mode 100644
index 00000000..50f729aa
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Properties/AssemblyInfo.cs
@@ -0,0 +1,85 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+#region Using directives
+
+using System;
+using System.Globalization;
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Windows;
+using System.Windows.Markup;
+
+#endregion
+
+// 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( "Xceed DataGrid for WPF" )]
+[assembly: AssemblyDescription( "This assembly implements the Xceed.Wpf.DataGrid namespace, a data grid control for the Windows Presentation Framework." )]
+
+[assembly: AssemblyCompany( "Xceed Software Inc." )]
+[assembly: AssemblyProduct( "Xceed DataGrid for WPF" )]
+[assembly: AssemblyCopyright( "Copyright � Xceed Software Inc. 2007-2011" )]
+
+
+[assembly: CLSCompliant( true )]
+[assembly: ComVisible( false )]
+
+[assembly: XmlnsPrefix( "http://schemas.xceed.com/wpf/xaml/datagrid", "xcdg" )]
+[assembly: XmlnsDefinition( "http://schemas.xceed.com/wpf/xaml/datagrid", "Xceed.Wpf.DataGrid")]
+[assembly: XmlnsDefinition( "http://schemas.xceed.com/wpf/xaml/datagrid", "Xceed.Wpf.DataGrid.Converters")]
+[assembly: XmlnsDefinition( "http://schemas.xceed.com/wpf/xaml/datagrid", "Xceed.Wpf.DataGrid.Markup")]
+[assembly: XmlnsDefinition( "http://schemas.xceed.com/wpf/xaml/datagrid", "Xceed.Wpf.DataGrid.Views")]
+[assembly: XmlnsDefinition( "http://schemas.xceed.com/wpf/xaml/datagrid", "Xceed.Wpf.DataGrid.Print")]
+[assembly: XmlnsDefinition( "http://schemas.xceed.com/wpf/xaml/datagrid", "Xceed.Wpf.DataGrid.Stats")]
+[assembly: XmlnsDefinition( "http://schemas.xceed.com/wpf/xaml/datagrid", "Xceed.Wpf.DataGrid.Automation")]
+[assembly: XmlnsDefinition( "http://schemas.xceed.com/wpf/xaml/datagrid", "Xceed.Wpf.DataGrid.ValidationRules")]
+[assembly: XmlnsDefinition( "http://schemas.xceed.com/wpf/xaml/datagrid", "Xceed.Wpf.DataGrid.FilterCriteria")]
+
+
+[assembly: SecurityRules( SecurityRuleSet.Level1 )]
+[assembly: AllowPartiallyTrustedCallers]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the 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.SourceAssembly, //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)
+)]
+
+#pragma warning disable 1699
+[assembly: AssemblyDelaySign( false )]
+[assembly: AssemblyKeyFile( @"sn.snk" )]
+[assembly: AssemblyKeyName( "" )]
+#pragma warning restore 1699
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ReadOnlyColumnCollection.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ReadOnlyColumnCollection.cs
new file mode 100644
index 00000000..e5bd6869
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ReadOnlyColumnCollection.cs
@@ -0,0 +1,66 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Text;
+using System.Collections.Specialized;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class ReadOnlyColumnCollection : ReadOnlyObservableCollection
+ {
+ public ReadOnlyColumnCollection()
+ : base( new ObservableCollection() )
+ {
+ }
+
+ internal void RaiseItemChanged( ColumnBase column )
+ {
+ this.OnCollectionChanged(
+ new NotifyCollectionChangedEventArgs( NotifyCollectionChangedAction.Replace, column, column, this.IndexOf( column ) ) );
+ }
+
+ internal void InternalClear()
+ {
+ this.Items.Clear();
+ }
+
+ internal void InternalAdd( ColumnBase column )
+ {
+ this.Items.Add( column );
+ }
+
+ internal void InternalInsert( int index, ColumnBase column )
+ {
+ this.Items.Insert( index, column );
+ }
+
+ internal bool InternalRemove( ColumnBase column )
+ {
+ return this.Items.Remove( column );
+ }
+
+ internal void InternalRemoveAt( int index )
+ {
+ this.Items.RemoveAt( index );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RecyclingManager.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RecyclingManager.cs
new file mode 100644
index 00000000..d1acc100
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RecyclingManager.cs
@@ -0,0 +1,189 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System.Collections.Generic;
+using System.Windows;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class RecyclingManager
+ {
+ public void EnqueueItemContainer( DependencyObject container )
+ {
+ m_itemRecyclingStack.Push( container );
+ }
+
+ public DependencyObject DequeueItemContainer()
+ {
+ if( m_itemRecyclingStack.Count > 0 )
+ return m_itemRecyclingStack.Pop();
+
+ return null;
+ }
+
+ public void EnqueueHeaderFooterContainer( object key, DependencyObject container )
+ {
+ Stack headerFooterItemTypeStack;
+
+ if( !m_headerRecyclingStacks.TryGetValue( key, out headerFooterItemTypeStack ) )
+ {
+ headerFooterItemTypeStack = new Stack();
+ m_headerRecyclingStacks.Add( key, headerFooterItemTypeStack );
+ }
+
+ headerFooterItemTypeStack.Push( container );
+ }
+
+ public DependencyObject DequeueHeaderFooterContainer( object key )
+ {
+ Stack headerFooterItemTypeStack;
+
+ if( m_headerRecyclingStacks.TryGetValue( key, out headerFooterItemTypeStack ) )
+ {
+ if( headerFooterItemTypeStack.Count > 0 )
+ return headerFooterItemTypeStack.Pop();
+ }
+
+ return null;
+ }
+
+ public void EnqueueGroupHeaderFooterContainer( object key, object item, DependencyObject container )
+ {
+ Dictionary> headerFooterRecyclingStacks;
+
+ if( !m_groupLevelRecyclingStacks.TryGetValue( key, out headerFooterRecyclingStacks ) )
+ {
+ headerFooterRecyclingStacks = new Dictionary>();
+ m_groupLevelRecyclingStacks.Add( key, headerFooterRecyclingStacks );
+ }
+
+ if( item is GroupHeaderFooterItem )
+ {
+ item = ( ( GroupHeaderFooterItem )item ).Template;
+ }
+
+ Stack headerFooterItemTypeStack;
+
+ if( !headerFooterRecyclingStacks.TryGetValue( item, out headerFooterItemTypeStack ) )
+ {
+ headerFooterItemTypeStack = new Stack();
+ headerFooterRecyclingStacks.Add( item, headerFooterItemTypeStack );
+ }
+
+ headerFooterItemTypeStack.Push( container );
+ }
+
+ public DependencyObject DequeueGroupHeaderFooterContainer( object key, object item )
+ {
+
+ Dictionary> headerFooterRecyclingStacks;
+
+ if( m_groupLevelRecyclingStacks.TryGetValue( key, out headerFooterRecyclingStacks ) )
+ {
+ if( item is GroupHeaderFooterItem )
+ {
+ item = ( ( GroupHeaderFooterItem )item ).Template;
+ }
+
+ Stack headerFooterItemTypeStack;
+
+ if( headerFooterRecyclingStacks.TryGetValue( item, out headerFooterItemTypeStack ) )
+ {
+ if( headerFooterItemTypeStack.Count > 0 )
+ return headerFooterItemTypeStack.Pop();
+ }
+ }
+
+ return null;
+ }
+
+ public IList Clear()
+ {
+ List removedContainers = new List();
+
+ foreach( DependencyObject container in m_itemRecyclingStack )
+ {
+ removedContainers.Add( container );
+ }
+
+ m_itemRecyclingStack.Clear();
+
+ foreach( Stack stack in m_headerRecyclingStacks.Values )
+ {
+ foreach( DependencyObject container in stack )
+ {
+ removedContainers.Add( container );
+ }
+
+ stack.Clear();
+ }
+
+ m_headerRecyclingStacks.Clear();
+
+ foreach( Dictionary> stacks in m_groupLevelRecyclingStacks.Values )
+ {
+ foreach( Stack stack in stacks.Values )
+ {
+ foreach( DependencyObject container in stack )
+ {
+ removedContainers.Add( container );
+ }
+
+ stack.Clear();
+ }
+
+ stacks.Clear();
+ }
+
+ m_groupLevelRecyclingStacks.Clear();
+
+ return removedContainers;
+ }
+
+ public void AddRef( CustomItemContainerGenerator reference )
+ {
+ if( m_refList.Contains( reference ) == false )
+ {
+ m_refList.Add( reference );
+ }
+ }
+
+ public void RemoveRef( CustomItemContainerGenerator reference )
+ {
+ if( m_refList.Contains( reference ) == true )
+ {
+ m_refList.Remove( reference );
+ }
+ }
+
+ public int RefCount
+ {
+ get
+ {
+ return m_refList.Count;
+ }
+ }
+
+ private readonly Dictionary>> m_groupLevelRecyclingStacks = new Dictionary>>();
+ private readonly Dictionary> m_headerRecyclingStacks = new Dictionary>();
+ private readonly Stack m_itemRecyclingStack = new Stack();
+ private readonly List m_refList = new List();
+
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Row.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Row.cs
new file mode 100644
index 00000000..ef1e0a9f
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/Row.cs
@@ -0,0 +1,2857 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+using System.ComponentModel;
+using System.Diagnostics;
+using System.Reflection;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Input;
+using System.Windows.Markup;
+using System.Windows.Media;
+using System.Security;
+using System.Security.Permissions;
+using System.Windows.Automation.Peers;
+
+using Xceed.Wpf.DataGrid.Print;
+using Xceed.Wpf.DataGrid.ValidationRules;
+using Xceed.Wpf.DataGrid.Views;
+using Xceed.Utils.Wpf;
+using Xceed.Wpf.DataGrid.Export;
+using Xceed.Wpf.DataGrid.Automation;
+using System.Windows.Threading;
+using System.Windows.Media.Animation;
+
+namespace Xceed.Wpf.DataGrid
+{
+ [TemplatePart( Name = "PART_CellsHost", Type = typeof( Panel ) )]
+ [TemplatePart( Name = "PART_RowFocusRoot", Type = typeof( FrameworkElement ) )]
+ [ContentProperty( "Cells" )]
+ public abstract class Row : Control, IPrintInfo, IDataGridItemContainer
+ {
+ // This validation rule is meant to be used as the rule in error set in a Row's ValidationError when
+ // we want to flag a validation error even though no binding's ValidationRules or CellValidationRules are invalid.
+ // ie: Row EditEnding event throws or returns with e.Cancel set to True, IEditableObject's EndEdit throws.
+ private static readonly ValidationRule CustomRowValidationExceptionValidationRule = new ExceptionValidationRule();
+
+ static Row()
+ {
+ // We need to override the default Validation ErrorTemplate, else the default adorner will appear
+ // around the row when the main column's cell has a Validation error.
+ // We handle validation errors at the cell level by using error styles, not through Validation.ErrorTemplate.
+ Validation.ErrorTemplateProperty.OverrideMetadata( typeof( Xceed.Wpf.DataGrid.Row ),
+ new FrameworkPropertyMetadata( null, FrameworkPropertyMetadataOptions.OverridesInheritanceBehavior ) );
+
+ Row.IsCurrentProperty = Row.IsCurrentPropertyKey.DependencyProperty;
+ Row.CellsProperty = Row.CellsPropertyKey.DependencyProperty;
+ Row.IsBeingEditedProperty = Row.IsBeingEditedPropertyKey.DependencyProperty;
+ Row.IsDirtyProperty = Row.IsDirtyPropertyKey.DependencyProperty;
+ Row.IsSelectedProperty = Row.IsSelectedPropertyKey.DependencyProperty;
+
+ DataGridControl.ParentDataGridControlPropertyKey.OverrideMetadata( typeof( Row ),
+ new FrameworkPropertyMetadata( new PropertyChangedCallback( OnParentGridControlChanged ) ) );
+
+ DataGridControl.CellEditorDisplayConditionsProperty.OverrideMetadata( typeof( Row ),
+ new FrameworkPropertyMetadata( new PropertyChangedCallback( OnCellEditorDisplayConditionsChanged ) ) );
+
+ // We do this last to ensure all the Static content of the row is done before using
+ // any static readonly field of the cell.
+ EventManager.RegisterClassHandler( typeof( Row ), Cell.IsDirtyEvent, new RoutedEventHandler( OnCellIsDirty_ClassHandler ) );
+ Row.IsDirtyEvent = Cell.IsDirtyEvent.AddOwner( typeof( Row ) );
+ }
+
+ protected Row()
+ {
+ this.CommandBindings.Add( new CommandBinding( DataGridCommands.BeginEdit,
+ new ExecutedRoutedEventHandler( OnBeginEditExecuted ),
+ new CanExecuteRoutedEventHandler( OnBeginEditCanExecute ) ) );
+
+ this.CommandBindings.Add( new CommandBinding( DataGridCommands.EndEdit,
+ new ExecutedRoutedEventHandler( OnEndEditExecuted ),
+ new CanExecuteRoutedEventHandler( OnEndEditCanExecute ) ) );
+
+ this.CommandBindings.Add( new CommandBinding( DataGridCommands.CancelEdit,
+ new ExecutedRoutedEventHandler( OnCancelEditExecuted ),
+ new CanExecuteRoutedEventHandler( OnCancelEditCanExecute ) ) );
+
+ // Cache the CellsCollection and always return it in the
+ // getter of the DependencyProperty's CLR accessor
+ m_cellsCache = new VirtualizingCellCollection( this );
+
+ // Set the Value of the DependencyProperty
+ this.SetValue( Row.CellsPropertyKey, m_cellsCache );
+ }
+
+ #region NavigationBehavior Property
+
+ public static readonly DependencyProperty NavigationBehaviorProperty = DataGridControl.NavigationBehaviorProperty.AddOwner(
+ typeof( Row ),
+ new FrameworkPropertyMetadata( NavigationBehavior.CellOnly, FrameworkPropertyMetadataOptions.Inherits,
+ new PropertyChangedCallback( OnNavigationBehaviorChanged ) ) );
+
+ public NavigationBehavior NavigationBehavior
+ {
+ get
+ {
+ return ( NavigationBehavior )this.GetValue( Row.NavigationBehaviorProperty );
+ }
+ set
+ {
+ this.SetValue( Row.NavigationBehaviorProperty, value );
+ }
+ }
+
+ #endregion NavigationBehavior Property
+
+ #region Cells Read-Only Property
+
+ private static readonly DependencyPropertyKey CellsPropertyKey =
+ DependencyProperty.RegisterReadOnly( "Cells",
+ typeof( CellCollection ),
+ typeof( Row ),
+ new PropertyMetadata( null ) );
+
+ public static readonly DependencyProperty CellsProperty;
+
+ // Define it as Virtualizing to avoid casts
+ private VirtualizingCellCollection m_cellsCache; // = null;
+
+ public CellCollection Cells
+ {
+ get
+ {
+ return m_cellsCache;
+ }
+ }
+
+ #endregion Cells Read-Only Property
+
+ #region CellEditorDisplayConditions Property
+
+ public CellEditorDisplayConditions CellEditorDisplayConditions
+ {
+ get
+ {
+ return ( CellEditorDisplayConditions )this.GetValue( DataGridControl.CellEditorDisplayConditionsProperty );
+ }
+ set
+ {
+ this.SetValue( DataGridControl.CellEditorDisplayConditionsProperty, value );
+ }
+ }
+
+ #endregion CellEditorDisplayConditions Property
+
+ #region CellErrorStyle Property
+
+ public static readonly DependencyProperty CellErrorStyleProperty =
+ DataGridControl.CellErrorStyleProperty.AddOwner( typeof( Row ) );
+
+ public Style CellErrorStyle
+ {
+ get
+ {
+ return ( Style )this.GetValue( Row.CellErrorStyleProperty );
+ }
+
+ set
+ {
+ this.SetValue( Row.CellErrorStyleProperty, value );
+ }
+ }
+
+ #endregion CellErrorStyle Property
+
+ #region CellContentOpacity Internal Property
+
+ internal static readonly DependencyProperty CellContentOpacityProperty = DependencyProperty.RegisterAttached(
+ "CellContentOpacity",
+ typeof( double ),
+ typeof( Row ),
+ new FrameworkPropertyMetadata( 1d ) );
+
+ internal static double GetCellContentOpacity( DependencyObject obj )
+ {
+ return ( double )obj.GetValue( Row.CellContentOpacityProperty );
+ }
+ internal static void SetCellContentOpacity( DependencyObject obj, double value )
+ {
+ obj.SetValue( Row.CellContentOpacityProperty, value );
+ }
+
+ #endregion CellContentOpacity Internal Property
+
+ #region EditTriggers Property
+
+ public static readonly DependencyProperty EditTriggersProperty = DataGridControl.EditTriggersProperty.AddOwner( typeof( Row ) );
+
+ public EditTriggers EditTriggers
+ {
+ get
+ {
+ return ( EditTriggers )this.GetValue( Row.EditTriggersProperty );
+ }
+ set
+ {
+ this.SetValue( Row.EditTriggersProperty, value );
+ }
+ }
+
+ #endregion EditTriggers Property
+
+ #region HasValidationError Read-Only Property
+
+ internal static readonly DependencyPropertyKey HasValidationErrorPropertyKey =
+ DependencyProperty.RegisterReadOnly( "HasValidationError", typeof( bool ), typeof( Row ), new UIPropertyMetadata( false ) );
+
+ public static readonly DependencyProperty HasValidationErrorProperty =
+ HasValidationErrorPropertyKey.DependencyProperty;
+
+ public bool HasValidationError
+ {
+ get
+ {
+ return ( bool )this.GetValue( Row.HasValidationErrorProperty );
+ }
+ }
+
+ internal void SetHasValidationError( bool value )
+ {
+ if( value != this.HasValidationError )
+ {
+ if( value )
+ {
+ this.SetValue( Row.HasValidationErrorPropertyKey, value );
+ }
+ else
+ {
+ this.SetValue( Row.HasValidationErrorPropertyKey, DependencyProperty.UnsetValue );
+ }
+ }
+ }
+
+ #endregion HasValidationError Read-Only Property
+
+ #region IsValidationErrorRestrictive Read-Only Property
+
+ private static readonly DependencyPropertyKey IsValidationErrorRestrictivePropertyKey =
+ DependencyProperty.RegisterReadOnly( "IsValidationErrorRestrictive", typeof( bool ), typeof( Row ), new UIPropertyMetadata( false ) );
+
+ public static readonly DependencyProperty IsValidationErrorRestrictiveProperty =
+ IsValidationErrorRestrictivePropertyKey.DependencyProperty;
+
+ public bool IsValidationErrorRestrictive
+ {
+ get
+ {
+ return ( bool )this.GetValue( Row.IsValidationErrorRestrictiveProperty );
+ }
+ }
+
+ internal void SetIsValidationErrorRestrictive( bool value )
+ {
+ if( value != this.IsValidationErrorRestrictive )
+ {
+ if( value )
+ {
+ this.SetValue( Row.IsValidationErrorRestrictivePropertyKey, value );
+ }
+ else
+ {
+ this.SetValue( Row.IsValidationErrorRestrictivePropertyKey, DependencyProperty.UnsetValue );
+ }
+ }
+ }
+
+ #endregion IsValidationErrorRestrictive Read-Only Property
+
+ #region IsBeingEdited Read-Only Property
+
+ internal static readonly DependencyPropertyKey IsBeingEditedPropertyKey =
+ DependencyProperty.RegisterReadOnly( "IsBeingEdited", typeof( bool ), typeof( Row ), new PropertyMetadata( false, new PropertyChangedCallback( OnIsBeingEditedChanged ) ) );
+
+ public static readonly DependencyProperty IsBeingEditedProperty;
+
+ public bool IsBeingEdited
+ {
+ get
+ {
+ return this.IsBeingEditedCache;
+ }
+ }
+
+ private void SetIsBeingEdited( bool value )
+ {
+ if( value != this.IsBeingEdited )
+ {
+ this.IsBeingEditedCache = value;
+
+ if( value )
+ {
+ this.SetValue( Row.IsBeingEditedPropertyKey, value );
+ }
+ else
+ {
+ this.SetValue( Row.IsBeingEditedPropertyKey, DependencyProperty.UnsetValue );
+ }
+ }
+ }
+
+ #endregion IsBeingEdited Read-Only Property
+
+ #region IsDirty Read-Only Property
+
+ private static readonly DependencyPropertyKey IsDirtyPropertyKey =
+ DependencyProperty.RegisterReadOnly( "IsDirty", typeof( bool ), typeof( Row ), new PropertyMetadata( false ) );
+
+ public static readonly DependencyProperty IsDirtyProperty;
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields" )]
+ internal static RoutedEvent IsDirtyEvent; // = null;
+
+ public bool IsDirty
+ {
+ get
+ {
+ return ( bool )this.GetValue( Row.IsDirtyProperty );
+ }
+ }
+
+ private void SetIsDirty( bool value )
+ {
+ if( value != this.IsDirty )
+ {
+ if( value )
+ {
+ this.SetValue( Row.IsDirtyPropertyKey, value );
+ }
+ else
+ {
+ this.SetValue( Row.IsDirtyPropertyKey, DependencyProperty.UnsetValue );
+ }
+
+ if( this.IsBeingEdited )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl gridControl = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ if( gridControl != null )
+ {
+ RowState rowState = gridControl.CurrentRowInEditionState;
+ Debug.Assert( rowState != null );
+
+ if( rowState != null )
+ rowState.SetIsDirty( value );
+ }
+ }
+ }
+ }
+
+ #endregion IsDirty Read-Only Property
+
+ #region RowDisplayEditorMatchingConditions Property
+
+ internal static readonly DependencyProperty RowDisplayEditorMatchingConditionsProperty =
+ DependencyProperty.Register( "RowDisplayEditorMatchingConditions", typeof( CellEditorDisplayConditions ), typeof( Row ), new FrameworkPropertyMetadata( CellEditorDisplayConditions.None ) );
+
+ internal void SetDisplayEditorMatchingCondition( CellEditorDisplayConditions condition )
+ {
+ CellEditorDisplayConditions previousValue = ( CellEditorDisplayConditions )this.GetValue( Row.RowDisplayEditorMatchingConditionsProperty );
+
+ previousValue = previousValue | condition;
+
+ this.SetValue( Row.RowDisplayEditorMatchingConditionsProperty, previousValue );
+ }
+
+ internal void RemoveDisplayEditorMatchingCondition( CellEditorDisplayConditions condition )
+ {
+ CellEditorDisplayConditions previousValue = ( CellEditorDisplayConditions )this.GetValue( Row.RowDisplayEditorMatchingConditionsProperty );
+
+ previousValue = previousValue & ~condition;
+
+ this.SetValue( Row.RowDisplayEditorMatchingConditionsProperty, previousValue );
+ }
+
+ #endregion RowDisplayEditorMatchingConditions Property
+
+ #region ReadOnly Property
+
+ public static readonly DependencyProperty ReadOnlyProperty =
+ DataGridControl.ReadOnlyProperty.AddOwner( typeof( Row ) );
+
+ public bool ReadOnly
+ {
+ get
+ {
+ return ( bool )this.GetValue( Row.ReadOnlyProperty );
+ }
+ set
+ {
+ this.SetValue( Row.ReadOnlyProperty, value );
+ }
+ }
+
+ #endregion ReadOnly Property
+
+ #region IsCurrent Read-Only Property
+
+ internal static readonly DependencyPropertyKey IsCurrentPropertyKey =
+ DependencyProperty.RegisterReadOnly( "IsCurrent", typeof( bool ), typeof( Row ), new UIPropertyMetadata( false, new PropertyChangedCallback( Row.OnIsCurrentChanged ) ) );
+
+ public static readonly DependencyProperty IsCurrentProperty;
+
+ public bool IsCurrent
+ {
+ get
+ {
+ return ( bool )this.GetValue( Row.IsCurrentProperty );
+ }
+ }
+
+ internal void SetIsCurrent( bool value )
+ {
+ if( value )
+ {
+ this.SetValue( Row.IsCurrentPropertyKey, true );
+ }
+ else
+ {
+ this.SetValue( Row.IsCurrentPropertyKey, DependencyProperty.UnsetValue );
+ }
+
+ this.UpdateNavigationBehavior();
+ }
+
+ #endregion IsCurrent Read-Only Property
+
+ #region IsSelected Read-only Property
+
+ private static readonly DependencyPropertyKey IsSelectedPropertyKey =
+ DependencyProperty.RegisterReadOnly( "IsSelected", typeof( bool ), typeof( Row ), new UIPropertyMetadata( false ) );
+
+ public static readonly DependencyProperty IsSelectedProperty;
+
+ public bool IsSelected
+ {
+ get
+ {
+ return ( bool )this.GetValue( Row.IsSelectedProperty );
+ }
+ }
+
+ internal void SetIsSelected( bool value )
+ {
+ if( this.IsSelected != value )
+ {
+ if( value )
+ {
+ this.SetValue( Row.IsSelectedPropertyKey, value );
+ }
+ else
+ {
+ this.ClearValue( Row.IsSelectedPropertyKey );
+ }
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext != null )
+ dataGridContext.RaiseIsSelectedChangedAutomationEvent( this, value );
+ }
+ }
+
+ #endregion IsSelected Property
+
+ #region CellsHostPanel Read-Only Property
+
+ internal Panel CellsHostPanel
+ {
+ get
+ {
+ return m_cellsHostPanel;
+ }
+ }
+
+ #endregion CellsHostPanel Read-Only Property
+
+ #region SelectionBackground Property
+
+ public static readonly DependencyProperty SelectionBackgroundProperty =
+ DependencyProperty.Register( "SelectionBackground",
+ typeof( Brush ),
+ typeof( Row ),
+ new FrameworkPropertyMetadata( null ) );
+
+ public Brush SelectionBackground
+ {
+ get
+ {
+ return ( Brush )this.GetValue( Row.SelectionBackgroundProperty );
+ }
+ set
+ {
+ this.SetValue( Row.SelectionBackgroundProperty, value );
+ }
+ }
+
+ #endregion SelectionBackground Property
+
+ #region SelectionForeground Property
+
+ public static readonly DependencyProperty SelectionForegroundProperty =
+ DependencyProperty.Register( "SelectionForeground",
+ typeof( Brush ),
+ typeof( Row ),
+ new FrameworkPropertyMetadata( null ) );
+
+ public Brush SelectionForeground
+ {
+ get
+ {
+ return ( Brush )this.GetValue( Row.SelectionForegroundProperty );
+ }
+ set
+ {
+ this.SetValue( Row.SelectionForegroundProperty, value );
+ }
+ }
+
+ #endregion SelectionForeground Property
+
+ #region InactiveSelectionBackground Property
+
+ public static readonly DependencyProperty InactiveSelectionBackgroundProperty =
+ DependencyProperty.Register( "InactiveSelectionBackground",
+ typeof( Brush ),
+ typeof( Row ),
+ new FrameworkPropertyMetadata( null ) );
+
+ public Brush InactiveSelectionBackground
+ {
+ get
+ {
+ return ( Brush )this.GetValue( Row.InactiveSelectionBackgroundProperty );
+ }
+ set
+ {
+ this.SetValue( Row.InactiveSelectionBackgroundProperty, value );
+ }
+ }
+
+ #endregion InactiveSelectionBackground Property
+
+ #region InactiveSelectionForeground Property
+
+ public static readonly DependencyProperty InactiveSelectionForegroundProperty =
+ DependencyProperty.Register( "InactiveSelectionForeground",
+ typeof( Brush ),
+ typeof( Row ),
+ new FrameworkPropertyMetadata( null ) );
+
+ public Brush InactiveSelectionForeground
+ {
+ get
+ {
+ return ( Brush )this.GetValue( Row.InactiveSelectionForegroundProperty );
+ }
+ set
+ {
+ this.SetValue( Row.InactiveSelectionForegroundProperty, value );
+ }
+ }
+
+ #endregion InactiveSelectionForeground Property
+
+ #region RowFocusRoot Property
+
+ private FrameworkElement m_rowFocusRoot;
+
+ internal FrameworkElement RowFocusRoot
+ {
+ get
+ {
+ return m_rowFocusRoot;
+ }
+ }
+
+ #endregion
+
+ #region IsTemplateCell Attached Property
+
+ internal static readonly DependencyProperty IsTemplateCellProperty =
+ DependencyProperty.RegisterAttached( "IsTemplateCell", typeof( bool ), typeof( Row ), new UIPropertyMetadata( false ) );
+
+ internal static bool GetIsTemplateCell( DependencyObject obj )
+ {
+ return ( bool )obj.GetValue( Row.IsTemplateCellProperty );
+ }
+
+ private static void SetIsTemplateCell( DependencyObject obj, bool value )
+ {
+ obj.SetValue( Row.IsTemplateCellProperty, value );
+ }
+
+ #endregion IsTemplateCell Attached Property
+
+ #region ValidationError Read-Only Property
+
+ public static readonly RoutedEvent ValidationErrorChangingEvent = EventManager.RegisterRoutedEvent( "ValidationErrorChanging", RoutingStrategy.Bubble, typeof( RowValidationErrorRoutedEventHandler ), typeof( Row ) );
+
+ public event RowValidationErrorRoutedEventHandler ValidationErrorChanging
+ {
+ add
+ {
+ base.AddHandler( Row.ValidationErrorChangingEvent, value );
+ }
+ remove
+ {
+
+ base.RemoveHandler( Row.ValidationErrorChangingEvent, value );
+ }
+ }
+
+ protected virtual void OnValidationErrorChanging( RowValidationErrorRoutedEventArgs e )
+ {
+ this.RaiseEvent( e );
+ }
+
+ private static readonly DependencyPropertyKey ValidationErrorPropertyKey =
+ DependencyProperty.RegisterReadOnly( "ValidationError", typeof( RowValidationError ), typeof( Row ),
+ new PropertyMetadata( null,
+ new PropertyChangedCallback( Row.OnValidationErrorChanged ),
+ new CoerceValueCallback( Row.OnCoerceValidationError ) ) );
+
+ public static readonly DependencyProperty ValidationErrorProperty = Row.ValidationErrorPropertyKey.DependencyProperty;
+
+ public RowValidationError ValidationError
+ {
+ get
+ {
+ return ( RowValidationError )this.GetValue( Row.ValidationErrorProperty );
+ }
+ }
+
+ internal void SetValidationError( RowValidationError value )
+ {
+ this.SetValue( Row.ValidationErrorPropertyKey, value );
+ }
+
+ private static object OnCoerceValidationError( DependencyObject sender, object value )
+ {
+ if( value == null )
+ return value;
+
+ Row row = ( Row )sender;
+
+ RowValidationErrorRoutedEventArgs rowValidationErrorRoutedEventArgs =
+ new RowValidationErrorRoutedEventArgs( Row.ValidationErrorChangingEvent, row, ( RowValidationError )value );
+
+ row.OnValidationErrorChanging( rowValidationErrorRoutedEventArgs );
+
+ return rowValidationErrorRoutedEventArgs.RowValidationError;
+ }
+
+ private static void OnValidationErrorChanged( object sender, DependencyPropertyChangedEventArgs e )
+ {
+ Row row = ( Row )sender;
+
+ if( row.IsBeingEdited )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( row );
+
+ if( dataGridContext != null )
+ {
+ DataGridControl dataGridControl = dataGridContext.DataGridControl;
+
+ if( dataGridControl != null )
+ {
+ RowState rowState = dataGridControl.CurrentRowInEditionState;
+ Debug.Assert( rowState != null );
+
+ if( rowState != null )
+ rowState.SetItemValidationError( e.NewValue as RowValidationError );
+ }
+ }
+ }
+
+ if( e.OldValue == null )
+ {
+ row.m_cachedLocalToolTip = row.ReadLocalValue( Row.ToolTipProperty );
+ row.ToolTip = ( ( RowValidationError )e.NewValue ).ErrorContent;
+ }
+ else if( e.NewValue == null )
+ {
+ if( row.m_cachedLocalToolTip == DependencyProperty.UnsetValue )
+ {
+ row.ClearValue( Row.ToolTipProperty );
+ }
+ else
+ {
+ row.ToolTip = row.m_cachedLocalToolTip;
+ }
+ }
+ else
+ {
+ row.ToolTip = ( ( RowValidationError )e.NewValue ).ErrorContent;
+ }
+
+
+ row.UpdateHasErrorFlags( null );
+ }
+
+ private object m_cachedLocalToolTip;
+
+ #endregion ValidationError Read-Only Property
+
+ #region UnboundDataItemContext Property
+
+ internal UnboundDataItem UnboundDataItemContext
+ {
+ get
+ {
+ return m_unboundDataItem;
+ }
+ }
+
+ internal void ClearUnboundDataItemContext()
+ {
+ if( m_unboundDataItemNode != null )
+ {
+ UnboundDataItem.FreeUnboundDataItem( m_unboundDataItemNode );
+ m_unboundDataItemNode = null;
+ }
+
+ m_unboundDataItem = null;
+ }
+
+ internal void UpdateUnboundDataItemContext()
+ {
+ object dataItem = this.DataContext;
+
+ if( dataItem == null )
+ {
+ this.ClearUnboundDataItemContext();
+ return;
+ }
+
+ if( m_unboundDataItemNode != null )
+ {
+ object oldDataItem = m_unboundDataItemNode.DataItem;
+
+ if( ( m_unboundDataItemNode.IsAlive ) && ( object.Equals( oldDataItem, dataItem ) ) )
+ return;
+
+ UnboundDataItem.FreeUnboundDataItem( m_unboundDataItemNode );
+ }
+
+ m_unboundDataItemNode = UnboundDataItem.GetUnboundDataItemNode( dataItem, out m_unboundDataItem );
+ }
+
+ private UnboundDataItem.UnboundDataItemNode m_unboundDataItemNode;
+ private UnboundDataItem m_unboundDataItem;
+
+ #endregion
+
+ #region CreatedCells Property
+
+ internal IEnumerable CreatedCells
+ {
+ get
+ {
+ Debug.Assert( m_cellsCache != null );
+ return m_cellsCache.BindedCells;
+ }
+ }
+
+ #endregion
+
+ #region CreatedCellsCount Property
+
+ internal int CreatedCellsCount
+ {
+ get
+ {
+ Debug.Assert( m_cellsCache != null );
+ return m_cellsCache.BindedCells.Count;
+ }
+ }
+
+ #endregion
+
+ #region IsClearingContainer Property
+
+ internal bool IsClearingContainer
+ {
+ get
+ {
+ return m_flags[ ( int )RowFlags.IsClearingContainer ];
+ }
+ set
+ {
+ m_flags[ ( int )RowFlags.IsClearingContainer ] = value;
+ }
+ }
+
+ #endregion
+
+ #region IsContainerPrepared Property
+
+ internal bool IsContainerPrepared
+ {
+ get
+ {
+ return m_flags[ ( int )RowFlags.IsContainerPrepared ];
+ }
+ set
+ {
+ m_flags[ ( int )RowFlags.IsContainerPrepared ] = value;
+ }
+ }
+
+ #endregion
+
+ #region IsInitializingInsertionRow Property
+
+ internal bool IsInitializingInsertionRow
+ {
+ get
+ {
+ return m_flags[ ( int )RowFlags.IsInitializingInsertionRow ];
+ }
+ set
+ {
+ m_flags[ ( int )RowFlags.IsInitializingInsertionRow ] = value;
+ }
+ }
+
+ #endregion
+
+ #region EmptyDataItem Property
+
+ internal EmptyDataItem EmptyDataItem
+ {
+ get
+ {
+ if( m_emptyDataItem == null )
+ {
+ m_emptyDataItem = new EmptyDataItem();
+ }
+
+ return m_emptyDataItem;
+ }
+ }
+
+ private EmptyDataItem m_emptyDataItem; // = null;
+
+ #endregion
+
+ public static Row FindFromChild( DependencyObject child )
+ {
+ return Row.FindFromChild( ( DataGridContext )null, child );
+ }
+
+ public static Row FindFromChild( DataGridContext dataGridContext, DependencyObject child )
+ {
+ // In this situation, the dataGridContext is the DataGridContext of the Row to find.
+ // Useful when a grid is used as a Cell editor and want the Row for a specific DataGridContext.
+ if( child == null )
+ return null;
+
+ Row row = null;
+
+ while( ( row == null ) && ( child != null ) )
+ {
+ child = TreeHelper.GetParent( child );
+ row = child as Row;
+
+ if( row == null )
+ {
+ RowSelector rowSelector = child as RowSelector;
+
+ if( rowSelector != null )
+ row = rowSelector.DataContext as Row;
+ }
+
+ if( ( row != null )
+ && ( dataGridContext != null )
+ && ( dataGridContext != DataGridControl.GetDataGridContext( row ) ) )
+ {
+ row = null;
+ }
+ }
+
+ return row;
+ }
+
+ public static Row FindFromChild( DataGridControl dataGridControl, DependencyObject child )
+ {
+ // In this situation, the dataGridControl is the DataGridControl of the Cell to find.
+ // Useful when a grid is used as a Cell editor and want the Row for a specific DataGridControl.
+ if( child == null )
+ return null;
+
+ Row row = null;
+
+ while( ( row == null ) && ( child != null ) )
+ {
+ child = TreeHelper.GetParent( child );
+ row = child as Row;
+
+ if( ( row != null ) && ( dataGridControl != null ) )
+ {
+ DataGridContext tempDataGridContext = DataGridControl.GetDataGridContext( row );
+
+ if( ( tempDataGridContext == null ) || ( tempDataGridContext.DataGridControl != dataGridControl ) )
+ {
+ row = null;
+ }
+ }
+ }
+
+ return row;
+ }
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+ if( dataGridContext == null )
+ throw new DataGridInternalException();
+
+ DataGridControl gridControl = dataGridContext.DataGridControl;
+ if( gridControl == null )
+ throw new DataGridInternalException();
+
+ m_rowFocusRoot = this.GetTemplateChild( "PART_RowFocusRoot" ) as FrameworkElement;
+
+ {
+ m_oldCellsHostPanel = m_cellsHostPanel;
+
+ IVirtualizingCellsHost oldVirtualizingCellsHost = m_oldCellsHostPanel as IVirtualizingCellsHost;
+
+ if( oldVirtualizingCellsHost != null )
+ oldVirtualizingCellsHost.ClearCellsHost();
+
+ m_cellsHostPanel = this.GetTemplateChild( "PART_CellsHost" ) as Panel;
+
+ if( m_rowFocusRoot == null )
+ m_rowFocusRoot = m_cellsHostPanel;
+
+ m_templateCells.Clear();
+
+ this.GetTemplateCells( this, m_templateCells );
+
+ this.PreparePreviousTemplateCells();
+
+ this.SynchronizeCellsWithColumns( gridControl, m_templateCells, false );
+
+ if( m_oldCellsHostPanel != null )
+ m_oldCellsHostPanel.Children.Clear();
+
+ IVirtualizingCellsHost virtualizingCellsHost = m_cellsHostPanel as IVirtualizingCellsHost;
+
+ if( virtualizingCellsHost != null )
+ {
+ // Ensure to register to the TableViewColumnVirtualizationManager
+ virtualizingCellsHost.PrepareCellsHost( dataGridContext );
+
+ // We force an InvalidateMeasure on the CellsHost to be sure
+ // the visible Cells are the good ones
+ virtualizingCellsHost.InvalidateCellsHostMeasure();
+ }
+ else
+ {
+ // Reset the flag
+ if( gridControl.ForceGeneratorReset == false )
+ gridControl.ForceGeneratorReset = true;
+
+ this.PutCellsInVisualTree( m_templateCells );
+ }
+
+
+ m_oldCellsHostPanel = null;
+ }
+
+ this.UpdateNavigationBehavior();
+ this.UpdateCellsFocusableStatus();
+ }
+
+ protected override void OnMouseMove( MouseEventArgs e )
+ {
+ base.OnMouseMove( e );
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext == null )
+ return;
+
+ if( e.LeftButton == MouseButtonState.Pressed )
+ {
+ dataGridContext.DataGridControl.DoDrag( e );
+ }
+ else
+ {
+ dataGridContext.DataGridControl.ResetDragDataObject();
+ }
+ }
+
+ protected override void OnMouseLeftButtonDown( MouseButtonEventArgs e )
+ {
+ base.OnMouseLeftButtonDown( e );
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext == null )
+ return;
+
+ dataGridContext.DataGridControl.ResetDragDataObject();
+
+ if( e.Handled )
+ return;
+
+ if( this.NavigationBehavior != NavigationBehavior.None )
+ {
+ // We accept NavigationBehavior.CellOnly because it will be handled by the OnPreviewGotKeyboardFocus
+ // of DataGridControl to redirect it to the current column if possible.
+ DependencyObject sourceElement = e.OriginalSource as DependencyObject;
+
+ // Is not FixedColumnSplitter
+ if( !( sourceElement is Views.FixedColumnSplitter ) && ( !Row.IsPartOfFixedColumnSplitter( sourceElement ) ) )
+ {
+ bool focused = false;
+
+ if( m_rowFocusRoot != null )
+ {
+ focused = m_rowFocusRoot.Focus();
+ }
+ else
+ {
+ focused = this.Focus();
+ }
+
+ e.Handled = true;
+
+ if( focused )
+ {
+ // Keep a reference to the mouse position so we can calculate when a drag operation is actually started.
+ dataGridContext.DataGridControl.InitializeDragPostion( e );
+ }
+ }
+ }
+ }
+
+ protected override void OnMouseEnter( MouseEventArgs e )
+ {
+ base.OnMouseEnter( e );
+
+ //If the current CellEditorDisplayConditions requires display when mouse is over the Row
+ if( Row.IsCellEditorDisplayConditionsSet( this, CellEditorDisplayConditions.MouseOverRow ) )
+ {
+ //Display the editors for the Row
+ this.SetDisplayEditorMatchingCondition( CellEditorDisplayConditions.MouseOverRow );
+ }
+
+ // In case a value was explicitly specified on the Cell's ParentColumn
+ this.RefreshCellsDisplayedTemplate();
+ }
+
+ protected override void OnMouseLeave( MouseEventArgs e )
+ {
+ base.OnMouseLeave( e );
+
+ //If the current CellEditorDisplayConditions requires display when mouse is over the Row
+ if( Row.IsCellEditorDisplayConditionsSet( this, CellEditorDisplayConditions.MouseOverRow ) )
+ {
+ //Display the editors for the Row
+ this.RemoveDisplayEditorMatchingCondition( CellEditorDisplayConditions.MouseOverRow );
+ }
+
+ // In case a value was explicitly specified on the Cell's ParentColumn
+ this.RefreshCellsDisplayedTemplate();
+ }
+
+ protected override void OnIsKeyboardFocusWithinChanged( DependencyPropertyChangedEventArgs e )
+ {
+ base.OnIsKeyboardFocusWithinChanged( e );
+
+ //Update the NavigationBehavior related parameter
+ this.UpdateNavigationBehavior();
+ this.UpdateCellsFocusableStatus();
+ }
+
+ protected override void OnGotKeyboardFocus( KeyboardFocusChangedEventArgs e )
+ {
+ //this element or a child element of the row is receiving the keyboard focus
+ //update the cell navigation parameters
+ this.UpdateNavigationBehavior();
+ this.UpdateCellsFocusableStatus();
+
+ base.OnGotKeyboardFocus( e );
+ }
+
+ protected internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ object currentThemeKey = view.GetDefaultStyleKey( typeof( Row ) );
+
+ if( currentThemeKey.Equals( this.DefaultStyleKey ) == false )
+ {
+ this.DefaultStyleKey = currentThemeKey;
+ }
+ }
+
+ protected override AutomationPeer OnCreateAutomationPeer()
+ {
+ return new RowAutomationPeer( this );
+ }
+
+ protected virtual void SetDataContext( object item )
+ {
+ this.DataContext = item;
+ }
+
+ protected virtual void ClearDataContext()
+ {
+ this.DataContext = null;
+ }
+
+ protected internal virtual void PrepareContainer( DataGridContext dataGridContext, object item )
+ {
+ if( this.IsContainerPrepared )
+ Debug.Fail( "A Row can't be prepared twice, it must be cleaned before PrepareContainer is called again" );
+
+ if( dataGridContext == null )
+ throw new ArgumentNullException( "dataGridContext" );
+
+ DataGridControl gridControl = dataGridContext.DataGridControl;
+
+ this.SetDataContext( item );
+
+ if( dataGridContext.InternalCurrentItem == item )
+ {
+ this.SetIsCurrent( true );
+ // The cell.SetIsCurrent is set later since at that stage we have no cell.
+
+ if( !gridControl.IsSetFocusInhibited )
+ {
+ gridControl.QueueSetFocusHelper( false );
+ }
+ }
+
+ //this Forces the generation of the template and linked to that, of the cells.
+ this.ApplyTemplate();
+
+ // We ensure to create the CurrentCell by fetching it from the CellsCollection
+ // If there is a current column on the DataGridContext, try to restore the currency of the cell
+ if( ( this.IsCurrent ) && ( dataGridContext.CurrentColumn != null ) )
+ {
+ //This will also trigger the creation of the Cell if it is virtualized.
+ Cell currentCell = m_cellsCache[ dataGridContext.CurrentColumn ];
+ }
+
+ IVirtualizingCellsHost virtualizingCellsHost = this.CellsHostPanel as IVirtualizingCellsHost;
+
+ if( virtualizingCellsHost != null )
+ {
+ virtualizingCellsHost.PrepareCellsHost( dataGridContext );
+
+ // We force an InvalidateMeasure on the CellsHost to be sure
+ // the visible Cells are the good ones
+ virtualizingCellsHost.InvalidateCellsHostMeasure();
+ }
+ else
+ {
+ //re-Initialize all created cells for the new "assignation"
+ foreach( Cell cell in this.CreatedCells )
+ {
+ // Call Cell.PrepareContainer for cells already created
+ cell.PrepareContainer( dataGridContext, item );
+ }
+ }
+
+ if( m_templateCells != null )
+ {
+ // We must ensure every template cells are prepared
+ foreach( Cell cell in m_templateCells.Values )
+ {
+ cell.PrepareContainer( dataGridContext, item );
+ }
+ }
+
+ // We must set isContainerPrepared because a verification is made in BeginEdit
+ // to ensure the container is prepared before entering in edition. The following
+ // RestoreEditionState will possibly trigger BeginEdit.
+ this.IsContainerPrepared = true;
+
+ // Use the CurrentContext.InternalCurrentItem when restoring current item
+ object currentItemInEdition = gridControl.CurrentItemInEdition;
+
+ // Restore the edition state on the container
+ // Voluntarilly using the .Equals() method instead of == operator to allow Struct comparison (in case of GroupHeaderFooterItem struct)
+ if( ( currentItemInEdition != null ) && ( currentItemInEdition.Equals( item ) ) )
+ {
+ //PL: Should only occur when hitting a CollectionView reset while editing.
+ this.RestoreEditionState( gridControl.CurrentRowInEditionState, dataGridContext.CurrentColumn );
+ }
+
+
+ this.UpdateMatchingDisplayConditions();
+ this.UpdateNavigationBehavior();
+ }
+
+ protected abstract Cell CreateCell( ColumnBase column );
+
+ protected abstract bool IsValidCellType( Cell cell );
+
+ protected internal virtual void ClearContainer()
+ {
+ this.IsClearingContainer = true;
+
+ try
+ {
+ // Clear every Cells before clearing the Row's values to allow the Cell
+ // to check some properties on there parent row when processing a Cell.ClearContainer
+ IVirtualizingCellsHost virtualizingCellsHost = this.CellsHostPanel as IVirtualizingCellsHost;
+
+ // If there were some validation errors, we want to clear every Cells to be sure
+ // they will never reflect the error state on another data item if they are not
+ // explicitly prepared
+ if( virtualizingCellsHost != null )
+ {
+ virtualizingCellsHost.ClearCellsHost();
+ }
+
+ // Ensure every prepared Cells are cleared correctly
+ foreach( Cell cell in m_cellsCache.Cells )
+ {
+ if( cell.IsContainerPrepared )
+ {
+ cell.ClearContainer();
+ }
+ }
+
+ // Clear all the DP's that are either public or somehow inherited.
+ this.ClearValue( Row.NavigationBehaviorProperty );
+ this.ClearValue( DataGridControl.CellEditorDisplayConditionsProperty );
+
+ // We need to clear both the ValidationError and the HasValidationError property.
+ // The clearing of ValidationError will take care of the tooltip/cached local tooltip
+ // The clearing of HasValidationError will take care of updating the DataGridControl HasValidationError property.
+ this.ClearValue( Row.ValidationErrorPropertyKey );
+ this.ClearValue( Row.HasValidationErrorPropertyKey );
+
+ this.ClearValue( Row.IsBeingEditedPropertyKey );
+ this.ClearValue( Row.IsDirtyPropertyKey );
+ this.ClearValue( Row.RowDisplayEditorMatchingConditionsProperty );
+ this.ClearValue( Row.IsCurrentPropertyKey );
+ this.ClearValue( Row.IsSelectedPropertyKey );
+
+ this.ClearValue( DataGridControl.ContainerGroupConfigurationProperty );
+
+ this.ClearDataContext();
+ }
+ finally
+ {
+ this.IsClearingContainer = false;
+ this.IsContainerPrepared = false;
+ }
+ }
+
+ internal static Row FromContainer( DependencyObject container )
+ {
+ if( container == null )
+ return null;
+
+ Row row = container as Row;
+
+ if( row != null )
+ return row;
+
+ HeaderFooterItem headerFooterItem = container as HeaderFooterItem;
+
+ if( headerFooterItem != null )
+ {
+ row = HeaderFooterItem.FindIDataGridItemContainerInChildren(
+ headerFooterItem, headerFooterItem.AsVisual() ) as Row;
+
+ if( row != null )
+ return row;
+ }
+
+ return null;
+ }
+
+ internal static void SetRowValidationErrorOnException( Row row, Exception exception )
+ {
+ System.Diagnostics.Debug.Assert( ( row != null ) && ( exception != null ) );
+
+ // This method will set a validation error on the row and throw back a DataGridValidationException so that
+ // the row stays in edition.
+
+ if( exception is TargetInvocationException )
+ exception = exception.InnerException;
+
+ row.SetValidationError( new RowValidationError(
+ Row.CustomRowValidationExceptionValidationRule,
+ row,
+ exception.Message,
+ exception ) );
+
+ // Throwing a DataGridValidationException will be caught by the grid and will make the cell stay in edition.
+ throw new DataGridValidationException( "An error occurred while attempting to end the edit process.", exception );
+ }
+
+ internal static bool IsCellEditorDisplayConditionsSet( Row row, CellEditorDisplayConditions condition )
+ {
+ return ( ( row.CellEditorDisplayConditions & condition ) == condition );
+ }
+
+ internal void UpdateCellsContentBindingTarget()
+ {
+ // We need to refresh the Cell.Content target binding in case the dataObject value was coerced to something else.
+ foreach( Cell cell in this.CreatedCells )
+ {
+ cell.UpdateContentBindingTarget();
+ }
+ }
+
+ internal void SynchronizeCellsWithColumns(
+ DataGridControl parentGrid,
+ Dictionary templateCells,
+ bool onlyIfParentGridHasChanged )
+ {
+ if( ( !onlyIfParentGridHasChanged ) ||
+ ( parentGrid != m_parentGridUsedForCellsGeneration ) )
+ {
+ if( templateCells == null )
+ {
+ // Create a list of manually specified Cells in the Template. These will be
+ // ignored by the Cell adding process.
+ templateCells = m_templateCells;
+ }
+
+ this.GenerateMissingAndRemoveUnusedCells( templateCells );
+ m_parentGridUsedForCellsGeneration = parentGrid;
+ }
+ }
+
+ internal void UpdateHasErrorFlags( Cell errorChangedCell )
+ {
+ System.Diagnostics.Debug.Assert( ( errorChangedCell == null ) || ( errorChangedCell.ParentRow == this ) );
+
+ RowValidationError itemValidationError = this.ValidationError;
+
+ bool rowHasValidationError = ( itemValidationError != null );
+ bool rowHasRestrictiveValidationError = ( rowHasValidationError ) ? Row.GetIsValidationErrorRestrictive( itemValidationError ) : false;
+
+ if( ( !rowHasRestrictiveValidationError ) && ( errorChangedCell != null ) )
+ {
+ // We must check the passed cell since it might not yet be part of the row's Cells collection.
+ // This can occur when initializing a cell and its Content binding already has a ValidationError.
+ if( errorChangedCell.HasValidationError )
+ {
+ rowHasValidationError = true;
+
+ if( errorChangedCell.IsValidationErrorRestrictive )
+ rowHasRestrictiveValidationError = true;
+ }
+
+ if( !rowHasRestrictiveValidationError )
+ {
+ // Create a clone of the list to avoid concurrent access when iterating
+ // and a Cell is added to CreatedCells because of ColumnVirtualization
+ List createdCells = new List( this.CreatedCells );
+
+ foreach( Cell cell in createdCells )
+ {
+ if( cell == errorChangedCell )
+ continue;
+
+ if( cell.HasValidationError )
+ {
+ rowHasValidationError = true;
+
+ if( cell.IsValidationErrorRestrictive )
+ {
+ rowHasRestrictiveValidationError = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ this.SetHasValidationError( rowHasValidationError );
+ this.SetIsValidationErrorRestrictive( rowHasRestrictiveValidationError );
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl gridControl = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ if( gridControl != null )
+ {
+ bool gridHasValidationError = rowHasValidationError;
+
+ if( !gridHasValidationError )
+ {
+ var generator = gridControl.CustomItemContainerGenerator;
+ foreach( var item in generator.GetRealizedDataItems() )
+ {
+ var row = gridControl.GetContainerFromItem( item ) as Row;
+
+ if( ( row != null ) && ( row.HasValidationError ) )
+ {
+ gridHasValidationError = true;
+ break;
+ }
+ }
+ }
+
+ gridControl.SetHasValidationError( gridHasValidationError );
+ }
+ }
+
+ internal virtual bool IsEditTriggerSet( EditTriggers triggers )
+ {
+ return ( ( this.EditTriggers & triggers ) == triggers );
+ }
+
+ internal Cell ProvideCell( ColumnBase column )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+ bool isCurrentColumn = ( dataGridContext.CurrentColumn == column );
+ bool rowIsCurrent = this.IsCurrent;
+
+ Xceed.Wpf.DataGrid.Views.ViewBase view = dataGridContext.DataGridControl.GetView();
+
+ string fieldName = column.FieldName;
+ Cell cell = null;
+
+ if( fieldName != null )
+ {
+ // Get the Cell from either the explicitly positioned cells of the row template
+ // or from the already created cells.
+ m_templateCells.TryGetValue( fieldName, out cell );
+ }
+
+ // We get the IVirtualizingCellsHost to be able to modify the LogicalParent if required
+ IVirtualizingCellsHost virtualizingCellsHost = m_cellsHostPanel as IVirtualizingCellsHost;
+
+ if( cell == null )
+ {
+ cell = this.CreateCell( column );
+
+ bool isNewDataContext;
+ Cell.AssignDataContext( cell, this.DataContext, this.UnboundDataItemContext, column, out isNewDataContext );
+
+ cell.Initialize( dataGridContext, this, column );
+
+ cell.PrepareDefaultStyleKey( view );
+
+ if( ( virtualizingCellsHost != null ) && ( virtualizingCellsHost.CanModifyLogicalParent ) )
+ virtualizingCellsHost.SetLogicalParent( cell );
+
+ //optimization so that lookup speed will be increased for the Cells
+ cell.SetValue( DataGridControl.DataGridContextPropertyKey, dataGridContext );
+
+ if( ( rowIsCurrent ) && ( isCurrentColumn ) )
+ cell.SetIsCurrent( true );
+
+ // We must update the Focusable status for the newly created cell since
+ // UpdateCellsFocusableStatus is only called in Row.OnApplyTemplate
+ this.UpdateCellFocusableStatus( cell );
+
+ // The cell was just created, we must prepare it manually the first time
+ // since it is added by the PART_CellsHost and not directly put in it
+ // by the Row. Further PrepareContainer will be done when Row.PrepareContainer
+ // is called since it will be contained in Row.CreatedCells
+ cell.PrepareContainer( dataGridContext, this.DataContext );
+ }
+ else
+ {
+ if( virtualizingCellsHost == null )
+ {
+ cell.Initialize( dataGridContext, this, column );
+ }
+ else
+ {
+ Debug.Fail( "We do not want to return a template Cell since it already has a visual parent." );
+ // We do not want to return a template Cell since it already has a visual parent
+ cell = null;
+ }
+ }
+
+ return cell;
+ }
+
+ internal void RefreshCellsDisplayedTemplate()
+ {
+ // We never want to update the cell's template while clearing container
+ if( this.IsClearingContainer )
+ return;
+
+ // In case a value was explicitly specified on the Cell's ParentColumn
+ foreach( Cell cell in this.CreatedCells )
+ {
+ cell.RefreshDisplayedTemplate();
+ }
+ }
+
+ private static bool GetIsValidationErrorRestrictive( RowValidationError validationError )
+ {
+ if( validationError == null )
+ return false;
+
+ return !( validationError.RuleInError is DataErrorValidationRule );
+ }
+
+ private static bool IsPartOfFixedColumnSplitter( DependencyObject element )
+ {
+ DependencyObject parent = TreeHelper.GetParent( element );
+
+ while( parent != null )
+ {
+ if( parent is Views.FixedColumnSplitter )
+ break;
+
+ // It is not necessary to go further than the Row.
+ if( parent is Row )
+ break;
+
+ parent = TreeHelper.GetParent( parent );
+ }
+
+ return ( parent is Views.FixedColumnSplitter );
+ }
+
+ private static bool IsPartOfCell( DependencyObject element )
+ {
+ bool retval = false;
+
+ DependencyObject parent = element;
+
+ while( ( parent is Row ) == false )
+ {
+ parent = TreeHelper.GetParent( parent );
+
+ //if the parent is null, then the item "clicked" is floating... let's consider it as a cell, so that
+ //no specific actions are taken
+ if( parent == null )
+ {
+ retval = true;
+ break;
+ }
+
+ if( parent is Cell )
+ {
+ retval = true;
+ break;
+ }
+ }
+
+ return retval;
+ }
+
+ private void UpdateMatchingDisplayConditions()
+ {
+ CellEditorDisplayConditions newEffectiveValue = CellEditorDisplayConditions.None;
+
+ if( ( Row.IsCellEditorDisplayConditionsSet( this, CellEditorDisplayConditions.RowIsBeingEdited ) ) &&
+ ( this.IsBeingEdited ) )
+ {
+ newEffectiveValue |= CellEditorDisplayConditions.RowIsBeingEdited;
+ }
+
+ if( ( Row.IsCellEditorDisplayConditionsSet( this, CellEditorDisplayConditions.MouseOverRow ) ) &&
+ ( this.IsMouseOver ) )
+ {
+ newEffectiveValue |= CellEditorDisplayConditions.MouseOverRow;
+ }
+
+ if( ( Row.IsCellEditorDisplayConditionsSet( this, CellEditorDisplayConditions.RowIsCurrent ) ) &&
+ ( this.IsCurrent ) )
+ {
+ newEffectiveValue |= CellEditorDisplayConditions.RowIsCurrent;
+ }
+
+ if( Row.IsCellEditorDisplayConditionsSet( this, CellEditorDisplayConditions.Always ) )
+ {
+ newEffectiveValue |= CellEditorDisplayConditions.Always;
+ }
+
+ this.SetValue( Row.RowDisplayEditorMatchingConditionsProperty, newEffectiveValue );
+ }
+
+ private Cell GetCellForCurrentColumn()
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext == null )
+ return null;
+
+ return m_cellsCache[ dataGridContext.CurrentColumn ];
+ }
+
+ private void UpdateNavigationBehavior()
+ {
+ DataGridContext context = DataGridControl.GetDataGridContext( this );
+
+ // We check if the container has an Item binded to it and if
+ // it's not the case, we don't need to update the navigation
+ // mode since it has already been changed to None by the
+ // PrepareIsTabStop of the TableViewItemsHost class.
+ if( ( context == null )
+ || ( context.GetItemFromContainer( this ) == null ) )
+ {
+ return;
+ }
+
+ bool rowFocusable = false;
+ KeyboardNavigationMode tabNavigation = KeyboardNavigationMode.None;
+ KeyboardNavigationMode keyboardNavigation = KeyboardNavigationMode.None;
+
+ FrameworkElement focusItem = this.RowFocusRoot;
+
+ bool isKeyboardFocused = ( focusItem != null ) ? focusItem.IsKeyboardFocused : this.IsKeyboardFocused;
+
+ //do not want to update the navigation behavior of the row if i'm currently editing
+ if( !this.IsBeingEdited )
+ {
+ if( !this.IsCurrent )
+ {
+ rowFocusable = ( this.NavigationBehavior != NavigationBehavior.None );
+ tabNavigation = KeyboardNavigationMode.None;
+ keyboardNavigation = KeyboardNavigationMode.None;
+ }
+ else
+ {
+ switch( this.NavigationBehavior )
+ {
+ case NavigationBehavior.None:
+ rowFocusable = false;
+ tabNavigation = KeyboardNavigationMode.None;
+ keyboardNavigation = KeyboardNavigationMode.None;
+ break;
+
+ case NavigationBehavior.RowOnly:
+ rowFocusable = true;
+ tabNavigation = KeyboardNavigationMode.None;
+ keyboardNavigation = KeyboardNavigationMode.None;
+ break;
+
+ case NavigationBehavior.RowOrCell:
+ //There is an identified weakness with the IsKeyboardFocusWithin property where it cannot tell if the focus is within a Popup which is within the element
+ //This has been identified, and only the places where it caused problems were fixed... This comment is only here to remind developpers of the flaw
+ if( ( this.IsKeyboardFocusWithin ) && ( !isKeyboardFocused ) )
+ {
+ rowFocusable = false;
+ tabNavigation = KeyboardNavigationMode.None; //for case 99719: modified this to disable the Tab navigation between cells.
+ keyboardNavigation = KeyboardNavigationMode.Continue;
+ }
+ else
+ {
+ rowFocusable = true;
+ tabNavigation = KeyboardNavigationMode.None;
+ keyboardNavigation = KeyboardNavigationMode.None;
+ }
+ break;
+
+ case NavigationBehavior.CellOnly:
+ rowFocusable = false;
+ tabNavigation = KeyboardNavigationMode.None;
+ keyboardNavigation = KeyboardNavigationMode.Continue;
+ break;
+ }
+ }
+ }
+ else
+ {
+ rowFocusable = true;
+ tabNavigation = KeyboardNavigationMode.Cycle;
+ keyboardNavigation = KeyboardNavigationMode.Continue;
+ }
+
+ if( focusItem != null )
+ {
+ this.Focusable = false;
+ KeyboardNavigation.SetTabNavigation( this, KeyboardNavigationMode.None );
+ KeyboardNavigation.SetDirectionalNavigation( this, KeyboardNavigationMode.Continue );
+
+ if( focusItem.FocusVisualStyle != this.FocusVisualStyle )
+ {
+ if( focusItem.ReadLocalValue( FrameworkElement.FocusVisualStyleProperty ) == DependencyProperty.UnsetValue )
+ {
+ focusItem.FocusVisualStyle = this.FocusVisualStyle;
+ }
+ }
+
+ focusItem.Focusable = rowFocusable;
+ KeyboardNavigation.SetTabNavigation( focusItem, tabNavigation );
+ KeyboardNavigation.SetDirectionalNavigation( focusItem, keyboardNavigation );
+ }
+ else
+ {
+ this.Focusable = rowFocusable;
+ KeyboardNavigation.SetTabNavigation( this, tabNavigation );
+ KeyboardNavigation.SetDirectionalNavigation( this, keyboardNavigation );
+ }
+ }
+
+ private void UpdateCellsFocusableStatus()
+ {
+ //cycle through all cells and
+ foreach( Cell cell in this.CreatedCells )
+ {
+
+ //force an update of the NavigationBehavior characteristics
+ this.UpdateCellFocusableStatus( cell );
+ }
+ }
+
+ private void UpdateCellFocusableStatus( Cell cell )
+ {
+ if( cell == null )
+ return;
+
+ bool cellFocusable = true;
+
+ ColumnBase parentColumn = cell.ParentColumn;
+
+ if( !this.IsBeingEdited )
+ {
+ switch( this.NavigationBehavior )
+ {
+ case NavigationBehavior.None:
+ cellFocusable = false;
+ break;
+ case NavigationBehavior.RowOnly:
+ cellFocusable = false;
+ break;
+ case NavigationBehavior.RowOrCell:
+ cellFocusable = true;
+ break;
+ case NavigationBehavior.CellOnly:
+ cellFocusable = true;
+ break;
+ }
+ }
+
+ //force an update of the NavigationBehavior characteristics
+ cell.Focusable = ( cellFocusable && cell.GetCalculatedCanBeCurrent() );
+ }
+
+ private void RestoreEditionState( RowState savedState, ColumnBase currentColumn )
+ {
+ if( savedState == null )
+ return;
+
+ savedState = savedState.Clone();
+
+ Dictionary cachedCellStates = new Dictionary();
+
+ foreach( Cell cell in this.CreatedCells )
+ {
+ ColumnBase parentColumn = cell.ParentColumn;
+
+ if( parentColumn == null )
+ continue;
+
+ var currentRowInEditionCellState = parentColumn.CurrentRowInEditionCellState;
+
+ if( currentRowInEditionCellState != null )
+ cachedCellStates.Add( parentColumn, currentRowInEditionCellState.Clone() );
+ }
+
+ try
+ {
+ this.BeginEdit();
+ }
+ catch( DataGridException )
+ {
+ // We swallow exception if it occurs because of a validation error or Cell was read-only or
+ // any other GridException.
+ }
+
+ if( this.IsBeingEdited )
+ {
+ this.SetValidationError( savedState.ItemValidationError );
+
+ this.SetIsDirty( savedState.IsDirty );
+
+ foreach( Cell cell in this.CreatedCells )
+ {
+ ColumnBase parentColumn = cell.ParentColumn;
+ CellState cachedCellState;
+
+ if( cachedCellStates.TryGetValue( parentColumn, out cachedCellState ) )
+ parentColumn.CurrentRowInEditionCellState = cachedCellState;
+
+ cell.RestoreEditionState( currentColumn );
+ }
+ }
+ }
+
+ private void PutCellsInVisualTree( Dictionary templateCells )
+ {
+ if( m_cellsCache == null )
+ {
+ Debug.Fail( "PutCellsInVisualTree was called on a Row that is not a DataGridControl's descendant or whose DataGridControl ancestor has a null VisibleColumns property." );
+ return;
+ }
+
+ if( m_cellsHostPanel == null )
+ {
+ Debug.Fail( "PutCellsInVisualTree was called on a Row that doesn't have a Panel identified as the CellsHost in its VisualTree." );
+ return;
+ }
+
+ if( m_cellsHostPanel != m_oldCellsHostPanel )
+ m_cellsHostPanel.Children.Clear();
+
+ m_oldCellsHostPanel = null;
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext == null )
+ return;
+
+ object dataItem = this.DataContext;
+
+ // fill the cellsHostPanel in visible order.
+ foreach( ColumnBase column in dataGridContext.ColumnsByVisiblePosition )
+ {
+ Cell cell = m_cellsCache[ column ];
+
+ if( cell == null )
+ {
+ //This situation is now a proof that something is awfully wrong since
+ //the VirtualizingCellCollection will create a cell if none is present.
+ throw new DataGridInternalException();
+ }
+
+ // We don't add a cell specifically (manually) positioned in the row template.
+ if( cell.FieldName == null || !templateCells.ContainsKey( cell.FieldName ) )
+ m_cellsHostPanel.Children.Add( cell );
+
+ // When explicitly put in the PART_CellsHost, we must call Cell.PrepareContainer
+ cell.PrepareContainer( dataGridContext, dataItem );
+ }
+ }
+
+ private void GetTemplateCells( Visual root, Dictionary templateCells )
+ {
+ int childrenCount = VisualTreeHelper.GetChildrenCount( root );
+
+ if( childrenCount > 0 )
+ {
+ Visual child = null;
+ Cell cell = null;
+
+ for( int i = 0; i < childrenCount; i++ )
+ {
+ child = VisualTreeHelper.GetChild( root, i ) as Visual;
+
+ if( child != null )
+ {
+ cell = child as Cell;
+
+ if( cell == null )
+ {
+ this.GetTemplateCells( child, templateCells );
+ }
+ else
+ {
+ //if the cell is of the appropriate type for the Row, then ...
+ if( this.IsValidCellType( cell ) )
+ {
+ //mark the Cell as a Fixed Tempalte Cell.
+ Row.SetIsTemplateCell( cell, true );
+
+ templateCells.Add( cell.FieldName, cell );
+ }
+
+ //if the cell is not of the appropriate type, don't do anything, it will remain blank.
+ }
+ }
+ }
+ }
+ else
+ {
+ Visual child = null;
+ ContentControl contentControl = root as ContentControl;
+
+ if( contentControl == null )
+ {
+ ContentPresenter contentPresenter = root as ContentPresenter;
+
+ if( contentPresenter != null )
+ {
+ child = contentPresenter.Content as Visual;
+ }
+ }
+ else
+ {
+ child = contentControl.Content as Visual;
+ }
+
+ //avoid recursing into a Row object... that can only mean that the Row object is the data item for the Row (self contained, unbound).
+ if( ( child != null ) && ( ( child is Row ) == false ) )
+ {
+ Cell cell = child as Cell;
+
+ if( cell == null )
+ {
+ this.GetTemplateCells( child, templateCells );
+ }
+ else
+ {
+ //mark the Cell as a Fixed Tempalte Cell.
+ Row.SetIsTemplateCell( cell, true );
+
+ templateCells.Add( cell.FieldName, cell );
+ }
+ }
+ }
+ }
+
+ private void GenerateMissingAndRemoveUnusedCells( Dictionary templateCells )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ Debug.Assert( ( dataGridContext != null ) || ( DesignerProperties.GetIsInDesignMode( this ) ) );
+
+ if( dataGridContext == null )
+ return;
+
+ ColumnCollection columns = dataGridContext.Columns;
+ Hashtable cellsDictionary = new Hashtable();
+
+ //Take each and every cells already created and place them in a dictionary
+ //This dictionary is gonna be used to manage cells that need to be removed at the
+ //end of the method's body.
+ foreach( Cell cell in this.CreatedCells )
+ {
+ cellsDictionary.Add( cell.FieldName, cell );
+ }
+
+ ColumnBase currentColumn = dataGridContext.CurrentColumn;
+ bool rowIsCurrent = this.IsCurrent;
+
+ Xceed.Wpf.DataGrid.Views.ViewBase view = dataGridContext.DataGridControl.GetView();
+
+ foreach( ColumnBase column in columns )
+ {
+ string fieldName = column.FieldName;
+ Cell cell = null;
+
+ //Try to get the Template Cell defined for this column
+ templateCells.TryGetValue( fieldName, out cell );
+
+ //The cell has been found in the template cells.
+ if( cell != null )
+ {
+ //A cell for this Column has been found in the Template cells, get rid of the corresponding Cell in the Cells collection (if any)
+ Cell oldCell;
+ if( m_cellsCache.TryGetCell( column, out oldCell ) )
+ {
+ //If an oldCell was present, clear it and remove it from the Cells collection
+ oldCell.ClearContainer();
+ m_cellsCache.InternalRemove( oldCell );
+ }
+
+ cell.Initialize( dataGridContext, this, column );
+
+ // Ensure to prepare a Cell if it is not prepared or virtualized
+ if( !cell.IsContainerPrepared || cell.IsContainerVirtualized )
+ {
+ cell.PrepareContainer( dataGridContext, this.DataContext );
+ }
+
+ //then add the Template cell to the Cells collection.
+ m_cellsCache.InternalAdd( cell );
+
+ //finally, if the Row is current and the column in question is current, set the Currency state on the template cell.
+ if( ( rowIsCurrent ) && ( column == currentColumn ) )
+ {
+ cell.SetIsCurrent( true );
+ }
+ }
+ //The cell is not part of the Template Cells... But it still needs some work to be done.
+ else
+ {
+ if( !m_cellsCache.TryGetCell( column, out cell ) )
+ {
+ cell = null;
+ }
+ }
+
+ //the Cell is a Template Cell OR is already present in the Row
+ if( cell != null )
+ {
+ //To ensure that the DefaultStyleKey is set appropriatly
+ cell.PrepareDefaultStyleKey( view );
+ }
+
+ //remove the Column's FieldName from the dictionary of Cells to remove
+ //This is to prevent the removal of the cell in the cleanup code below.
+ cellsDictionary.Remove( fieldName );
+ }
+
+ // clean unmatched cells
+ foreach( Cell cell in cellsDictionary.Values )
+ {
+ cell.ClearContainer();
+ m_cellsCache.InternalRemove( cell );
+ }
+ }
+
+ private void PreparePreviousTemplateCells()
+ {
+ List removeList = new List();
+
+ //cycle through all the cells, and if any of them have the
+ //attached property identifying them as Fixed Template Cells,
+ // then clear their FieldName property
+ foreach( Cell cell in this.CreatedCells )
+ {
+ if( Row.GetIsTemplateCell( cell ) )
+ {
+ removeList.Add( cell );
+ }
+ }
+
+ foreach( Cell removedCell in removeList )
+ {
+ removedCell.ClearContainer();
+ m_cellsCache.InternalRemove( removedCell );
+ }
+ }
+
+ #region EDITION
+
+ public static readonly RoutedEvent EditBeginningEvent = EventManager.RegisterRoutedEvent( "EditBeginning", RoutingStrategy.Bubble, typeof( CancelRoutedEventHandler ), typeof( Row ) );
+ public static readonly RoutedEvent EditBegunEvent = EventManager.RegisterRoutedEvent( "EditBegun", RoutingStrategy.Bubble, typeof( RoutedEventHandler ), typeof( Row ) );
+
+ #region IsBeginningEditFromCell Property
+
+ internal bool IsBeginningEditFromCell
+ {
+ get
+ {
+ return m_flags[ ( int )RowFlags.IsBeginningEditFromCell ];
+ }
+ set
+ {
+ m_flags[ ( int )RowFlags.IsBeginningEditFromCell ] = value;
+ }
+ }
+
+ #endregion
+
+ #region IsBeginningEdition Property
+
+ internal bool IsBeginningEdition
+ {
+ get
+ {
+ return m_flags[ ( int )RowFlags.IsBeginningEdition ];
+ }
+ private set
+ {
+ m_flags[ ( int )RowFlags.IsBeginningEdition ] = value;
+ }
+ }
+
+ #endregion
+
+ #region IsEndingEdition Property
+
+ internal bool IsEndingEdition
+ {
+ get
+ {
+ return m_flags[ ( int )RowFlags.IsEndingEdition ];
+ }
+ private set
+ {
+ m_flags[ ( int )RowFlags.IsEndingEdition ] = value;
+ }
+ }
+
+ #endregion
+
+ #region IsCancelingEdition Property
+
+ internal bool IsCancelingEdition
+ {
+ get
+ {
+ return m_flags[ ( int )RowFlags.IsCancelingEdition ];
+ }
+ private set
+ {
+ m_flags[ ( int )RowFlags.IsCancelingEdition ] = value;
+ }
+ }
+
+ #endregion
+
+ #region IsBeingEditedCache Property
+
+ private bool IsBeingEditedCache
+ {
+ get
+ {
+ return m_flags[ ( int )RowFlags.IsBeingEditedCache ];
+ }
+ set
+ {
+ m_flags[ ( int )RowFlags.IsBeingEditedCache ] = value;
+ }
+ }
+
+ #endregion
+
+ public event CancelRoutedEventHandler EditBeginning
+ {
+ add
+ {
+ base.AddHandler( Row.EditBeginningEvent, value );
+ }
+ remove
+ {
+ base.RemoveHandler( Row.EditBeginningEvent, value );
+ }
+ }
+
+ public event RoutedEventHandler EditBegun
+ {
+ add
+ {
+ base.AddHandler( Row.EditBegunEvent, value );
+ }
+ remove
+ {
+ base.RemoveHandler( Row.EditBegunEvent, value );
+ }
+ }
+
+ protected internal virtual void OnEditBeginning( CancelRoutedEventArgs e )
+ {
+ this.RaiseEvent( e );
+ }
+
+ protected internal virtual void OnEditBegun()
+ {
+ RoutedEventArgs e = new RoutedEventArgs( Row.EditBegunEvent, this );
+ this.RaiseEvent( e );
+ }
+
+ public void BeginEdit()
+ {
+ if( this.IsBeingEdited )
+ return;
+
+ // If there is no dataItem mapped to this container, we don't want to
+ // enter in edition
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext != null )
+ {
+ object dataItem = dataGridContext.GetItemFromContainer( this );
+
+ if( dataItem == null )
+ return;
+ }
+
+ // We must prevent entering in edition of an EmptyDataItem.
+ if( this.DataContext is EmptyDataItem )
+ return;
+
+ Debug.Assert( this.IsContainerPrepared, "Can't edit a container that has not been prepared." );
+
+ if( this.ReadOnly )
+ throw new DataGridException( "An attempt was made to edit a read-only row." );
+
+ if( this.IsBeginningEdition )
+ throw new DataGridException( "An attempt was made to edit a row for which the edit process has already begun." );
+
+ this.IsBeginningEdition = true;
+
+ try
+ {
+ if( !this.IsBeginningEditFromCell )
+ {
+ CancelRoutedEventArgs e = new CancelRoutedEventArgs( Row.EditBeginningEvent, this );
+ this.OnEditBeginning( e );
+
+ if( e.Cancel )
+ throw new DataGridException( "BeginEdit was canceled." );
+ }
+
+ // We must update the CellStates before calling BeginEditCore to ensure we have the
+ // values that are currently present in the Cells before entering in edition.
+ DataGridControl gridControl = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ // This call will also validate that we're not starting a second row edition.
+ // It will also save the row state and update the columns' CurrentRowInEditionCellState.
+ if( gridControl != null )
+ gridControl.UpdateCurrentRowInEditionCellStates( this, this.DataContext );
+
+ this.SetIsBeingEdited( true );
+
+ try
+ {
+ this.BeginEditCore();
+ }
+ catch
+ {
+ this.SetIsBeingEdited( false );
+
+ // Ensure to clear the CellStates
+ if( gridControl != null )
+ gridControl.UpdateCurrentRowInEditionCellStates( null, null );
+
+ throw;
+ }
+ }
+ finally
+ {
+ this.IsBeginningEdition = false;
+ }
+
+ if( !this.IsBeginningEditFromCell )
+ this.OnEditBegun();
+ }
+
+ protected virtual void BeginEditCore()
+ {
+ if( this.IsBeginningEditFromCell )
+ return;
+
+ Cell currentCell = this.GetCellForCurrentColumn();
+
+ try
+ {
+ if( currentCell != null )
+ {
+ if( !currentCell.IsBeingEdited )
+ {
+ currentCell.BeginEdit();
+ }
+ }
+ else
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext != null )
+ {
+ int firstEditableColumn = DataGridScrollViewer.GetFirstVisibleFocusableColumnIndex( dataGridContext );
+ if( firstEditableColumn < 0 )
+ throw new DataGridException( "Trying to edit while no cell is focusable. " );
+
+ currentCell = m_cellsCache[ dataGridContext.VisibleColumns[ firstEditableColumn ] ];
+
+ if( currentCell != null )
+ currentCell.BeginEdit();
+ }
+ }
+ }
+ catch( DataGridException )
+ {
+ // We swallow exception if it occurs because of a validation error or Cell was read-only or
+ // any other GridException.
+ }
+ }
+
+ public static readonly RoutedEvent EditEndingEvent = EventManager.RegisterRoutedEvent( "EditEnding", RoutingStrategy.Bubble, typeof( CancelRoutedEventHandler ), typeof( Row ) );
+ public static readonly RoutedEvent EditEndedEvent = EventManager.RegisterRoutedEvent( "EditEnded", RoutingStrategy.Bubble, typeof( RoutedEventHandler ), typeof( Row ) );
+
+ public event CancelRoutedEventHandler EditEnding
+ {
+ add
+ {
+ base.AddHandler( Row.EditEndingEvent, value );
+ }
+ remove
+ {
+ base.RemoveHandler( Row.EditEndingEvent, value );
+ }
+ }
+
+ public event RoutedEventHandler EditEnded
+ {
+ add
+ {
+ base.AddHandler( Row.EditEndedEvent, value );
+ }
+ remove
+ {
+ base.RemoveHandler( Row.EditEndedEvent, value );
+ }
+ }
+
+ protected virtual void OnEditEnding( CancelRoutedEventArgs e )
+ {
+ this.RaiseEvent( e );
+ }
+
+ protected virtual void OnEditEnded()
+ {
+ RoutedEventArgs e = new RoutedEventArgs( Row.EditEndedEvent, this );
+ this.RaiseEvent( e );
+ }
+
+ public void EndEdit()
+ {
+ if( !this.IsBeingEdited )
+ return;
+
+ if( this.IsEndingEdition )
+ throw new InvalidOperationException( "An attempt was made to end the edit process while it is already in the process of being ended." );
+
+ if( this.IsCancelingEdition )
+ throw new InvalidOperationException( "An attempt was made to end the edit process while it is being canceled." );
+
+ CancelRoutedEventArgs e = new CancelRoutedEventArgs( Row.EditEndingEvent, this );
+
+ try
+ {
+ this.OnEditEnding( e );
+
+ if( e.Cancel )
+ throw new DataGridValidationException( "EndEdit was canceled." );
+ }
+ catch( Exception exception )
+ {
+ // This method will set a validation error on the row and throw back a DataGridValidationException so that
+ // the row stays in edition.
+ Row.SetRowValidationErrorOnException( this, exception );
+ }
+
+ this.IsEndingEdition = true;
+
+ try
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext == null )
+ {
+ Debug.Fail( "DataGridContext cannot be null." );
+ return;
+ }
+
+ IDisposable deferRefresh = ( this is DataRow )
+ ? DataRow.DeferCollectionViewRefresh( dataGridContext ) : null;
+
+ try
+ {
+ this.EndEditCore();
+ this.TerminateEditionFromEndEdit();
+ }
+ finally
+ {
+ if( deferRefresh != null )
+ {
+ deferRefresh.Dispose();
+ }
+ }
+
+ this.PreEditEnded();
+
+ // Item validation has passed if we reached this far.
+ // Since it is the Row's HasValidationError which drives the error styles, setting
+ // the ItemValidationError to null will not get in the way of the row's cells validation.
+ this.SetValidationError( null );
+
+ }
+ finally
+ {
+ this.IsEndingEdition = false;
+ }
+
+ this.OnEditEnded();
+ }
+
+ private void TerminateEditionFromEndEdit()
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl gridControl = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ // Lower IsDirty flags.
+ foreach( Cell cell in this.CreatedCells )
+ {
+ cell.SetIsDirty( false );
+ cell.SetIsDirtyFromInitializingInsertionRow( false );
+ }
+
+ this.SetIsDirty( false );
+
+ // Reset current row in edition.
+ if( gridControl != null )
+ gridControl.UpdateCurrentRowInEditionCellStates( null, null );
+
+ this.SetIsBeingEdited( false );
+
+ this.UpdateCellsContentBindingTarget();
+
+ if( ( this.NavigationBehavior == NavigationBehavior.RowOnly ) && ( this.IsCurrent ) )
+ {
+ if( gridControl != null )
+ gridControl.QueueClearCurrentColumn( gridControl.CurrentItem );
+ }
+ }
+
+ internal virtual void PreEditEnded()
+ {
+ }
+
+ protected virtual void EndEditCore()
+ {
+ bool hasRestrictiveError = false;
+
+ // Create a clone of the list to avoid concurrent access when iterating
+ // and a Cell is added to CreatedCells because of ColumnVirtualization
+ List createdCells = new List| ();
+
+ Cell editCell = null;
+
+ foreach( Cell cell in this.CreatedCells )
+ {
+ createdCells.Add( cell );
+
+ if( cell.IsBeingEdited )
+ editCell = cell;
+ }
+
+ if( editCell != null )
+ {
+ try
+ {
+ editCell.EndEdit( true, true, true );
+ hasRestrictiveError |= Cell.GetIsValidationErrorRestrictive( editCell.ValidationError );
+ }
+ catch( DataGridValidationException )
+ {
+ hasRestrictiveError = true;
+ }
+ }
+
+ foreach( Cell cell in createdCells )
+ {
+ if( cell == editCell )
+ continue;
+
+ Nullable contentBindingUpdateSourceTrigger = cell.GetContentBindingUpdateSourceTrigger();
+
+ bool updateContentBindingSource = ( contentBindingUpdateSourceTrigger == DataGridUpdateSourceTrigger.RowEndingEdit );
+
+ bool cascadeValidate = updateContentBindingSource;
+
+ Exception exception;
+ CellValidationRule ruleInError;
+
+ ValidationResult result =
+ cell.ValidateAndSetAllErrors( true, true, updateContentBindingSource, cascadeValidate, out exception, out ruleInError );
+
+ if( ( !result.IsValid ) && ( Cell.GetIsRuleInErrorRestrictive( ruleInError ) ) )
+ hasRestrictiveError = true;
+ }
+
+ // Throwing a DataGridValidationException will be caught by the grid and will make the cell stay in edition.
+ if( hasRestrictiveError )
+ throw new DataGridValidationException( "Row.EndEdit cannot complete because the cell content is invalid." );
+ }
+
+ public static readonly RoutedEvent EditCancelingEvent = EventManager.RegisterRoutedEvent( "EditCanceling", RoutingStrategy.Bubble, typeof( RoutedEventHandler ), typeof( Row ) );
+ public static readonly RoutedEvent EditCanceledEvent = EventManager.RegisterRoutedEvent( "EditCanceled", RoutingStrategy.Bubble, typeof( RoutedEventHandler ), typeof( Row ) );
+
+ public event RoutedEventHandler EditCanceling
+ {
+ add
+ {
+ base.AddHandler( Row.EditCancelingEvent, value );
+ }
+ remove
+ {
+ base.RemoveHandler( Row.EditCancelingEvent, value );
+ }
+ }
+
+ public event RoutedEventHandler EditCanceled
+ {
+ add
+ {
+ base.AddHandler( Row.EditCanceledEvent, value );
+ }
+ remove
+ {
+ base.RemoveHandler( Row.EditCanceledEvent, value );
+ }
+ }
+
+ protected virtual void OnEditCanceling()
+ {
+ RoutedEventArgs e = new RoutedEventArgs( Row.EditCancelingEvent, this );
+ this.RaiseEvent( e );
+ }
+
+ protected virtual void OnEditCanceled()
+ {
+ RoutedEventArgs e = new RoutedEventArgs( Row.EditCanceledEvent, this );
+ this.RaiseEvent( e );
+ }
+
+ public void CancelEdit()
+ {
+ this.CancelEdit( false );
+ }
+
+ internal void CancelEdit( bool forceCancelEdit )
+ {
+ if( !this.IsBeingEdited )
+ return;
+
+ if( this.IsCancelingEdition )
+ throw new InvalidOperationException( "An attempt was made to cancel the edit process while it is already in the process of being canceled." );
+
+ if( ( !forceCancelEdit ) && ( this.IsEndingEdition ) )
+ throw new InvalidOperationException( "An attempt was made to cancel the edit process while it is being ended." );
+
+ this.OnEditCanceling();
+ this.IsCancelingEdition = true;
+
+ try
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+ IDisposable deferRefresh = null;
+
+ if( dataGridContext != null )
+ {
+ deferRefresh = ( this is DataRow )
+ ? DataRow.DeferCollectionViewRefresh( dataGridContext ) : null;
+ }
+
+ try
+ {
+ this.CancelEditCore();
+ this.TerminateEditionFromCancelEdit();
+ }
+ finally
+ {
+ if( deferRefresh != null )
+ {
+ deferRefresh.Dispose();
+ }
+ }
+
+ this.PreEditCanceled();
+
+ // A row cannot have an ItemValidationError when not being edited.
+ // Since it is the Row's HasValidationError which drives the error styles, setting
+ // the ItemValidationError to null will not get in the way of the row's cells validation.
+ this.SetValidationError( null );
+
+ }
+ finally
+ {
+ this.IsCancelingEdition = false;
+ }
+
+ this.OnEditCanceled();
+ }
+
+ private void TerminateEditionFromCancelEdit()
+ {
+ this.SetIsDirty( false );
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl gridControl = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ if( gridControl != null )
+ gridControl.UpdateCurrentRowInEditionCellStates( null, null );
+
+ this.SetIsBeingEdited( false );
+
+ if( ( this.NavigationBehavior == NavigationBehavior.RowOnly )
+ && ( this.IsCurrent ) )
+ {
+ if( gridControl != null )
+ gridControl.QueueClearCurrentColumn( gridControl.CurrentItem );
+ }
+ }
+
+ internal virtual void PreEditCanceled()
+ {
+ }
+
+ protected virtual void CancelEditCore()
+ {
+ // Restore the Cell.Content
+ foreach( Cell cell in this.CreatedCells )
+ {
+ if( cell.IsBeingEdited )
+ cell.CancelEdit();
+
+ cell.RevertEditedValue();
+ }
+
+#if DEBUG
+ foreach( Cell cell in this.CreatedCells )
+ {
+ Debug.Assert( Cell.GetIsValidationErrorRestrictive( cell.ValidationError ) == false );
+ }
+#endif
+ }
+
+ #endregion EDITION
+
+ #region DP CHANGED HANDLERS
+
+ private static void OnIsBeingEditedChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ Row row = ( Row )sender;
+
+ row.UpdateNavigationBehavior();
+ row.UpdateCellsFocusableStatus();
+
+ //If the current CellEditorDisplayConditions requires display when Row is Editing
+ if( Row.IsCellEditorDisplayConditionsSet( row, CellEditorDisplayConditions.RowIsBeingEdited ) )
+ {
+ if( ( bool )e.NewValue )
+ {
+ //Display the editors for the Row
+ row.SetDisplayEditorMatchingCondition( CellEditorDisplayConditions.RowIsBeingEdited );
+ }
+ else
+ {
+ //Hide the editors for the Row
+ row.RemoveDisplayEditorMatchingCondition( CellEditorDisplayConditions.RowIsBeingEdited );
+ }
+ }
+
+ // In case a value was explicitly specified on the Cell's ParentColumn
+ row.RefreshCellsDisplayedTemplate();
+ }
+
+ private static void OnIsCurrentChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ Row row = ( Row )sender;
+
+ if( row == null )
+ return;
+
+ // In case a value was explicitly specified on the Cell's ParentColumn
+ row.RefreshCellsDisplayedTemplate();
+ }
+
+ private static void OnCellEditorDisplayConditionsChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ Row obj = ( Row )sender;
+
+ obj.UpdateMatchingDisplayConditions();
+ }
+
+ private static void OnNavigationBehaviorChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ Row obj = ( Row )sender;
+
+ obj.UpdateNavigationBehavior();
+ obj.UpdateCellsFocusableStatus();
+ }
+
+ private static void OnParentGridControlChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ DataGridControl grid = e.NewValue as DataGridControl;
+ Row row = sender as Row;
+
+ if( ( row != null ) && ( grid != null ) )
+ {
+ row.PrepareDefaultStyleKey( grid.GetView() );
+ }
+ }
+
+ private static void OnCellIsDirty_ClassHandler( object sender, RoutedEventArgs e )
+ {
+ Row row = ( Row )sender;
+
+ row.SetIsDirty( true );
+
+ e.Source = row;
+ }
+
+ #endregion DP CHANGED HANDLERS
+
+ #region COMMANDS
+
+ private void OnBeginEditExecuted( object sender, ExecutedRoutedEventArgs e )
+ {
+ try
+ {
+ this.BeginEdit();
+ }
+ catch( DataGridException )
+ {
+ // We swallow exception if it occurs because of a validation error or Cell was readonly or
+ // any other GridException.
+ }
+ }
+
+ private void OnBeginEditCanExecute( object sender, CanExecuteRoutedEventArgs e )
+ {
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ DataGridControl parentGrid = ( dataGridContext != null )
+ ? dataGridContext.DataGridControl
+ : null;
+
+ if( parentGrid == null )
+ {
+ e.CanExecute = false;
+ }
+ else
+ {
+ e.CanExecute = ( !this.IsBeingEdited ) && ( !this.ReadOnly )
+ && ( this.IsEditTriggerSet( EditTriggers.BeginEditCommand ) );
+ }
+ }
+
+ private void OnCancelEditExecuted( object sender, ExecutedRoutedEventArgs e )
+ {
+ this.CancelEdit();
+ }
+
+ private void OnCancelEditCanExecute( object sender, CanExecuteRoutedEventArgs e )
+ {
+ e.CanExecute = this.IsBeingEdited;
+ }
+
+ private void OnEndEditExecuted( object sender, ExecutedRoutedEventArgs e )
+ {
+ this.OnEndEditCommandExecutedCore( e );
+ }
+
+ internal virtual void OnEndEditCommandExecutedCore( ExecutedRoutedEventArgs e )
+ {
+ try
+ {
+ this.EndEdit();
+ }
+ catch( DataGridException )
+ {
+ // We swallow exception if it occurs because of a validation error or Cell was read-only or
+ // any other GridException.
+ }
+ }
+
+ private void OnEndEditCanExecute( object sender, CanExecuteRoutedEventArgs e )
+ {
+ this.OnEndEditCommandCanExecuteCore( e );
+ }
+
+ internal virtual void OnEndEditCommandCanExecuteCore( CanExecuteRoutedEventArgs e )
+ {
+ e.CanExecute = this.IsBeingEdited;
+ }
+
+ #endregion COMMANDS
+
+ #region IPrintInfo Members
+
+ double IPrintInfo.GetPageRightOffset( double horizontalOffset, double viewportWidth )
+ {
+ IPrintInfo subPrintInfo = this.CellsHostPanel as IPrintInfo;
+
+ if( subPrintInfo != null )
+ return subPrintInfo.GetPageRightOffset( horizontalOffset, viewportWidth );
+
+ return horizontalOffset + viewportWidth;
+ }
+
+ void IPrintInfo.UpdateElementVisibility( double horizontalOffset, double viewportWidth, object state )
+ {
+ IPrintInfo subPrintInfo = this.CellsHostPanel as IPrintInfo;
+
+ if( subPrintInfo != null )
+ subPrintInfo.UpdateElementVisibility( horizontalOffset, viewportWidth, state );
+ }
+
+ object IPrintInfo.CreateElementVisibilityState()
+ {
+ IPrintInfo subPrintInfo = this.CellsHostPanel as IPrintInfo;
+ if( subPrintInfo == null )
+ return null;
+
+ return subPrintInfo.CreateElementVisibilityState();
+ }
+
+ #endregion
+
+ #region IDataGridItemContainer Members
+
+ void IDataGridItemContainer.PrepareContainer( DataGridContext dataGridContext, object item )
+ {
+ this.PrepareContainer( dataGridContext, item );
+ }
+
+ void IDataGridItemContainer.ClearContainer()
+ {
+ this.ClearContainer();
+ }
+
+ #endregion
+
+ private DataGridControl m_parentGridUsedForCellsGeneration; // = null
+ private Panel m_oldCellsHostPanel; // = null
+ private Panel m_cellsHostPanel; // = null
+ private Dictionary m_templateCells = new Dictionary();
+ private BitVector32 m_flags = new BitVector32();
+
+ [Flags]
+ private enum RowFlags
+ {
+ IsBeginningEdition = 1,
+ IsEndingEdition = 2,
+ IsCancelingEdition = 4,
+ IsBeginningEditFromCell = 8,
+ IsBeingEditedCache = 16,
+ IsClearingContainer = 32,
+ IsContainerPrepared = 64,
+ IsInitializingInsertionRow = 128
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowSelector.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowSelector.cs
new file mode 100644
index 00000000..71004689
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowSelector.cs
@@ -0,0 +1,742 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System.Windows.Controls;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Controls.Primitives;
+using System;
+using System.ComponentModel;
+using System.Collections.ObjectModel;
+using System.Windows.Data;
+using Xceed.Wpf.DataGrid.Views;
+
+namespace Xceed.Wpf.DataGrid
+{
+ [TemplatePart( Name = "PART_RowResizerThumb", Type = typeof( Thumb ) )]
+ public class RowSelector : ContentControl, INotifyPropertyChanged
+ {
+ static RowSelector()
+ {
+ RowSelector.IsPressedProperty = RowSelector.IsPressedPropertyKey.DependencyProperty;
+ RowSelector.RowTypeProperty = RowSelector.RowTypePropertyKey.DependencyProperty;
+
+ FocusableProperty.OverrideMetadata( typeof( RowSelector ),
+ new FrameworkPropertyMetadata( false ) );
+
+ DataGridControl.ParentDataGridControlPropertyKey.OverrideMetadata( typeof( RowSelector ),
+ new FrameworkPropertyMetadata( new PropertyChangedCallback( RowSelector.OnParentGridControlChanged ) ) );
+
+ m_sItemIndexBinding = new Binding();
+ m_sItemIndexBinding.Path = new PropertyPath( DataGridVirtualizingPanel.ItemIndexProperty );
+ m_sItemIndexBinding.Mode = BindingMode.OneWay;
+
+ m_sIsCurrentBinding = new Binding();
+ m_sIsCurrentBinding.Path = new PropertyPath( Row.IsCurrentProperty );
+ m_sIsCurrentBinding.Mode = BindingMode.OneWay;
+
+ m_sIsBeingEditedBinding = new Binding();
+ m_sIsBeingEditedBinding.Path = new PropertyPath( Row.IsBeingEditedProperty );
+ m_sIsBeingEditedBinding.Mode = BindingMode.OneWay;
+
+ m_sHasValidationErrorBinding = new Binding();
+ m_sHasValidationErrorBinding.Path = new PropertyPath( Row.HasValidationErrorProperty );
+ m_sHasValidationErrorBinding.Mode = BindingMode.OneWay;
+
+ m_sRowSelectorVisibleBinding = new Binding();
+ m_sRowSelectorVisibleBinding.Path = new PropertyPath( RowSelector.VisibleProperty );
+ m_sRowSelectorVisibleBinding.Mode = BindingMode.OneWay;
+ m_sRowSelectorVisibleBinding.Converter = new BooleanToVisibilityConverter();
+ m_sRowSelectorVisibleBinding.ConverterParameter = Visibility.Collapsed;
+
+ m_sRowSelectorStyleBinding = new Binding();
+ m_sRowSelectorStyleBinding.Path = new PropertyPath( RowSelector.RowSelectorStyleProperty );
+ m_sRowSelectorStyleBinding.Mode = BindingMode.OneWay;
+
+ m_sAllowResizeBinding = new Binding();
+ m_sAllowResizeBinding.RelativeSource = new RelativeSource( RelativeSourceMode.Self );
+ m_sAllowResizeBinding.Path = new PropertyPath( "(0).(1)",
+ DataGridControl.DataGridContextProperty,
+ TableView.AllowRowResizeProperty );
+
+ m_sAllowResizeBinding.Mode = BindingMode.OneWay;
+ }
+
+ internal RowSelector()
+ {
+ this.DataContext = null;
+
+ BindingOperations.SetBinding( this, RowSelector.IsCurrentProperty, m_sIsCurrentBinding );
+ BindingOperations.SetBinding( this, RowSelector.IsBeingEditedProperty, m_sIsBeingEditedBinding );
+ BindingOperations.SetBinding( this, RowSelector.ItemIndexProperty, m_sItemIndexBinding );
+ BindingOperations.SetBinding( this, RowSelector.HasValidationErrorProperty, m_sHasValidationErrorBinding );
+ BindingOperations.SetBinding( this, RowSelector.VisibilityProperty, m_sRowSelectorVisibleBinding );
+ BindingOperations.SetBinding( this, RowSelector.RowSelectorStyleProperty, m_sRowSelectorStyleBinding );
+ BindingOperations.SetBinding( this, RowSelector.AllowResizeProperty, m_sAllowResizeBinding );
+ }
+
+ #region AllowResize Property
+
+ internal static readonly DependencyProperty AllowResizeProperty = DependencyProperty.Register(
+ "AllowResize",
+ typeof( bool ),
+ typeof( RowSelector ),
+ new FrameworkPropertyMetadata( true ) );
+
+ internal bool AllowResize
+ {
+ get
+ {
+ return ( bool )this.GetValue( RowSelector.AllowResizeProperty );
+ }
+ set
+ {
+ this.SetValue( RowSelector.AllowResizeProperty, value );
+ }
+ }
+
+ private bool CanBeResized( Orientation orientation )
+ {
+ if( this.AllowResize )
+ {
+ FrameworkElement rowItem = this.DataContext as FrameworkElement;
+ if( rowItem != null )
+ {
+ if( orientation == Orientation.Vertical )
+ {
+ object minHeight = rowItem.ReadLocalValue( FrameworkElement.MinHeightProperty );
+ object maxHeight = rowItem.ReadLocalValue( FrameworkElement.MaxHeightProperty );
+
+ if( ( minHeight == DependencyProperty.UnsetValue )
+ || ( maxHeight == DependencyProperty.UnsetValue )
+ || ( !object.Equals( minHeight, maxHeight ) ) )
+ {
+ return true;
+ }
+ }
+ else
+ {
+ object minWidth = rowItem.ReadLocalValue( FrameworkElement.MinWidthProperty );
+ object maxWidth = rowItem.ReadLocalValue( FrameworkElement.MaxWidthProperty );
+
+ if( ( minWidth == DependencyProperty.UnsetValue )
+ || ( maxWidth == DependencyProperty.UnsetValue )
+ || ( !object.Equals( minWidth, maxWidth ) ) )
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ #endregion AllowResize Property
+
+ #region INotifyPropertyChanged Members
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private void NotifyPropertyChanged( string propertyName )
+ {
+ if( this.PropertyChanged != null )
+ this.PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
+ }
+
+ #endregion
+
+ #region Orientation Property
+
+ public Orientation Orientation
+ {
+ get
+ {
+ return ( Orientation )GetValue( OrientationProperty );
+ }
+ set
+ {
+ SetValue( OrientationProperty, value );
+ }
+ }
+
+ // Using a DependencyProperty as the backing store for Orientation. This enables animation, styling, binding, etc...
+ public static readonly DependencyProperty OrientationProperty =
+ DependencyProperty.Register( "Orientation", typeof( Orientation ), typeof( RowSelector ), new UIPropertyMetadata( Orientation.Vertical ) );
+
+ #endregion
+
+ #region IsPressed Read-Only Property
+
+ private static readonly DependencyPropertyKey IsPressedPropertyKey =
+ DependencyProperty.RegisterReadOnly( "IsPressed", typeof( bool ), typeof( RowSelector ), new PropertyMetadata( false ) );
+
+ public static readonly DependencyProperty IsPressedProperty;
+
+ public bool IsPressed
+ {
+ get
+ {
+ return ( bool )this.GetValue( RowSelector.IsPressedProperty );
+ }
+ }
+
+ private void SetIsPressed( bool value )
+ {
+ this.SetValue( RowSelector.IsPressedPropertyKey, value );
+ }
+
+ #endregion IsPressed Read-Only Property
+
+ #region IsCurrent
+
+ public static readonly DependencyProperty IsCurrentProperty = DependencyProperty.Register( "IsCurrent", typeof( bool ), typeof( RowSelector ), new FrameworkPropertyMetadata( false ) );
+
+ public bool IsCurrent
+ {
+ get
+ {
+ return ( bool )this.GetValue( IsCurrentProperty );
+ }
+ }
+
+ #endregion
+
+ #region IsBeingEdited
+
+ public static readonly DependencyProperty IsBeingEditedProperty = DependencyProperty.Register( "IsBeingEdited", typeof( bool ), typeof( RowSelector ), new FrameworkPropertyMetadata( false ) );
+
+ public bool IsBeingEdited
+ {
+ get
+ {
+ return ( bool )this.GetValue( IsBeingEditedProperty );
+ }
+ }
+
+ #endregion
+
+ #region ItemIndex
+
+ public static readonly DependencyProperty ItemIndexProperty = DataGridVirtualizingPanel.ItemIndexProperty.AddOwner( typeof( RowSelector ) );
+
+ public int ItemIndex
+ {
+ get
+ {
+ return ( int )this.GetValue( ItemIndexProperty );
+ }
+ }
+
+ #endregion
+
+ #region HasValidationError
+
+ public static readonly DependencyProperty HasValidationErrorProperty = DependencyProperty.Register( "HasValidationError", typeof( bool ), typeof( RowSelector ), new FrameworkPropertyMetadata( false ) );
+
+ public bool HasValidationError
+ {
+ get
+ {
+ return ( bool )this.GetValue( HasValidationErrorProperty );
+ }
+ }
+
+ #endregion
+
+ #region RowType
+
+ private static readonly DependencyPropertyKey RowTypePropertyKey
+ = DependencyProperty.RegisterReadOnly( "RowType", typeof( Type ), typeof( RowSelector ),
+ new FrameworkPropertyMetadata( null ) );
+
+ public static readonly DependencyProperty RowTypeProperty;
+
+ public Type RowType
+ {
+ get
+ {
+ return ( Type )this.GetValue( RowSelector.RowTypeProperty );
+ }
+ }
+
+ private void SetRowType( Type value )
+ {
+ this.SetValue( RowSelector.RowTypePropertyKey, value );
+ }
+
+ #endregion
+
+ #region ContainerRect
+
+ public static readonly DependencyProperty ContainerRectProperty =
+ DependencyProperty.Register( "ContainerRect", typeof( Rect ), typeof( RowSelector ),
+ new FrameworkPropertyMetadata( null ) );
+
+ public Rect ContainerRect
+ {
+ get
+ {
+ return ( Rect )this.GetValue( ContainerRectProperty );
+ }
+ internal set
+ {
+ this.SetValue( ContainerRectProperty, value );
+ }
+ }
+
+ #endregion
+
+ #region Visible Attached Property
+
+ public static readonly DependencyProperty VisibleProperty =
+ DependencyProperty.RegisterAttached( "Visible", typeof( bool ), typeof( RowSelector ),
+ new FrameworkPropertyMetadata( true ) );
+
+ public static bool GetVisible( DependencyObject d )
+ {
+ return ( bool )d.GetValue( RowSelector.VisibleProperty );
+ }
+
+ public static void SetVisible( DependencyObject d, bool value )
+ {
+ d.SetValue( RowSelector.VisibleProperty, value );
+ }
+
+ #endregion
+
+ #region RowSelectorStyle Attached Property
+
+ public static readonly DependencyProperty RowSelectorStyleProperty =
+ DependencyProperty.RegisterAttached( "RowSelectorStyle", typeof( Style ), typeof( RowSelector ),
+ new FrameworkPropertyMetadata( null, new PropertyChangedCallback( OnRowSelectorStyleChanged ) ) );
+
+ public static Style GetRowSelectorStyle( DependencyObject obj )
+ {
+ return ( Style )obj.GetValue( RowSelectorStyleProperty );
+ }
+
+ public static void SetRowSelectorStyle( DependencyObject obj, Style value )
+ {
+ obj.SetValue( RowSelectorStyleProperty, value );
+ }
+
+ private static void OnRowSelectorStyleChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ RowSelector rowSelector = sender as RowSelector;
+
+ //Here, since the property is an Attached propety, the type of sender can be anything, but in this particular case, I want
+ //to filter out everything but RowSelector since this is my queue to set a local style on the RowSelector ( or clear the local
+ //value).
+ if( rowSelector == null )
+ return;
+
+ if( e.NewValue == null )
+ {
+ rowSelector.ClearValue( RowSelector.StyleProperty );
+ }
+ else
+ {
+ rowSelector.SetValue( RowSelector.StyleProperty, e.NewValue );
+ }
+ }
+
+ #endregion
+
+ #region ReferenceElement Internal Property
+
+ internal static readonly DependencyProperty ReferenceElementProperty =
+ DependencyProperty.Register( "ReferenceElement", typeof( FrameworkElement ), typeof( RowSelector ),
+ new FrameworkPropertyMetadata( null ) );
+
+ internal FrameworkElement ReferenceElement
+ {
+ get
+ {
+ return ( FrameworkElement )this.GetValue( ReferenceElementProperty );
+ }
+ set
+ {
+ this.SetValue( ReferenceElementProperty, value );
+ }
+ }
+
+ #endregion
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ this.SetupColumnResizerThumb();
+ }
+
+ protected override void OnPropertyChanged( DependencyPropertyChangedEventArgs e )
+ {
+ base.OnPropertyChanged( e );
+
+ if( e.Property == RowSelector.DataContextProperty )
+ {
+ Type rowType = null;
+
+ Row row = this.DataContext as Row;
+ if( row != null )
+ {
+ rowType = row.GetType();
+ }
+ else
+ {
+ HeaderFooterItem hfi = this.DataContext as HeaderFooterItem;
+ if( hfi != null )
+ {
+ row = hfi.AsVisual() as Row;
+ if( row != null )
+ {
+ rowType = row.GetType();
+ }
+ }
+ }
+
+ this.SetRowType( rowType );
+
+ }
+ }
+
+ private static void OnParentGridControlChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ RowSelector rowSelector = ( RowSelector )sender;
+ DataGridControl newGrid = e.NewValue as DataGridControl;
+
+ if( newGrid != null )
+ {
+ rowSelector.PrepareDefaultStyleKey( newGrid.GetView() );
+ }
+ }
+
+ internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( RowSelector ) );
+ }
+
+ #region Row Resizer Code
+
+ private void SetupColumnResizerThumb()
+ {
+ if( m_rowResizerThumb != null )
+ {
+ m_rowResizerThumb.DragStarted -= new DragStartedEventHandler( m_rowResizerThumb_DragStarted );
+ m_rowResizerThumb.DragDelta -= new DragDeltaEventHandler( m_rowResizerThumb_DragDelta );
+ m_rowResizerThumb.DragCompleted -= new DragCompletedEventHandler( m_rowResizerThumb_DragCompleted );
+ m_rowResizerThumb.QueryCursor -= new QueryCursorEventHandler( m_rowResizerThumb_QueryCursor );
+ m_rowResizerThumb.MouseDoubleClick -= new MouseButtonEventHandler( m_rowResizerThumb_MouseDoubleClick );
+
+ m_rowResizerThumb = null;
+ }
+
+ m_rowResizerThumb = this.GetTemplateChild( "PART_RowResizerThumb" ) as Thumb;
+
+ if( m_rowResizerThumb != null )
+ {
+ m_rowResizerThumb.DragStarted += new DragStartedEventHandler( m_rowResizerThumb_DragStarted );
+ m_rowResizerThumb.DragDelta += new DragDeltaEventHandler( m_rowResizerThumb_DragDelta );
+ m_rowResizerThumb.DragCompleted += new DragCompletedEventHandler( m_rowResizerThumb_DragCompleted );
+ m_rowResizerThumb.QueryCursor += new QueryCursorEventHandler( m_rowResizerThumb_QueryCursor );
+ m_rowResizerThumb.MouseDoubleClick += new MouseButtonEventHandler( m_rowResizerThumb_MouseDoubleClick );
+ }
+ }
+
+ private void m_rowResizerThumb_MouseDoubleClick( object sender, MouseButtonEventArgs e )
+ {
+ FrameworkElement rowItem = ( FrameworkElement )this.DataContext;
+
+ if( rowItem == null )
+ return;
+
+ if( !this.CanBeResized( this.Orientation ) )
+ return;
+
+ // Resets the row width or height depending on the orientation.
+ DependencyProperty property = ( this.Orientation == Orientation.Vertical )
+ ? FrameworkElement.HeightProperty
+ : FrameworkElement.WidthProperty;
+
+ rowItem.ClearValue( property );
+ }
+
+ private void m_rowResizerThumb_QueryCursor( object sender, QueryCursorEventArgs e )
+ {
+ FrameworkElement rowItem = ( FrameworkElement )this.DataContext;
+
+ if( rowItem == null )
+ return;
+
+ if( this.CanBeResized( this.Orientation ) )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext != null )
+ {
+ TableView tableView = dataGridContext.DataGridControl.GetView() as TableView;
+
+ e.Cursor = ( ( tableView != null ) && ( this.Orientation == Orientation.Vertical ) )
+ ? tableView.RowSelectorResizeNorthSouthCursor
+ : tableView.RowSelectorResizeWestEastCursor;
+ }
+
+ if( e.Cursor == null )
+ {
+ e.Cursor = ( this.Orientation == Orientation.Vertical )
+ ? TableView.DefaultRowSelectorResizeNorthSouthCursor
+ : TableView.DefaultRowSelectorResizeWestEastCursor;
+ }
+ }
+
+ e.Handled = true;
+ }
+
+ private void m_rowResizerThumb_DragStarted( object sender, DragStartedEventArgs e )
+ {
+ if( !this.CanBeResized( this.Orientation ) )
+ return;
+
+ FrameworkElement rowItem = ( FrameworkElement )this.DataContext;
+
+ if( rowItem == null )
+ return;
+
+ if( this.Orientation == Orientation.Vertical )
+ {
+ m_originalSize = rowItem.ActualHeight;
+ }
+ else
+ {
+ m_originalSize = rowItem.ActualWidth;
+ }
+ }
+
+ private void m_rowResizerThumb_DragDelta( object sender, DragDeltaEventArgs e )
+ {
+ if( !this.CanBeResized( this.Orientation ) )
+ return;
+
+ FrameworkElement rowItem = ( FrameworkElement )this.DataContext;
+
+ if( rowItem == null )
+ return;
+
+ double newSize;
+
+ if( this.Orientation == Orientation.Vertical )
+ {
+ newSize = rowItem.ActualHeight + e.VerticalChange;
+ }
+ else
+ {
+ newSize = rowItem.ActualWidth + e.HorizontalChange;
+ }
+
+ if( newSize < MIN_SIZE )
+ {
+ newSize = MIN_SIZE;
+ }
+
+ if( this.Orientation == Orientation.Vertical )
+ {
+ if( newSize < rowItem.MinHeight )
+ {
+ newSize = rowItem.MinHeight;
+ }
+ else if( newSize > rowItem.MaxHeight )
+ {
+ newSize = rowItem.MaxHeight;
+ }
+
+ rowItem.Height = newSize;
+ }
+ else
+ {
+ if( newSize < rowItem.MinWidth )
+ {
+ newSize = rowItem.MinWidth;
+ }
+ else if( newSize > rowItem.MaxWidth )
+ {
+ newSize = rowItem.MaxWidth;
+ }
+
+ rowItem.Width = newSize;
+ }
+ }
+
+ private void m_rowResizerThumb_DragCompleted( object sender, DragCompletedEventArgs e )
+ {
+ if( !this.CanBeResized( this.Orientation ) )
+ return;
+
+ FrameworkElement rowItem = ( FrameworkElement )this.DataContext;
+
+ if( rowItem == null )
+ return;
+
+ if( e.Canceled )
+ {
+ if( this.Orientation == Orientation.Vertical )
+ {
+ rowItem.Height = m_originalSize;
+ }
+ else
+ {
+ rowItem.Width = m_originalSize;
+ }
+ }
+
+ m_originalSize = -1d;
+ }
+
+ private const double MIN_SIZE = 4d;
+
+ private double m_originalSize = -1d;
+ private Thumb m_rowResizerThumb; // = null
+
+ #endregion Column Resizer Code
+
+ #region Button Behavior Code
+
+ protected override void OnMouseLeftButtonDown( MouseButtonEventArgs e )
+ {
+ m_IsFromMouseButtonDown = true;
+
+ if( this.CaptureMouse() )
+ {
+ this.SetIsPressed( true );
+ e.Handled = true;
+
+ FrameworkElement rowItemContainer = this.DataContext as FrameworkElement;
+
+ if( rowItemContainer != null )
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( rowItemContainer );
+
+ if( dataGridContext != null )
+ {
+ DataGridControl dataGridControl = dataGridContext.DataGridControl;
+
+ // Ensure to push the RowSelector source to the SelectionChangeManager to be able to
+ // detect that a RowSelector was used to perform a selection of Row(s) and/or Cell(s)
+ using( dataGridControl.SelectionChangerManager.PushUpdateSelectionSource( SelectionManager.UpdateSelectionSource.RowSelector ) )
+ {
+ if( dataGridControl.SetFocusHelper( rowItemContainer, dataGridContext.CurrentColumn, true, true, true ) )
+ {
+ // Keep a reference to the mouse position so we can calculate when a drag operation is actually started.
+ dataGridControl.InitializeDragPostion( e );
+ }
+
+ if( dataGridControl.NavigationBehavior == NavigationBehavior.RowOrCell )
+ {
+ try
+ {
+ dataGridContext.SetCurrentColumnCore( null, false, false );
+ }
+ catch( DataGridException )
+ {
+ // We swallow the exception if it occurs because of a validation error or Cell was read-only or any other GridException.
+ }
+ }
+ }
+ }
+ }
+ }
+
+ base.OnMouseLeftButtonDown( e );
+
+ m_IsFromMouseButtonDown = false;
+ }
+
+ protected override void OnMouseMove( MouseEventArgs e )
+ {
+ bool doDrag = false;
+
+ if( e.LeftButton == MouseButtonState.Pressed )
+ {
+ if( this.IsMouseCaptured )
+ {
+ Rect bounds = new Rect( 0d, 0d, this.ActualWidth, this.ActualHeight );
+ this.SetIsPressed( bounds.Contains( e.GetPosition( this ) ) );
+
+ e.Handled = true;
+ }
+
+ doDrag = true;
+ }
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext != null )
+ {
+ //Do not start the drag operation until we have an actual mouse mouve.
+ if( doDrag && !m_IsFromMouseButtonDown )
+ {
+ dataGridContext.DataGridControl.DoDrag( e );
+ }
+ else
+ {
+ dataGridContext.DataGridControl.ResetDragDataObject();
+ }
+ }
+
+ base.OnMouseMove( e );
+ }
+
+ protected override void OnMouseLeftButtonUp( MouseButtonEventArgs e )
+ {
+ bool isMouseCaptured = this.IsMouseCaptured;
+ bool isPressed = this.IsPressed;
+
+ if( isMouseCaptured )
+ {
+ this.ReleaseMouseCapture();
+ this.SetIsPressed( false );
+ e.Handled = true;
+ }
+
+ base.OnMouseLeftButtonUp( e );
+ }
+
+ protected override void OnLostMouseCapture( MouseEventArgs e )
+ {
+ if( this.IsPressed )
+ {
+ this.SetIsPressed( false );
+ }
+
+ base.OnLostMouseCapture( e );
+ }
+
+ private bool m_IsFromMouseButtonDown = false;
+
+ #endregion Button Behavior Code
+
+ private static readonly Binding m_sItemIndexBinding;
+ private static readonly Binding m_sIsCurrentBinding;
+ private static readonly Binding m_sIsBeingEditedBinding;
+ private static readonly Binding m_sHasValidationErrorBinding;
+ private static readonly Binding m_sRowSelectorVisibleBinding;
+ private static readonly Binding m_sRowSelectorStyleBinding;
+ private static readonly Binding m_sAllowResizeBinding;
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowSelectorPane.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowSelectorPane.cs
new file mode 100644
index 00000000..040a226f
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowSelectorPane.cs
@@ -0,0 +1,380 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections;
+using System.Collections.Specialized;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Media;
+using System.Windows.Threading;
+using Xceed.Wpf.DataGrid.Views;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.ComponentModel;
+using System.Windows.Input;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class RowSelectorPane : Panel
+ {
+ private const double SafeOffScreenCoordinate = -1000000.0d;
+
+ private static readonly Rect RecycleReadyRect = new Rect( RowSelectorPane.SafeOffScreenCoordinate, RowSelectorPane.SafeOffScreenCoordinate, 0, 0 );
+ private static readonly Point OriginPoint = new Point( 0, 0 );
+
+ static RowSelectorPane()
+ {
+ ClipToBoundsProperty.OverrideMetadata( typeof( RowSelectorPane ), new FrameworkPropertyMetadata( true ) );
+
+ FocusableProperty.OverrideMetadata( typeof( RowSelectorPane ), new FrameworkPropertyMetadata( false ) );
+
+ DataGridControl.ParentDataGridControlPropertyKey.OverrideMetadata( typeof( RowSelectorPane ), new FrameworkPropertyMetadata( new PropertyChangedCallback( RowSelectorPane.OnParentGridControlChanged ) ) );
+ }
+
+ //------------------------------
+ // PROPERTIES
+ //------------------------------
+
+ #region Orientation Property
+
+ public Orientation Orientation
+ {
+ get
+ {
+ return ( Orientation )GetValue( OrientationProperty );
+ }
+ set
+ {
+ SetValue( OrientationProperty, value );
+ }
+ }
+
+ // Using a DependencyProperty as the backing store for Orientation. This enables animation, styling, binding, etc...
+ public static readonly DependencyProperty OrientationProperty =
+ DependencyProperty.Register( "Orientation", typeof( Orientation ), typeof( RowSelectorPane ), new UIPropertyMetadata( Orientation.Vertical, new PropertyChangedCallback( OnOrientationChanged ) ) );
+
+
+ private static void OnOrientationChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+
+ }
+
+ #endregion Orientation Property
+
+ internal void SetRowSelectorPosition( DependencyObject container, Rect containerRect, FrameworkElement referenceElement )
+ {
+ Debug.Assert( ( Visibility )this.GetValue( RowSelectorPane.VisibilityProperty ) == Visibility.Visible, "We should not be creating RowSelectors if we are not Visible." );
+
+ RowSelector rowSelector = null;
+
+ //Check if a RowSelector is already assigned to the container passed.
+ if( m_visibleSelectors.TryGetValue( container, out rowSelector ) == false )
+ {
+ //If no RowSelector exists for this container, try to recycle one.
+ if( m_recycleQueue.Count > 0 )
+ {
+ //get next recyclable RowSelector.
+ rowSelector = m_recycleQueue.Dequeue();
+ }
+ else
+ {
+ //Recycling pool empty, create a new RowSelector and place it in the visual tree right away.
+ rowSelector = new RowSelector();
+ this.InternalChildren.Add( rowSelector );
+ }
+
+ //Since there was no RowSelector for the container, map the container to this RowSelector
+ rowSelector.DataContext = container;
+ DataGridControl.SetContainer( rowSelector, container );
+ rowSelector.ReferenceElement = referenceElement;
+ m_visibleSelectors.Add( container, rowSelector );
+ }
+
+ //At this stage, it is not normal that no RowSelector instance is available.
+ Debug.Assert( rowSelector != null );
+
+ if( rowSelector == null )
+ throw new InvalidOperationException( "An attempt was made to set the position of a row selector that does not exist." );
+
+ rowSelector.ContainerRect = containerRect;
+
+ this.InvalidateMeasure();
+ }
+
+ internal void FreeRowSelector( DependencyObject container )
+ {
+ RowSelector rowSelector = null;
+
+ //Check if a RowSelector is already assigned to the container passed.
+ if( m_visibleSelectors.TryGetValue( container, out rowSelector ) == true )
+ {
+ //Remove from Visible list, add to pending cleanup list
+ m_visibleSelectors.Remove( container );
+ m_pendingCleanup.Add( rowSelector );
+
+ //clear data context (container) from the RowSelector.
+ rowSelector.DataContext = null;
+ DataGridControl.ClearContainer( rowSelector );
+
+ //Invalidate Measure since the list of VisibleSelector + PendingCleanup changed
+ this.InvalidateMeasure();
+ }
+ //If there are no RowSelector for container, then do nothing.
+ }
+
+ protected override Size MeasureOverride( Size availableSize )
+ {
+ Size desiredSize = new Size( 0, 0 );
+
+ foreach( RowSelector rowSelector in m_visibleSelectors.Values )
+ {
+ Size rowSelectorAvaillableSize = this.PrepareAvailableSizeForRowSelector( rowSelector, availableSize );
+
+ rowSelector.Measure( rowSelectorAvaillableSize );
+ }
+
+ return desiredSize;
+ }
+
+ protected override Size ArrangeOverride( Size finalSize )
+ {
+ foreach( RowSelector toClean in m_pendingCleanup )
+ {
+ toClean.Arrange( RecycleReadyRect );
+ m_recycleQueue.Enqueue( toClean );
+ }
+
+ m_pendingCleanup.Clear();
+
+ foreach( RowSelector visibleRowSelector in m_visibleSelectors.Values )
+ {
+ Rect rowSelectorArrangeRect = this.PrepareArrangeRectForRowSelector( finalSize, visibleRowSelector );
+
+ visibleRowSelector.Clip = this.PrepareClipForRowSelector( visibleRowSelector, rowSelectorArrangeRect, visibleRowSelector.ReferenceElement );
+
+ visibleRowSelector.Arrange( rowSelectorArrangeRect );
+ }
+
+ //Case 117296: update the mouse status to make sure no container has invalid mouse over status. Only do this when the mouse is over the panel, to
+ //prevent unescessary update when scrolling with thumb
+ if( this.IsMouseOver == true )
+ {
+ Mouse.Synchronize();
+ }
+
+ return finalSize;
+ }
+
+ internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( RowSelectorPane ) );
+ }
+
+ private Rect PrepareArrangeRectForRowSelector( Size finalSize, RowSelector visibleRowSelector )
+ {
+ Rect returnRect;
+
+ FrameworkElement container = visibleRowSelector.DataContext as FrameworkElement;
+ Debug.Assert( container != null );
+
+ GeneralTransform coordinatesTransform = container.TransformToVisual( this );
+ Point origin = coordinatesTransform.Transform( OriginPoint );
+
+ if( this.Orientation == Orientation.Vertical )
+ {
+ returnRect = new Rect( 0, origin.Y, finalSize.Width, container.ActualHeight );
+ }
+ else
+ {
+ returnRect = new Rect( origin.X, 0, container.ActualWidth, finalSize.Height );
+ }
+
+ return returnRect;
+ }
+
+ private Size PrepareAvailableSizeForRowSelector( RowSelector rowSelector, Size availableSize )
+ {
+ Rect containerRect = rowSelector.ContainerRect;
+ Size returnSize;
+
+ if( this.Orientation == Orientation.Vertical )
+ {
+ returnSize = new Size( availableSize.Width, containerRect.Height );
+ }
+ else
+ {
+ returnSize = new Size( containerRect.Width, availableSize.Height );
+ }
+
+ return returnSize;
+ }
+
+ private Geometry PrepareClipForRowSelector( RowSelector rowSelector, Rect arrangeRect, FrameworkElement referenceElement )
+ {
+ if( referenceElement == null )
+ return null;
+
+ GeneralTransform referenceElementToRowSelectorPaneTransform = referenceElement.TransformToVisual( this );
+
+ Rect referenceElementRegion = referenceElementToRowSelectorPaneTransform.TransformBounds( new Rect( 0, 0, referenceElement.ActualWidth, referenceElement.ActualHeight ) );
+
+ RectangleGeometry clipRegion = null;
+
+ if( this.Orientation == Orientation.Vertical )
+ {
+ UIElement container = rowSelector.DataContext as UIElement;
+
+ if( ( container != null ) && ( container.Clip != null ) )
+ {
+ Rect containerClipBounds = container.Clip.Bounds;
+
+ // In this case, we will use the container's clip properties (Top and Bottom).
+ clipRegion = new RectangleGeometry( new Rect( 0d, containerClipBounds.Y, arrangeRect.Width, containerClipBounds.Height ) );
+ }
+ else if( ( arrangeRect.Top < referenceElementRegion.Top ) || ( arrangeRect.Bottom > referenceElementRegion.Bottom ) )
+ {
+ double x = 0d;
+ double y = Math.Max( referenceElementRegion.Top - arrangeRect.Top, 0 );
+
+ double width = arrangeRect.Width;
+ double height = Math.Max( 0, arrangeRect.Height - y - Math.Max( 0, arrangeRect.Bottom - referenceElementRegion.Bottom ) );
+
+
+ clipRegion = new RectangleGeometry( new Rect( x, y, width, height ) );
+ }
+ }
+ else
+ {
+ UIElement container = rowSelector.DataContext as UIElement;
+
+ if( ( container != null ) && ( container.Clip != null ) )
+ {
+ Rect containerClipBounds = container.Clip.Bounds;
+
+ // In this case, we will use the container's clip properties (Left and Right).
+ clipRegion = new RectangleGeometry( new Rect( containerClipBounds.X, 0d, containerClipBounds.Width, arrangeRect.Height ) );
+ }
+ else if( ( arrangeRect.Left < referenceElementRegion.Left ) || ( arrangeRect.Right > referenceElementRegion.Right ) )
+ {
+ double x = Math.Max( referenceElementRegion.Left - arrangeRect.Left, 0 );
+ double y = 0d;
+
+ double width = arrangeRect.Width - x - Math.Max( 0, arrangeRect.Right - referenceElementRegion.Right );
+ double height = arrangeRect.Height;
+
+ clipRegion = new RectangleGeometry( new Rect( x, y, width, height ) );
+ }
+ }
+
+ return clipRegion;
+ }
+
+ //------------------------------
+ // DP CHANGED HANDLERS
+ //------------------------------
+
+ private static void OnParentGridControlChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ RowSelectorPane rowSelectorPane = ( RowSelectorPane )sender;
+ DataGridControl newGrid = e.NewValue as DataGridControl;
+ DataGridControl oldGrid = e.OldValue as DataGridControl;
+
+ if( oldGrid != null )
+ {
+ // The RowSelectorPane must clear its internal state when the ParentGridControl changes
+ rowSelectorPane.m_pendingCleanup.Clear();
+ rowSelectorPane.m_recycleQueue.Clear();
+ rowSelectorPane.m_visibleSelectors.Clear();
+ rowSelectorPane.InternalChildren.Clear();
+ }
+
+ if( newGrid != null )
+ {
+ rowSelectorPane.PrepareDefaultStyleKey( newGrid.GetView() );
+ }
+
+ rowSelectorPane.InvalidateMeasure();
+ }
+
+ #region Obsolete Members
+
+ [Obsolete( "The ItemsPlacementReference property is obsolete and should no longer be used.", true )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ public FrameworkElement ItemsPlacementReference
+ {
+ get
+ {
+ return null;
+ }
+ set
+ {
+ }
+ }
+
+ [Obsolete( "The ItemsPlacementReferenceProperty dependency property is obsolete and should no longer be used.", true )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ public static readonly DependencyProperty ItemsPlacementReferenceProperty;
+
+
+ [Obsolete( "The RowSelectorStyle attached property is obsolete and has been replaced by the RowSelector.RowSelectorStyle attached property.", true )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ public static Style GetRowSelectorStyle( DependencyObject obj )
+ {
+ return null;
+ }
+
+ [Obsolete( "The RowSelectorStyle attached property is obsolete and has been replaced by the RowSelector.RowSelectorStyle attached property.", true )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ public static void SetRowSelectorStyle( DependencyObject obj, Style value )
+ {
+ }
+
+ [Obsolete( "The RowSelectorStyleProperty dependency property is obsolete and has been replaced by the RowSelector.RowSelectorStyleProperty dependency property.", true )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ public static readonly DependencyProperty RowSelectorStyleProperty;
+
+ [Obsolete( "The ScrollViewer property is obsolete and should no longer be used.", true )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ public ScrollViewer ScrollViewer
+ {
+ get
+ {
+ return null;
+ }
+ set
+ {
+ }
+ }
+
+ [Obsolete( "The ScrollViewerProperty dependency property is obsolete and should no longer be used.", true )]
+ [EditorBrowsable( EditorBrowsableState.Never )]
+ public static readonly DependencyProperty ScrollViewerProperty;
+
+ #endregion Obsolete Members
+
+
+ private Queue m_recycleQueue = new Queue();
+ private List m_pendingCleanup = new List();
+ Dictionary m_visibleSelectors = new Dictionary();
+
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowState.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowState.cs
new file mode 100644
index 00000000..206e7175
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowState.cs
@@ -0,0 +1,75 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Collections;
+using System.Windows.Controls;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class RowState
+ {
+ public RowState()
+ {
+ }
+
+ // Restore it only if the Row is being edited
+ public RowValidationError ItemValidationError
+ {
+ get
+ {
+ return m_itemValidationError;
+ }
+ }
+
+ // Restore it only if the Row is being edited
+ public bool IsDirty
+ {
+ get
+ {
+ return m_isDirty;
+ }
+ }
+
+ public RowState Clone()
+ {
+ RowState rowState = new RowState();
+
+ rowState.m_itemValidationError = m_itemValidationError;
+ rowState.m_isDirty = m_isDirty;
+
+ return rowState;
+ }
+
+ internal void SetItemValidationError( RowValidationError value )
+ {
+ m_itemValidationError = value;
+ }
+
+ internal void SetIsDirty( bool value )
+ {
+ m_isDirty = value;
+ }
+
+ private RowValidationError m_itemValidationError; // = null;
+ private bool m_isDirty; // = false
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowValidationError.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowValidationError.cs
new file mode 100644
index 00000000..5418aa7a
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowValidationError.cs
@@ -0,0 +1,80 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Windows.Controls;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class RowValidationError
+ {
+ public RowValidationError(
+ ValidationRule ruleInError,
+ Row rowInError,
+ object errorContent,
+ Exception exception )
+ {
+ m_ruleInError = ruleInError;
+ m_rowInError = rowInError;
+ m_errorContent = errorContent;
+ m_exception = exception;
+ }
+
+ public ValidationRule RuleInError
+ {
+ get
+ {
+ return m_ruleInError;
+ }
+ }
+
+ public Row RowInError
+ {
+ get
+ {
+ return m_rowInError;
+ }
+ }
+
+ public object ErrorContent
+ {
+ get
+ {
+ return m_errorContent;
+ }
+ set
+ {
+ m_errorContent = value;
+ }
+ }
+
+ public Exception Exception
+ {
+ get
+ {
+ return m_exception;
+ }
+ }
+
+ private ValidationRule m_ruleInError;
+ private Row m_rowInError;
+ private object m_errorContent;
+ private Exception m_exception;
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowValidationErrorRoutedEvent.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowValidationErrorRoutedEvent.cs
new file mode 100644
index 00000000..5a6c4e60
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/RowValidationErrorRoutedEvent.cs
@@ -0,0 +1,71 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Windows;
+using System.Windows.Controls;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public delegate void RowValidationErrorRoutedEventHandler( object sender, RowValidationErrorRoutedEventArgs e );
+
+ public class RowValidationErrorRoutedEventArgs : RoutedEventArgs
+ {
+ public RowValidationErrorRoutedEventArgs( RowValidationError rowValidationError )
+ : base()
+ {
+ if( rowValidationError == null )
+ throw new ArgumentNullException( "rowValidationError" );
+
+ m_rowValidationError = rowValidationError;
+ }
+
+ public RowValidationErrorRoutedEventArgs( RoutedEvent routedEvent, RowValidationError rowValidationError )
+ : base( routedEvent )
+ {
+ if( rowValidationError == null )
+ throw new ArgumentNullException( "rowValidationError" );
+
+ m_rowValidationError = rowValidationError;
+ }
+
+ public RowValidationErrorRoutedEventArgs( RoutedEvent routedEvent, object source, RowValidationError rowValidationError )
+ : base( routedEvent, source )
+ {
+ if( rowValidationError == null )
+ throw new ArgumentNullException( "rowValidationError" );
+
+ m_rowValidationError = rowValidationError;
+ }
+
+ #region ValidationError PROPERTY
+
+ public RowValidationError RowValidationError
+ {
+ get
+ {
+ return m_rowValidationError;
+ }
+ }
+
+ private RowValidationError m_rowValidationError;
+
+ #endregion ValidationError PROPERTY
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ScrollChangedWeakEventManager.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ScrollChangedWeakEventManager.cs
new file mode 100644
index 00000000..23d59fd9
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ScrollChangedWeakEventManager.cs
@@ -0,0 +1,84 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Windows.Controls;
+using System.Windows;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class ScrollChangedWeakEventManager : WeakEventManager
+ {
+ private ScrollChangedWeakEventManager()
+ {
+ }
+
+ public static void AddListener( ScrollViewer source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedAddListener( source, listener );
+ }
+
+ public static void RemoveListener( ScrollViewer source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedRemoveListener( source, listener );
+ }
+
+ protected override void StartListening( object source )
+ {
+ ScrollViewer scrollViewer = source as ScrollViewer;
+
+ if( scrollViewer != null )
+ scrollViewer.ScrollChanged += this.ScrollViewer_ScrollChanged;
+ }
+
+ protected override void StopListening( object source )
+ {
+ ScrollViewer scrollViewer = source as ScrollViewer;
+
+ if( scrollViewer != null )
+ scrollViewer.ScrollChanged -= this.ScrollViewer_ScrollChanged;
+ }
+
+ private static ScrollChangedWeakEventManager CurrentManager
+ {
+ get
+ {
+ Type managerType = typeof( ScrollChangedWeakEventManager );
+ ScrollChangedWeakEventManager currentManager =
+ ( ScrollChangedWeakEventManager )WeakEventManager.GetCurrentManager( managerType );
+
+ if( currentManager == null )
+ {
+ currentManager = new ScrollChangedWeakEventManager();
+ WeakEventManager.SetCurrentManager( managerType, currentManager );
+ }
+
+ return currentManager;
+ }
+ }
+
+ private void ScrollViewer_ScrollChanged( object sender, ScrollChangedEventArgs e )
+ {
+ this.DeliverEvent( sender, e );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ScrollTip.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ScrollTip.cs
new file mode 100644
index 00000000..a08905b0
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ScrollTip.cs
@@ -0,0 +1,803 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Controls;
+using System.Windows;
+using System.Windows.Data;
+using System.Collections;
+using System.Windows.Controls.Primitives;
+using Xceed.Utils.Wpf;
+using System.Windows.Input;
+using System.Diagnostics;
+using System.ComponentModel;
+using Xceed.Wpf.DataGrid.Views;
+using System.Collections.Specialized;
+using System.Windows.Media;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public class ScrollTip : ContentControl, IWeakEventListener
+ {
+ #region CONSTRUCTORS
+
+ static ScrollTip()
+ {
+ // This DefaultStyleKey will only be used in design-time.
+ DefaultStyleKeyProperty.OverrideMetadata( typeof( ScrollTip ), new FrameworkPropertyMetadata( new Markup.ThemeKey( typeof( Views.TableView ), typeof( ScrollTip ) ) ) );
+
+ DataGridControl.ParentDataGridControlPropertyKey.OverrideMetadata( typeof( ScrollTip ), new FrameworkPropertyMetadata( new PropertyChangedCallback( ScrollTip.ParentGridControlPropertyChangedCallback ) ) );
+ DataGridControl.DataGridContextPropertyKey.OverrideMetadata( typeof( ScrollTip ), new FrameworkPropertyMetadata( new PropertyChangedCallback( ScrollTip.OnDataGridContextPropertyChanged ) ) );
+ }
+
+ #endregion
+
+ #region PUBLIC METHODS
+
+ public override void OnApplyTemplate()
+ {
+ base.OnApplyTemplate();
+
+ this.ScrollTipContentTemplateNeedsRefresh = true;
+ this.RefreshDefaultScrollTipContentTemplate();
+ }
+
+ #endregion
+
+ #region PROTECTED METHODS
+
+ protected internal virtual void PrepareDefaultStyleKey( Xceed.Wpf.DataGrid.Views.ViewBase view )
+ {
+ this.DefaultStyleKey = view.GetDefaultStyleKey( typeof( ScrollTip ) );
+ }
+
+ #endregion
+
+ #region ScrollTipContentTemplateNeedsRefresh Private Property
+
+ private bool ScrollTipContentTemplateNeedsRefresh
+ {
+ get
+ {
+ return m_flags[ ( int )ScrollTipFlags.ScrollTipContentTemplateNeedsRefresh ];
+ }
+ set
+ {
+ m_flags[ ( int )ScrollTipFlags.ScrollTipContentTemplateNeedsRefresh ] = value;
+ }
+ }
+
+ #endregion
+
+ #region UsingDefaultScrollTipContentTemplate Private Property
+
+ private bool UsingDefaultScrollTipContentTemplate
+ {
+ get
+ {
+ return m_flags[ ( int )ScrollTipFlags.UsingDefaultScrollTipContentTemplate ];
+ }
+ set
+ {
+ m_flags[ ( int )ScrollTipFlags.UsingDefaultScrollTipContentTemplate ] = value;
+ }
+ }
+
+ #endregion
+
+ #region IsInParentGridChanged Private Property
+
+ private bool IsInParentGridChanged
+ {
+ get
+ {
+ return m_flags[ ( int )ScrollTipFlags.IsInParentGridChanged ];
+ }
+ set
+ {
+ m_flags[ ( int )ScrollTipFlags.IsInParentGridChanged ] = value;
+ }
+ }
+
+ #endregion
+
+ #region IsPixelScrolling Private Property
+
+ private bool IsPixelScrolling
+ {
+ get
+ {
+ return m_flags[ ( int )ScrollTipFlags.IsPixelScrolling ];
+ }
+ set
+ {
+ m_flags[ ( int )ScrollTipFlags.IsPixelScrolling ] = value;
+ }
+ }
+
+ #endregion
+
+ #region ShouldDisplayScrollTip Private Property
+
+ private bool ShouldDisplayScrollTip
+ {
+ get
+ {
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+ if( dataGridContext != null )
+ {
+ if( dataGridContext.DataGridControl != null )
+ {
+ UIViewBase uiViewBase = dataGridContext.DataGridControl.GetView() as UIViewBase;
+ if( ( uiViewBase != null ) && ( uiViewBase.ShowScrollTip ) )
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ #endregion
+
+ #region DefaultScrollTipContentTemplateBinding Private Property
+
+ private Binding DefaultScrollTipContentTemplateBinding
+ {
+ get
+ {
+ if( m_defaultScrollTipContentTemplateBinding == null )
+ {
+ m_defaultScrollTipContentTemplateBinding = new Binding();
+ m_defaultScrollTipContentTemplateBinding.Path = new PropertyPath( "(0).ScrollTipContentTemplate", DataGridControl.DataGridContextProperty );
+ m_defaultScrollTipContentTemplateBinding.Source = this;
+ }
+
+ return m_defaultScrollTipContentTemplateBinding;
+ }
+ }
+
+ private Binding m_defaultScrollTipContentTemplateBinding; // = null;
+
+ #endregion
+
+ #region DefaultScrollTipContentTemplateSelectorBinding Private Property
+
+ private Binding DefaultScrollTipContentTemplateSelectorBinding
+ {
+ get
+ {
+ if( m_defaultScrollTipContentTemplateSelectorBinding == null )
+ {
+ m_defaultScrollTipContentTemplateSelectorBinding = new Binding();
+ m_defaultScrollTipContentTemplateSelectorBinding.Path = new PropertyPath( "(0).ScrollTipContentTemplateSelector", DataGridControl.DataGridContextProperty );
+ m_defaultScrollTipContentTemplateSelectorBinding.Source = this;
+ }
+
+ return m_defaultScrollTipContentTemplateSelectorBinding;
+ }
+ }
+
+
+ private Binding m_defaultScrollTipContentTemplateSelectorBinding; // = null;
+
+ #endregion
+
+ #region DefaultCellContentTemplateBinding Private Property
+
+ private Binding DefaultCellContentTemplateBinding
+ {
+ get
+ {
+ if( m_defaultCellContentTemplateBinding == null )
+ {
+ m_defaultCellContentTemplateBinding = new Binding();
+ m_defaultCellContentTemplateBinding.Path = new PropertyPath( "(0).Columns.MainColumn.CellContentTemplate", DataGridControl.DataGridContextProperty );
+ m_defaultCellContentTemplateBinding.Source = this;
+ }
+
+ return m_defaultCellContentTemplateBinding;
+ }
+ }
+
+ private Binding m_defaultCellContentTemplateBinding;
+
+ #endregion
+
+ #region DefaultCellContentTemplateSelectorBinding Private Property
+
+ private Binding DefaultCellContentTemplateSelectorBinding
+ {
+ get
+ {
+ if( m_defaultCellContentTemplateSelectorBinding == null )
+ {
+ m_defaultCellContentTemplateSelectorBinding = new Binding();
+ m_defaultCellContentTemplateSelectorBinding.Path = new PropertyPath( "(0).Columns.MainColumn.CellContentTemplateSelector", DataGridControl.DataGridContextProperty );
+ m_defaultCellContentTemplateSelectorBinding.Source = this;
+ }
+
+ return m_defaultCellContentTemplateSelectorBinding;
+ }
+ }
+
+ private Binding m_defaultCellContentTemplateSelectorBinding;
+
+ #endregion
+
+ #region PRIVATE METHODS
+
+ private static void OnDataGridContextPropertyChanged( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ ScrollTip scrollTip = ( ScrollTip )sender;
+
+ DataGridContext oldItemContext = e.OldValue as DataGridContext;
+ DataGridContext newItemContext = e.NewValue as DataGridContext;
+
+ if( scrollTip.m_mainColumn != null )
+ {
+ PropertyChangedEventManager.RemoveListener( scrollTip.m_mainColumn, scrollTip, "DisplayMemberBinding" );
+ }
+
+ scrollTip.m_mainColumn = null;
+
+ if( oldItemContext != null )
+ {
+ PropertyChangedEventManager.RemoveListener( oldItemContext.Columns, scrollTip, "MainColumn" );
+ }
+
+ scrollTip.ClearValue( ScrollTip.ContentProperty );
+
+ if( newItemContext != null )
+ {
+ scrollTip.m_mainColumn = newItemContext.Columns.MainColumn;
+
+ if( scrollTip.m_mainColumn != null )
+ {
+ PropertyChangedEventManager.AddListener( scrollTip.m_mainColumn, scrollTip, "DisplayMemberBinding" );
+ }
+
+ PropertyChangedEventManager.AddListener( newItemContext.Columns, scrollTip, "MainColumn" );
+ }
+
+ if( !scrollTip.IsInParentGridChanged )
+ {
+ if( !scrollTip.ApplyTemplate() )
+ scrollTip.RefreshDefaultScrollTipContentTemplate();
+ }
+ }
+
+ private static void ParentGridControlPropertyChangedCallback( DependencyObject sender, DependencyPropertyChangedEventArgs e )
+ {
+ DataGridControl oldParentGridControl = e.OldValue as DataGridControl;
+ DataGridControl parentGridControl = e.NewValue as DataGridControl;
+ ScrollTip scrollTip = sender as ScrollTip;
+
+ scrollTip.IsInParentGridChanged = true;
+ try
+ {
+ if( oldParentGridControl != null )
+ {
+ if( scrollTip.UsingDefaultScrollTipContentTemplate )
+ {
+ // Unload DefaultTemplate
+ Xceed.Wpf.DataGrid.Views.UIViewBase uiViewBase = oldParentGridControl.GetView() as Xceed.Wpf.DataGrid.Views.UIViewBase;
+
+ if( uiViewBase != null )
+ {
+ uiViewBase.ClearValue( UIViewBase.ScrollTipContentTemplateProperty );
+ scrollTip.UsingDefaultScrollTipContentTemplate = false;
+ }
+ }
+
+ if( scrollTip.m_mainColumn != null )
+ {
+ PropertyChangedEventManager.RemoveListener( scrollTip.m_mainColumn, scrollTip, "DisplayMemberBinding" );
+ }
+
+ scrollTip.m_mainColumn = null;
+ scrollTip.m_horizontalScrollBar = null;
+ scrollTip.m_horizontalScrollThumb = null;
+ scrollTip.m_verticalScrollBar = null;
+ scrollTip.m_verticalScrollThumb = null;
+
+ scrollTip.UnregisterListeners( oldParentGridControl );
+ }
+
+ if( parentGridControl == null )
+ return;
+
+ scrollTip.PrepareDefaultStyleKey( parentGridControl.GetView() );
+
+ // Assert Template is applied in order to be notified for ScrollBars events
+ DataGridControl.SetDataGridContext( scrollTip, parentGridControl.DataGridContext );
+
+ if( !scrollTip.ApplyTemplate() )
+ scrollTip.RefreshDefaultScrollTipContentTemplate();
+
+ scrollTip.RegisterListeners( parentGridControl );
+ }
+ finally
+ {
+ scrollTip.IsInParentGridChanged = false;
+ }
+ }
+
+ private void RegisterListeners( DataGridControl parentGridControl )
+ {
+ if( parentGridControl.ScrollViewer != null )
+ parentGridControl.ScrollViewer.ScrollChanged += new ScrollChangedEventHandler( this.OnScrollViewerScrollChanged );
+
+ m_verticalScrollBar = parentGridControl.ScrollViewer.Template.FindName( "PART_VerticalScrollBar", parentGridControl.ScrollViewer ) as ScrollBar;
+ m_horizontalScrollBar = parentGridControl.ScrollViewer.Template.FindName( "PART_HorizontalScrollBar", parentGridControl.ScrollViewer ) as ScrollBar;
+
+ if( m_verticalScrollBar != null )
+ {
+ if( parentGridControl.ScrollViewer != null )
+ {
+ // Assert the Template as been applied on the ScrollBar to get access to the ScrollThumb
+ if( m_verticalScrollBar.Track == null )
+ m_verticalScrollBar.ApplyTemplate();
+
+ Debug.Assert( m_verticalScrollBar.Track != null );
+
+ if( m_verticalScrollBar.Track != null )
+ m_verticalScrollThumb = m_verticalScrollBar.Track.Thumb;
+
+ if( m_verticalScrollThumb != null )
+ {
+ // Register to IsMouseCaptureChanged to know when this ScrollThumb is clicked to display the ScrollTip if required
+ m_verticalScrollThumb.IsMouseCapturedChanged += new DependencyPropertyChangedEventHandler( this.ScrollThumb_IsMouseCapturedChanged );
+ }
+ }
+ }
+
+ if( m_horizontalScrollBar != null )
+ {
+ if( parentGridControl.ScrollViewer != null )
+ {
+ // Assert the Template as been applied on the ScrollBar to get access to the ScrollThumb
+ if( m_horizontalScrollBar.Track == null )
+ m_horizontalScrollBar.ApplyTemplate();
+
+ Debug.Assert( m_horizontalScrollBar.Track != null );
+
+ if( m_horizontalScrollBar.Track != null )
+ m_horizontalScrollThumb = m_horizontalScrollBar.Track.Thumb;
+
+ if( m_horizontalScrollThumb != null )
+ {
+ // Register to IsMouseCaptureChanged to know when this ScrollThumb is clicked to display the ScrollTip if required
+ m_horizontalScrollThumb.IsMouseCapturedChanged += new DependencyPropertyChangedEventHandler( this.ScrollThumb_IsMouseCapturedChanged );
+ }
+ }
+ }
+ }
+
+ private void UnregisterListeners( DataGridControl parentGridControl )
+ {
+ if( ( parentGridControl != null ) && ( parentGridControl.ScrollViewer != null ) )
+ parentGridControl.ScrollViewer.ScrollChanged -= new ScrollChangedEventHandler( this.OnScrollViewerScrollChanged );
+
+ if( m_verticalScrollThumb != null )
+ m_verticalScrollThumb.IsMouseCapturedChanged -= new DependencyPropertyChangedEventHandler( this.ScrollThumb_IsMouseCapturedChanged );
+
+ if( m_horizontalScrollThumb != null )
+ m_horizontalScrollThumb.IsMouseCapturedChanged -= new DependencyPropertyChangedEventHandler( this.ScrollThumb_IsMouseCapturedChanged );
+ }
+
+ private void RefreshDefaultScrollTipContentTemplate()
+ {
+ DataGridContext itemContext = DataGridControl.GetDataGridContext( this );
+
+ if( itemContext == null )
+ return;
+
+ DataGridControl parentGridControl = itemContext.DataGridControl;
+
+ if( ( parentGridControl == null ) || ( parentGridControl.ScrollViewer == null ) )
+ return;
+
+ ColumnCollection columnCollection = itemContext.Columns;
+
+ if( columnCollection == null )
+ return;
+
+ Column mainColumn = columnCollection.MainColumn as Column;
+
+ if( mainColumn == null )
+ return;
+
+ Xceed.Wpf.DataGrid.Views.UIViewBase uiViewBase = parentGridControl.GetView() as Xceed.Wpf.DataGrid.Views.UIViewBase;
+
+ if( uiViewBase == null )
+ return;
+
+ // The ScrollTip.ContentTemplate will now be set. This is to avoid
+ // a null ContentTemplate when the ColumnsCollection update its
+ // MainColumn after the template is applied
+ this.ScrollTipContentTemplateNeedsRefresh = false;
+
+ ForeignKeyConfiguration configuration = mainColumn.ForeignKeyConfiguration;
+
+ // Do not create default template only when none was created before and a template already exists
+ if( ( !this.UsingDefaultScrollTipContentTemplate ) && ( uiViewBase.ScrollTipContentTemplate != null ) )
+ {
+ if( configuration != null )
+ {
+ this.ContentTemplate = configuration.DefaultScrollTipContentTemplate;
+ }
+ else
+ {
+ // Clear the value in case we previously affected it
+ this.ClearValue( ScrollTip.ContentTemplateProperty );
+
+ // Set the default Binding values
+ this.SetBinding( ScrollTip.ContentTemplateProperty, this.DefaultScrollTipContentTemplateBinding );
+ this.SetBinding( ScrollTip.ContentTemplateSelectorProperty, this.DefaultScrollTipContentTemplateSelectorBinding );
+ }
+ }
+ else
+ {
+ // A default ContentTemplate template is created using MainColumn as displayed data
+ this.UsingDefaultScrollTipContentTemplate = true;
+
+ // If a configuration was found, the default ContentTemplate will
+ // be used to convert Content to Foreign value and
+ // it will use the ScrollTipContentTemplate defined on UIViewBase
+ // if any
+ if( configuration == null )
+ {
+ // Disable warning for DisplayMemberBinding when internaly used
+#pragma warning disable 618
+
+ BindingBase displayMemberBinding = mainColumn.DisplayMemberBinding;
+
+#pragma warning restore 618
+
+ FrameworkElementFactory contentPresenter = new FrameworkElementFactory( typeof( ContentPresenter ) );
+ contentPresenter.SetValue( ContentPresenter.NameProperty, "defaultScrollTipDataTemplateContentPresenter" );
+ contentPresenter.SetBinding( ContentPresenter.ContentProperty, displayMemberBinding );
+
+ contentPresenter.SetBinding( ContentPresenter.ContentTemplateProperty, this.DefaultCellContentTemplateBinding );
+ contentPresenter.SetBinding( ContentPresenter.ContentTemplateSelectorProperty, this.DefaultCellContentTemplateSelectorBinding );
+
+ DataTemplate template = new DataTemplate();
+ template.VisualTree = contentPresenter;
+ template.Seal();
+
+ this.ContentTemplate = template;
+ }
+ else
+ {
+ this.SetBinding( ContentPresenter.ContentTemplateProperty, this.DefaultCellContentTemplateBinding );
+ this.SetBinding( ContentPresenter.ContentTemplateSelectorProperty, this.DefaultCellContentTemplateSelectorBinding );
+ this.ContentTemplate = configuration.DefaultScrollTipContentTemplate;
+ }
+ }
+ }
+
+ private void ScrollThumb_IsMouseCapturedChanged( object sender, DependencyPropertyChangedEventArgs e )
+ {
+ Thumb scrollThumb = sender as Thumb;
+
+ if( scrollThumb == null )
+ return;
+
+ if( !scrollThumb.IsMouseCaptured )
+ return;
+
+ if( !this.ShouldDisplayScrollTip )
+ return;
+
+ ScrollBar scrollBar = scrollThumb.TemplatedParent as ScrollBar;
+
+ if( scrollBar == null )
+ return;
+
+ // Register to LostMouseCapture to be sure to hide the ScrollTip when the ScrollThumb lost the focus
+ if( scrollThumb == m_horizontalScrollThumb )
+ {
+ m_horizontalScrollThumb.LostMouseCapture += new MouseEventHandler( this.ScrollThumb_LostMouseCapture );
+ }
+ else if( scrollThumb == m_verticalScrollThumb )
+ {
+ m_verticalScrollThumb.LostMouseCapture += new MouseEventHandler( this.ScrollThumb_LostMouseCapture );
+ }
+ else
+ {
+ Debug.Fail( "Unknown thumb used for scrolling." );
+ return;
+ }
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext == null )
+ return;
+
+ if( dataGridContext.DataGridControl == null )
+ return;
+
+ // Update items scrolling orientation and pixel scrolling
+ m_itemsScrollingOrientation = ScrollViewerHelper.GetItemScrollingOrientation( dataGridContext.DataGridControl );
+
+ if( m_itemsScrollingOrientation == Orientation.Vertical )
+ {
+ if( scrollBar != m_verticalScrollBar )
+ return;
+ }
+ else
+ {
+ if( scrollBar != m_horizontalScrollBar )
+ return;
+ }
+
+ this.Visibility = Visibility.Visible;
+
+ this.IsPixelScrolling = ScrollViewerHelper.IsPixelScrolling( dataGridContext.DataGridControl, dataGridContext.DataGridControl.ItemsHost, dataGridContext.DataGridControl.ScrollViewer );
+
+ this.RefreshScrollTipContent( scrollBar );
+ }
+
+ private void ScrollThumb_LostMouseCapture( object sender, MouseEventArgs e )
+ {
+ this.Visibility = Visibility.Collapsed;
+
+ Thumb scrollBarThumb = sender as Thumb;
+
+ Debug.Assert( scrollBarThumb != null );
+
+ if( scrollBarThumb != null )
+ scrollBarThumb.LostMouseCapture -= new MouseEventHandler( this.ScrollThumb_LostMouseCapture );
+ }
+
+ private void OnScrollViewerScrollChanged( object sender, ScrollChangedEventArgs e )
+ {
+ if( this.Visibility != Visibility.Visible )
+ return;
+
+ if( !this.ShouldDisplayScrollTip )
+ return;
+
+ // Determine if we are scrolling horizontally or vertically
+ ScrollBar scrollBar = null;
+
+ if( e.VerticalChange == 0 )
+ {
+ scrollBar = m_horizontalScrollBar;
+ }
+ else
+ {
+ scrollBar = m_verticalScrollBar;
+ }
+
+ if( scrollBar == null )
+ return;
+
+ this.RefreshScrollTipContent( scrollBar );
+ }
+
+ private void RefreshScrollTipContent( ScrollBar scrollBar )
+ {
+ const int MaxItemToVisit = 100;
+
+ DataGridContext dataGridContext = DataGridControl.GetDataGridContext( this );
+
+ if( dataGridContext == null )
+ return;
+
+ DataGridControl parentGridControl = dataGridContext.DataGridControl;
+
+ if( parentGridControl == null )
+ return;
+
+ if( scrollBar.Orientation != m_itemsScrollingOrientation )
+ return;
+
+ double maxOffset = 0;
+
+ if( scrollBar.Orientation == Orientation.Vertical )
+ {
+ maxOffset = parentGridControl.ScrollViewer.ExtentHeight;
+ }
+ else
+ {
+ maxOffset = parentGridControl.ScrollViewer.ExtentWidth;
+ }
+
+ double offset = scrollBar.Track.Value;
+
+ int itemMaxOffset = 0;
+ int itemOffset = 0;
+
+ if( this.IsPixelScrolling )
+ {
+ // Calculate the offset ratio to get the item from the Generator
+ int itemsCount = parentGridControl.CustomItemContainerGenerator.ItemCount;
+ itemMaxOffset = itemsCount;
+
+ if( ( maxOffset > 0 ) && ( itemsCount > 0 ) )
+ {
+ itemOffset = ( int )( offset / ( maxOffset / itemsCount ) );
+ }
+ else
+ {
+ itemOffset = 0;
+ }
+ }
+ else
+ {
+ itemMaxOffset = ( int )maxOffset;
+ itemOffset = ( int )offset;
+ }
+
+ object newValue = null;
+ DataGridContext itemContext = null;
+
+ // If data is grouped, we do not want to keep the next data item if a HeaderFooterItem
+ // is the current item returned. So we increment the scroll up to the next item in order
+ // to get the real item that will be visible when the user will release the mouse
+ ItemContextVisitor visitor = new ItemContextVisitor( parentGridControl.ItemsHost is TableflowViewItemsHost );
+ int endOffset = Math.Min( itemOffset + MaxItemToVisit, itemMaxOffset );
+ bool visitWasStopped;
+ ( ( IDataGridContextVisitable )parentGridControl.DataGridContext ).AcceptVisitor( itemOffset, endOffset, visitor, DataGridContextVisitorType.Items, out visitWasStopped );
+
+ object tempValue = visitor.Item;
+
+ if( visitor.VisitSuccessful )
+ {
+ newValue = tempValue;
+ itemContext = visitor.ParentDataGridContext;
+ }
+ else
+ {
+ //Set the ItemContext as the ScrollTip's DataGridContext, this is to ensure the
+ //ScrollTip will not change the ScrollContentTemplate ( by invalidation of the binding).
+ itemContext = DataGridControl.GetDataGridContext( this );
+ newValue = null; //show nothing in the ScrollTip.
+ }
+
+ // The TippedItemDataGridContext PropertChanged callback will take care of refreshing the ScrollTip CellContentTemplate.
+ if( itemContext != DataGridControl.GetDataGridContext( this ) )
+ DataGridControl.SetDataGridContext( this, itemContext );
+
+ if( this.Content != newValue )
+ this.Content = newValue;
+ }
+
+ internal static bool IsItemInView( UIElement item, UIElement itemsHost )
+ {
+ GeneralTransform childTransform = item.TransformToAncestor( itemsHost );
+ Rect rectangle = childTransform.TransformBounds( new Rect( new Point( 0, 0 ), item.RenderSize ) );
+
+ //Check if the elements Rect intersects with that of the scrollviewer's
+ Rect result = Rect.Intersect( new Rect( new Point( 0, 0 ), itemsHost.RenderSize ), rectangle );
+
+ //if result is Empty then the element is not in view
+ return ( result != Rect.Empty );
+ }
+
+ internal static bool IsDataItemHiddenBySticky( DataGridContext dataGridContext, object tempValue )
+ {
+ // We only want to do special handling for a TableflowItemsHost.
+ TableflowViewItemsHost itemsHost = dataGridContext.DataGridControl.ItemsHost as TableflowViewItemsHost;
+
+ if( itemsHost == null )
+ return false;
+
+ int index = dataGridContext.CustomItemContainerGenerator.FindIndexForItem( tempValue, dataGridContext );
+
+ if( index > -1 )
+ {
+ return itemsHost.IsDataItemHiddenBySticky( index );
+ }
+
+ return false;
+ }
+
+ #endregion
+
+ #region IWeakEventListener Members
+
+ bool IWeakEventListener.ReceiveWeakEvent( Type managerType, object sender, EventArgs e )
+ {
+ return OnReceiveWeakEvent( managerType, sender, e );
+ }
+
+ protected virtual bool OnReceiveWeakEvent( Type managerType, object sender, EventArgs e )
+ {
+ bool handled = false;
+
+ DataGridContext itemContext = DataGridControl.GetDataGridContext( this );
+
+ if( itemContext == null )
+ return handled;
+
+ if( managerType == typeof( PropertyChangedEventManager ) )
+ {
+ if( itemContext.Columns == sender )
+ {
+ if( m_mainColumn != null )
+ {
+ PropertyChangedEventManager.RemoveListener( m_mainColumn, this, "DisplayMemberBinding" );
+ }
+
+ m_mainColumn = itemContext.Columns.MainColumn;
+
+ if( m_mainColumn != null )
+ {
+ PropertyChangedEventManager.AddListener( m_mainColumn, this, "DisplayMemberBinding" );
+ }
+
+ // If a defaut template was created for the previous MainColumn
+ // create another one for the new MainColumn
+ if( this.UsingDefaultScrollTipContentTemplate || this.ScrollTipContentTemplateNeedsRefresh )
+ {
+ this.RefreshDefaultScrollTipContentTemplate();
+ }
+
+ handled = true;
+ }
+ else if( sender == m_mainColumn )
+ {
+ // If a defaut template was created for the previous MainColumn
+ // create another one
+ if( this.UsingDefaultScrollTipContentTemplate || this.ScrollTipContentTemplateNeedsRefresh )
+ {
+ this.RefreshDefaultScrollTipContentTemplate();
+ }
+
+ handled = true;
+ }
+ }
+
+ return handled;
+ }
+
+ #endregion
+
+ #region PRIVATE FIELDS
+
+ private ColumnBase m_mainColumn; // = null;
+ private Orientation m_itemsScrollingOrientation = Orientation.Vertical;
+ private ScrollBar m_horizontalScrollBar; // = null;
+ private Thumb m_horizontalScrollThumb; // = null;
+ private ScrollBar m_verticalScrollBar; // = null;
+ private Thumb m_verticalScrollThumb; // = null;
+
+ private BitVector32 m_flags = new BitVector32();
+
+ #endregion
+
+ #region ScrollTipFlags Private Enum
+
+ [Flags]
+ private enum ScrollTipFlags
+ {
+ UsingDefaultScrollTipContentTemplate = 1,
+ IsInParentGridChanged = 2,
+ IsPixelScrolling = 4,
+ ScrollTipContentTemplateNeedsRefresh = 8,
+ }
+
+ #endregion
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ScrollViewerHelper.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ScrollViewerHelper.cs
new file mode 100644
index 00000000..aaa13b29
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/ScrollViewerHelper.cs
@@ -0,0 +1,714 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Media;
+using Xceed.Utils.Math;
+using Xceed.Utils.Wpf;
+using Xceed.Wpf.DataGrid.Views;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class ScrollViewerHelper
+ {
+ //----------------- PROPERTIES -------------------------
+
+ private static ScrollViewerHelper Current
+ {
+ get
+ {
+ if( m_singleInstance == null )
+ {
+ m_singleInstance = new ScrollViewerHelper();
+ }
+
+ return m_singleInstance;
+ }
+ }
+
+ public static readonly int PixelScrollingCount = 16;
+
+ //----------------- PUBLIC METHODS -------------------------
+
+ public static bool IsFocusInElement( FrameworkElement element )
+ {
+ if( element != null )
+ return element.IsKeyboardFocusWithin;
+ else
+ return false;
+ }
+
+ public static FrameworkElement GetLastVisibleContainer(
+ DataGridControl gridControl,
+ FrameworkElement container,
+ ScrollViewer scrollViewer )
+ {
+ FrameworkElement retval = null;
+
+ if( ScrollViewerHelper.IsPixelScrolling( gridControl, container, scrollViewer ) == false )
+ {
+ //This means that the panel is performing Item Scrolling
+
+ //if the panel is Vertically scrolling the items (means the Horizontal Axis is Pixel scrolling)
+ if( ScrollViewerHelper.GetItemScrollingOrientation( gridControl, container, scrollViewer ) == Orientation.Vertical )
+ {
+ retval = ScrollViewerHelper.ProcessLastVisibleContainer( gridControl,
+ scrollViewer.VerticalOffset,
+ scrollViewer.ViewportHeight,
+ scrollViewer.HorizontalOffset,
+ scrollViewer.ViewportWidth,
+ Orientation.Vertical );
+ }
+ //the panel is Horizontally scrolling the items (means the Vertically Axis is Pixel scrolling)
+ else
+ {
+ retval = ScrollViewerHelper.ProcessLastVisibleContainer( gridControl,
+ scrollViewer.HorizontalOffset,
+ scrollViewer.ViewportWidth,
+ scrollViewer.VerticalOffset,
+ scrollViewer.ViewportHeight,
+ Orientation.Horizontal );
+ }
+ }
+ else
+ {
+ Point pt = new Point();
+
+ if( ( container is VirtualizingPanel )
+ || ( container is DataGridItemsHost )
+ || ( scrollViewer == null ) )
+ {
+ pt.X = 0;
+ pt.Y = 0;
+ }
+ else
+ {
+ pt.X = scrollViewer.HorizontalOffset;
+ pt.Y = scrollViewer.VerticalOffset;
+ }
+
+ Size size = new Size( ( scrollViewer != null ) ? scrollViewer.ViewportWidth : container.ActualWidth,
+ ( scrollViewer != null ) ? scrollViewer.ViewportHeight : container.ActualHeight );
+
+ Rect visibleRect = new Rect( pt, size );
+
+ RectangleGeometry geo = new RectangleGeometry( visibleRect );
+
+ lock( ScrollViewerHelper.Current )
+ {
+ m_sVisibleChildList.Clear();
+ m_sGridControl = gridControl;
+
+ VisualTreeHelper.HitTest( container,
+ new HitTestFilterCallback( ScrollViewerHelper.MyFilterFunct ),
+ new HitTestResultCallback( ScrollViewerHelper.UselessResultCallback ),
+ new GeometryHitTestParameters( geo ) );
+
+ m_sGridControl = null;
+
+ FrameworkElement preservedChild = null;
+ Nullable preservedOffset = null;
+
+ foreach( FrameworkElement child in m_sVisibleChildList )
+ {
+ Vector itemOffset = VisualTreeHelper.GetOffset( child );
+
+ Rect itemRect = new Rect( itemOffset.X, itemOffset.Y, child.ActualWidth, child.ActualHeight );
+
+ itemRect.Intersect( visibleRect );
+
+ switch( gridControl.ItemsPrimaryAxis )
+ {
+ case PrimaryAxis.Vertical:
+ if( DoubleUtil.AreClose( itemRect.Width, 0 ) == false )
+ {
+ if( DoubleUtil.AreClose( itemRect.Height, child.ActualHeight ) == true )
+ {
+ if( ScrollViewerHelper.IsABetterLastRow( preservedChild, preservedOffset, itemOffset ) == true )
+ {
+ preservedChild = child;
+ preservedOffset = itemOffset;
+ }
+ }
+ }
+ break;
+ case PrimaryAxis.Horizontal:
+ if( DoubleUtil.AreClose( itemRect.Width, child.ActualWidth ) == true )
+ {
+ if( DoubleUtil.AreClose( itemRect.Height, 0 ) == false )
+ {
+ if( ScrollViewerHelper.IsABetterLastRow( preservedChild, preservedOffset, itemOffset ) == true )
+ {
+ preservedChild = child;
+ preservedOffset = itemOffset;
+ }
+ }
+ }
+ break;
+ case PrimaryAxis.Both:
+ if( ( DoubleUtil.AreClose( itemRect.Width, child.ActualWidth ) == true )
+ && ( DoubleUtil.AreClose( itemRect.Height, child.ActualHeight ) == true ) )
+ {
+ if( ScrollViewerHelper.IsABetterLastRow( preservedChild, preservedOffset, itemOffset ) == true )
+ {
+ preservedChild = child;
+ preservedOffset = itemOffset;
+ }
+ }
+ break;
+ case PrimaryAxis.None:
+ if( itemRect.IsEmpty == false )
+ {
+ if( ( DoubleUtil.AreClose( itemRect.Height, 0 ) == false ) && ( DoubleUtil.AreClose( itemRect.Width, 0 ) == false ) )
+ {
+ if( ScrollViewerHelper.IsABetterLastRow( preservedChild, preservedOffset, itemOffset ) == true )
+ {
+ preservedChild = child;
+ preservedOffset = itemOffset;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ retval = preservedChild;
+
+ }//end lock (protection of shared static members with GetFirstVisibleItem() )
+ }
+
+
+ return retval;
+ }
+
+ private static FrameworkElement ProcessLastVisibleContainer(
+ DataGridControl gridControl,
+ double offset,
+ double viewportSize,
+ double opposedOffset,
+ double opposedViewportSize,
+ Orientation panelOrientation )
+ {
+ FrameworkElement retval = null;
+
+ // Only needed if the DataGridControl contains items
+ if( ( gridControl != null ) && ( gridControl.Items.Count > 0 ) )
+ {
+ bool qualifyingContainerFound = false;
+
+ //retrieve the last object index according to item scrolling axis (this covers both the "Vertical and None" primary axis cases...
+ //as well as default if no other row matches the PrimaryAxis criteria)
+ int runningIndex = ( int )( offset + viewportSize - 1 );
+
+ //cycle for as long as a qualifying container is not found.
+ while( qualifyingContainerFound == false )
+ {
+ retval = gridControl.GetContainerFromIndex( runningIndex ) as FrameworkElement;
+
+ if( retval != null )
+ {
+ qualifyingContainerFound = ScrollViewerHelper.IsContainerQualifying( retval,
+ gridControl,
+ offset,
+ viewportSize,
+ opposedOffset,
+ opposedViewportSize,
+ panelOrientation );
+ }
+
+ //under all circumstances, if I am back at the original offset (first item visible), then have it qualify.
+ if( runningIndex == offset )
+ qualifyingContainerFound = true;
+
+ runningIndex--;
+ }
+ }
+
+ return retval;
+ }
+
+ private static FrameworkElement ProcessFirstVisibleContainer(
+ DataGridControl gridControl,
+ double offset,
+ double viewportSize,
+ double opposedOffset,
+ double opposedViewportSize,
+ Orientation panelOrientation )
+ {
+ FrameworkElement retval = null;
+
+ // Only needed if the DataGridControl contains items
+ if( ( gridControl != null ) && ( gridControl.Items.Count > 0 ) )
+ {
+ bool qualifyingContainerFound = false;
+
+ //retrieve the last object index according to item scrolling axis (this covers both the "Vertical and None" primary axis cases...
+ //as well as default if no other row matches the PrimaryAxis criteria)
+ int runningIndex = ( int )( offset );
+
+ //cycle for as long as a qualifying container is not found.
+ while( !qualifyingContainerFound )
+ {
+ retval = gridControl.GetContainerFromIndex( runningIndex ) as FrameworkElement;
+
+ //will be reverted back if the container does not match a particular condition.
+ if( retval != null )
+ {
+ qualifyingContainerFound = ScrollViewerHelper.IsContainerQualifying( retval,
+ gridControl,
+ offset,
+ viewportSize,
+ opposedOffset,
+ opposedViewportSize,
+ panelOrientation );
+ }
+
+ //under all circumstances, if I am back at the end of the viewport ( last item visible), then have it qualify.
+ if( runningIndex == ( offset + viewportSize - 1 ) )
+ qualifyingContainerFound = true;
+
+ runningIndex++;
+ }
+ }
+
+ return retval;
+ }
+
+ private static bool IsContainerQualifying(
+ FrameworkElement container,
+ DataGridControl gridControl,
+ double offset,
+ double viewportSize,
+ double opposedOffset,
+ double opposedViewportSize,
+ Orientation panelOrientation )
+ {
+ bool retval = true;
+
+ HeaderFooterItem headerFooterItemContainer = container as HeaderFooterItem;
+ Row rowContainer = container as Row;
+
+ //Determine if the Element is Focusable or Navigable
+ if( headerFooterItemContainer != null )
+ {
+ rowContainer = headerFooterItemContainer.AsVisual() as Row;
+ if( rowContainer == null )
+ {
+ //If the HeaderFooter Item is not a Row and is Not focusable, then the item does not qualify.
+ UIElement uiElementContainer = headerFooterItemContainer.AsVisual() as UIElement;
+ if( ( uiElementContainer == null ) || ( !uiElementContainer.Focusable ) )
+ {
+ retval = false;
+ }
+ }
+ }
+
+ //If the container is a Row (in the headers footers region or not )
+ if( ( retval ) && ( rowContainer != null ) )
+ {
+ //and the Row is not navigable, then it does not qualify.
+ if( rowContainer.NavigationBehavior == NavigationBehavior.None )
+ {
+ retval = false;
+ }
+ }
+
+
+ //If the container still qualifies after first verification, check for the scrolling axis.
+ if( retval )
+ {
+ //if the PrimaryAxis requires the opposed axis to be fully visible.
+ if( ( ( panelOrientation == Orientation.Vertical ) && ( ( gridControl.ItemsPrimaryAxis == PrimaryAxis.Horizontal ) || ( gridControl.ItemsPrimaryAxis == PrimaryAxis.Both ) ) )
+ || ( ( panelOrientation == Orientation.Horizontal ) && ( ( gridControl.ItemsPrimaryAxis == PrimaryAxis.Vertical ) || ( gridControl.ItemsPrimaryAxis == PrimaryAxis.Both ) ) ) )
+ {
+ FrameworkElement frameworkElementContainer = container as FrameworkElement;
+ //Somehow, I decided that a container that is not a FrameworkElement (extremelly highly unprobable) was automaticaly NOT to qualify if opposed axis was required.
+ if( frameworkElementContainer != null )
+ {
+ Vector rowOffset = VisualTreeHelper.GetOffset( frameworkElementContainer );
+
+ double computedOffset = ( panelOrientation == Orientation.Vertical ) ? rowOffset.X : rowOffset.Y;
+ double computedSize = ( panelOrientation == Orientation.Vertical ) ? frameworkElementContainer.ActualWidth : frameworkElementContainer.ActualHeight;
+
+ //if the coordinates of the Row are NOT inside the Scrolling Axis' viewport... then the item is not qualifyable.
+ if( ( computedOffset < opposedOffset ) || ( ( computedOffset + computedSize ) > ( opposedViewportSize + opposedOffset ) ) )
+ {
+ retval = false;
+ }
+ }
+ else
+ {
+ retval = false;
+ }
+ }
+ }
+
+ return retval;
+ }
+
+ public static FrameworkElement GetFirstVisibleContainer(
+ DataGridControl gridControl,
+ FrameworkElement container,
+ ScrollViewer scrollViewer )
+ {
+ FrameworkElement retval = null;
+
+ if( !ScrollViewerHelper.IsPixelScrolling( gridControl, container, scrollViewer ) )
+ {
+ //This means that the panel is performing Item Scrolling
+
+ //if the panel is Vertically scrolling the items (means the Horizontal Axis is Pixel scrolling)
+ if( ScrollViewerHelper.GetItemScrollingOrientation( gridControl, container, scrollViewer ) == Orientation.Vertical )
+ {
+ retval = ScrollViewerHelper.ProcessFirstVisibleContainer( gridControl,
+ scrollViewer.VerticalOffset,
+ scrollViewer.ViewportHeight,
+ scrollViewer.HorizontalOffset,
+ scrollViewer.ViewportWidth,
+ Orientation.Vertical );
+ }
+ //the panel is Horizontally scrolling the items (means the Vertically Axis is Pixel scrolling)
+ else
+ {
+ retval = ScrollViewerHelper.ProcessFirstVisibleContainer( gridControl,
+ scrollViewer.HorizontalOffset,
+ scrollViewer.ViewportWidth,
+ scrollViewer.VerticalOffset,
+ scrollViewer.ViewportHeight,
+ Orientation.Horizontal );
+ }
+ }
+ else
+ {
+ Point pt = new Point();
+
+ if( ( container is VirtualizingPanel )
+ || ( container is DataGridItemsHost )
+ || ( scrollViewer == null ) )
+ {
+ pt.X = 0;
+ pt.Y = 0;
+ }
+ else
+ {
+ pt.X = scrollViewer.HorizontalOffset;
+ pt.Y = scrollViewer.VerticalOffset;
+ }
+
+ Size size = new Size( ( scrollViewer != null ) ? scrollViewer.ViewportWidth : container.ActualWidth,
+ ( scrollViewer != null ) ? scrollViewer.ViewportHeight : container.ActualHeight );
+
+ Rect visibleRect = new Rect( pt, size );
+
+ RectangleGeometry geo = new RectangleGeometry( visibleRect );
+
+ lock( ScrollViewerHelper.Current )
+ {
+ m_sVisibleChildList.Clear();
+ m_sGridControl = gridControl;
+
+ VisualTreeHelper.HitTest( container,
+ new HitTestFilterCallback( ScrollViewerHelper.MyFilterFunct ),
+ new HitTestResultCallback( ScrollViewerHelper.UselessResultCallback ),
+ new GeometryHitTestParameters( geo ) );
+
+ m_sGridControl = null;
+
+ FrameworkElement preservedChild = null;
+ Nullable preservedOffset = null;
+
+ foreach( FrameworkElement child in m_sVisibleChildList )
+ {
+ Vector itemOffset = VisualTreeHelper.GetOffset( child );
+
+ Rect itemRect = new Rect( itemOffset.X, itemOffset.Y, child.ActualWidth, child.ActualHeight );
+
+ itemRect.Intersect( visibleRect );
+
+ switch( gridControl.ItemsPrimaryAxis )
+ {
+ case PrimaryAxis.Vertical:
+ if( DoubleUtil.AreClose( itemRect.Width, 0 ) == false )
+ {
+ if( DoubleUtil.AreClose( child.ActualHeight, itemRect.Height ) == true )
+ {
+ if( ScrollViewerHelper.IsABetterFirstRow( preservedChild, preservedOffset, itemOffset ) == true )
+ {
+ preservedChild = child;
+ preservedOffset = itemOffset;
+ }
+ }
+ }
+ break;
+ case PrimaryAxis.Horizontal:
+ if( DoubleUtil.AreClose( itemRect.Height, 0 ) == false )
+ {
+ if( DoubleUtil.AreClose( itemRect.Width, child.ActualWidth ) == true )
+ {
+ if( ScrollViewerHelper.IsABetterFirstRow( preservedChild, preservedOffset, itemOffset ) == true )
+ {
+ preservedChild = child;
+ preservedOffset = itemOffset;
+ }
+ }
+ }
+ break;
+ case PrimaryAxis.Both:
+ if( ( DoubleUtil.AreClose( itemRect.Width, child.ActualWidth ) == true ) && ( DoubleUtil.AreClose( itemRect.Height, child.ActualHeight ) == true ) )
+ {
+ if( ScrollViewerHelper.IsABetterFirstRow( preservedChild, preservedOffset, itemOffset ) == true )
+ {
+ preservedChild = child;
+ preservedOffset = itemOffset;
+ }
+ }
+ break;
+ case PrimaryAxis.None:
+ if( ( DoubleUtil.AreClose( itemRect.Width, 0 ) == false ) && ( DoubleUtil.AreClose( itemRect.Width, 0 ) == false ) )
+ {
+ if( itemRect.IsEmpty == false )
+ {
+ if( ScrollViewerHelper.IsABetterFirstRow( preservedChild, preservedOffset, itemOffset ) == true )
+ {
+ preservedChild = child;
+ preservedOffset = itemOffset;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ retval = preservedChild;
+ }//end lock
+ }
+
+ return retval;
+ }
+
+ //----------------- INTERNAL METHODS -------------------------
+
+ internal static Thumb FindParentThumb( object reference, ScrollViewer scrollViewer )
+ {
+ Thumb retval = null;
+ DependencyObject obj = reference as DependencyObject;
+
+ if( obj != null )
+ {
+ do
+ {
+ retval = obj as Thumb;
+
+ if( retval == null )
+ {
+ obj = TreeHelper.GetParent( obj );
+
+ if( ( obj == scrollViewer ) || ( obj == null ) )
+ break;
+ }
+ } while( retval == null );
+ }
+
+ return retval;
+ }
+
+ internal static Orientation GetItemScrollingOrientation(
+ DataGridControl dataGridControl,
+ FrameworkElement container,
+ ScrollViewer scrollViewer )
+ {
+ int itemsCount = dataGridControl.Items.Count;
+
+ //if the Panel passed supports the CustomItemContainerGenerator, then retrieve the itemcount from the customItemContainerGenerator
+ if( ( container is DataGridVirtualizingPanel ) || ( container is DataGridItemsHost ) )
+ {
+ itemsCount = dataGridControl.CustomItemContainerGenerator.ItemCount;
+ }
+
+ if( scrollViewer.ExtentHeight == itemsCount )
+ return Orientation.Vertical;
+
+ return Orientation.Horizontal;
+ }
+
+ [System.Diagnostics.CodeAnalysis.SuppressMessage( "Microsoft.Performance", "CA1800:DoNotCastUnnecessarily" )]
+ internal static Orientation GetItemScrollingOrientation( DataGridControl dataGridControl )
+ {
+ int itemsCount = dataGridControl.Items.Count;
+
+ ScrollViewer scrollViewer = dataGridControl.ScrollViewer;
+ FrameworkElement container = dataGridControl.ItemsHost;
+
+ //if the Panel passed supports the CustomItemContainerGenerator, then retrieve the itemcount from the customItemContainerGenerator
+ if( ( container is DataGridVirtualizingPanel ) || ( container is DataGridItemsHost ) )
+ {
+ itemsCount = dataGridControl.CustomItemContainerGenerator.ItemCount;
+ }
+
+ Orientation foundOrientation = Orientation.Horizontal;
+ if( scrollViewer.ExtentHeight == itemsCount )
+ {
+ foundOrientation = Orientation.Vertical;
+ }
+ else if( container is StackPanel )
+ {
+ foundOrientation = ( ( StackPanel )container ).Orientation;
+ }
+ else if( container is WrapPanel )
+ {
+ foundOrientation = ( ( WrapPanel )container ).Orientation;
+ }
+ else if( container is IAnimatedScrollInfo )
+ {
+ foundOrientation = Orientation.Vertical;
+ }
+ else
+ {
+ foundOrientation = Orientation.Horizontal;
+ }
+ return foundOrientation;
+ }
+
+ internal static bool IsPixelScrolling( DataGridControl dataGridControl, FrameworkElement container, ScrollViewer scrollViewer )
+ {
+ int itemsCount = dataGridControl.Items.Count;
+
+ //if the Panel passed supports the CustomItemContainerGenerator, then retrieve the itemcount from the customItemContainerGenerator
+ if( ( container is DataGridVirtualizingPanel ) || ( container is DataGridItemsHost ) )
+ {
+ itemsCount = dataGridControl.CustomItemContainerGenerator.ItemCount;
+ }
+
+ //This means that the ScrollViewer performs Items Scrolling
+ if( ( scrollViewer != null )
+ && ( ( scrollViewer.ExtentHeight == itemsCount ) || ( scrollViewer.ExtentWidth == itemsCount ) )
+ && ( container is IScrollInfo ) && ( scrollViewer.CanContentScroll == true ) )
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ internal static void ResetScrollPositions( ScrollViewer scrollViewer )
+ {
+ if( scrollViewer == null )
+ {
+ throw new ArgumentNullException( "scrollViewer" );
+ }
+
+ DataGridScrollViewer dataGridScrollViewer = scrollViewer as DataGridScrollViewer;
+ if( dataGridScrollViewer != null )
+ {
+ foreach( SynchronizedScrollViewer ssv in dataGridScrollViewer.SynchronizedScrollViewers )
+ {
+ ssv.ScrollToTop();
+ ssv.ScrollToLeftEnd();
+ }
+ }
+
+ scrollViewer.ScrollToTop();
+ scrollViewer.ScrollToLeftEnd();
+
+ }
+
+ //----------------- PRIVATE METHODS -------------------------
+
+ private static HitTestFilterBehavior MyFilterFunct( DependencyObject obj )
+ {
+ Panel panel = m_sGridControl.ItemsHost as Panel;
+
+ //if the object passed is a direct child of the ItemsHostPanel; keep it
+ if( ( panel != null )
+ && ( panel.Children.Contains( ( UIElement )obj ) == true ) )
+ {
+ m_sVisibleChildList.Add( obj );
+
+ return HitTestFilterBehavior.ContinueSkipChildren;
+ }
+ else //otherwise, discard it.
+ {
+ return HitTestFilterBehavior.ContinueSkipSelf;
+ }
+ }
+
+ private static HitTestResultBehavior UselessResultCallback( HitTestResult htr )
+ {
+ return HitTestResultBehavior.Continue;
+ }
+
+ private static bool IsABetterFirstRow( DependencyObject referenceChild, Nullable referenceOffset, Vector newOffset )
+ {
+ bool retval = false;
+
+ if( referenceChild != null )
+ {
+ if( ( DoubleUtil.AreClose( referenceOffset.Value.Y, newOffset.Y ) == true ) && ( newOffset.X < referenceOffset.Value.X ) )
+ {
+ retval = true;
+ }
+ else if( referenceOffset.Value.Y > newOffset.Y )
+ {
+ retval = true;
+ }
+ }
+ else
+ {
+ retval = true;
+ }
+
+ return retval;
+ }
+
+ private static bool IsABetterLastRow( DependencyObject referenceChild, Nullable referenceOffset, Vector newOffset )
+ {
+ bool retval = false;
+
+ if( referenceChild != null )
+ {
+ if( ( DoubleUtil.AreClose( referenceOffset.Value.Y, newOffset.Y ) == true ) && ( newOffset.X > referenceOffset.Value.X ) )
+ {
+ retval = true;
+ }
+ else if( referenceOffset.Value.Y < newOffset.Y )
+ {
+ retval = true;
+ }
+ }
+ else
+ {
+ retval = true;
+ }
+
+ return retval;
+ }
+
+ //----------------- FIELDS -------------------------
+
+ private static ScrollViewerHelper m_singleInstance; // = null;
+ private static List m_sVisibleChildList = new List();
+ private static DataGridControl m_sGridControl; // = null;
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectedCellsStorage.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectedCellsStorage.cs
new file mode 100644
index 00000000..67489848
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectedCellsStorage.cs
@@ -0,0 +1,528 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Collections;
+using System.Diagnostics;
+using System.Windows.Data;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class SelectedCellsStorage : IEnumerable
+ {
+ #region CONSTRUCTORS
+
+ internal SelectedCellsStorage( DataGridContext dataGridContext, int capacity )
+ {
+ // dataGridContext can be null for store that we don't want to auto-get Items from index
+ m_dataGridContext = dataGridContext;
+ m_list = new List( capacity );
+ }
+
+ internal SelectedCellsStorage( SelectedCellsStorage collection )
+ {
+ m_dataGridContext = collection.m_dataGridContext;
+ m_list = new List( collection.m_list );
+ m_cellsCount = collection.m_cellsCount;
+ }
+
+ #endregion CONSTRUCTORS
+
+ #region PUBLIC PROPERTIES
+
+ public int Count
+ {
+ get
+ {
+ return m_list.Count;
+ }
+ }
+
+ public int CellsCount
+ {
+ get
+ {
+ return m_cellsCount;
+ }
+ }
+
+ public DataGridContext DataGridContext
+ {
+ get
+ {
+ return m_dataGridContext;
+ }
+ }
+
+ public SelectionCellRangeWithItems this[ int index ]
+ {
+ get
+ {
+ return m_list[ index ];
+ }
+ set
+ {
+ SelectionCellRangeWithItems oldRange = m_list[ index ];
+ m_list[ index ] = value;
+ m_cellsCount += value.Length - oldRange.Length;
+ }
+ }
+
+ #endregion PUBLIC PROPERTIES
+
+ #region IEnumerable
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return m_list.GetEnumerator();
+ }
+
+ #endregion IEnumerable
+
+ #region PUBLIC METHODS
+
+ public int Add( SelectionCellRangeWithItems cellRangeWithItems )
+ {
+
+
+ m_cellsCount += cellRangeWithItems.Length;
+ SelectionRangeWithItems itemRangeWithItems = cellRangeWithItems.ItemRangeWithItems;
+
+ SelectedItemsStorage.UpdateSelectionRangeWithItemsFromAdd(
+ m_dataGridContext, itemRangeWithItems, ref itemRangeWithItems );
+
+ m_list.Add( new SelectionCellRangeWithItems(
+ itemRangeWithItems.Range, itemRangeWithItems.Items, cellRangeWithItems.ColumnRange ) );
+
+ return m_list.Count - 1;
+ }
+
+ public void Insert( int index, SelectionCellRangeWithItems cellRangeWithItems )
+ {
+ Debug.Assert( !this.Contains( cellRangeWithItems.CellRange ) );
+ m_cellsCount += cellRangeWithItems.Length;
+
+ SelectionRangeWithItems itemRangeWithItems = cellRangeWithItems.ItemRangeWithItems;
+
+ SelectedItemsStorage.UpdateSelectionRangeWithItemsFromAdd(
+ m_dataGridContext, itemRangeWithItems, ref itemRangeWithItems );
+
+ m_list.Insert(
+ index, new SelectionCellRangeWithItems(
+ itemRangeWithItems.Range, itemRangeWithItems.Items, cellRangeWithItems.ColumnRange ) );
+ }
+
+ public void RemoveAt( int index )
+ {
+ m_cellsCount -= m_list[ index ].Length;
+ m_list.RemoveAt( index );
+ }
+
+ public void Clear()
+ {
+ m_cellsCount = 0;
+ m_list.Clear();
+ }
+
+ public SelectionCellRangeWithItems[] ToArray()
+ {
+ return m_list.ToArray();
+ }
+
+ public bool Contains( int itemIndex, int columnIndex )
+ {
+ if( ( itemIndex < 0 ) || ( columnIndex < 0 ) )
+ return false;
+
+ SelectionCellRange cellRange = new SelectionCellRange( itemIndex, columnIndex );
+ int count = m_list.Count;
+
+ for( int i = 0; i < count; i++ )
+ {
+ if( !m_list[ i ].CellRange.Intersect( cellRange ).IsEmpty )
+ return true;
+ }
+
+ return false;
+ }
+
+ public bool Contains( SelectionCellRange cellRange )
+ {
+ int count = m_list.Count;
+
+ int cellRangeLength = cellRange.Length;
+
+ // Ensure every Cells are present in the SelectedCellsStorage
+ if( cellRangeLength > 1 )
+ {
+ SelectedCellsStorage cellStorage = new SelectedCellsStorage( null, cellRangeLength );
+ cellStorage.Add( new SelectionCellRangeWithItems( cellRange.ItemRange,
+ null,
+ cellRange.ColumnRange ) );
+
+ for( int i = 0; i < count; i++ )
+ {
+ cellStorage.Remove( m_list[ i ] );
+
+ if( cellStorage.Count == 0 )
+ return true;
+ }
+ }
+ else
+ {
+ for( int i = 0; i < count; i++ )
+ {
+ if( !m_list[ i ].CellRange.Intersect( cellRange ).IsEmpty )
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public bool Contains( SelectionCellRangeWithItems cellRangeWithItemsToCompare )
+ {
+ int count = m_list.Count;
+ SelectionCellRange cellRangeToCompare = cellRangeWithItemsToCompare.CellRange;
+
+ int cellRangeLength = cellRangeToCompare.Length;
+ // If there is more than one Cell in the range, ensure
+ // the range is completely contained within this SelectedCellsStorage
+ if( cellRangeLength > 1 )
+ {
+ SelectedCellsStorage cellStorage = new SelectedCellsStorage( null, cellRangeLength );
+ cellStorage.Add( cellRangeWithItemsToCompare );
+
+ // The range is completely contained when the store
+ // is empty after removing all ranges contained within
+ // this SelectedCellsStorage
+ for( int i = 0; i < count; i++ )
+ {
+ SelectionCellRangeWithItems cellRangeWithItems = m_list[ i ];
+
+ cellStorage.Remove( cellRangeWithItems );
+
+ if( cellStorage.Count == 0 )
+ return true;
+ }
+ }
+ else
+ {
+ for( int i = 0; i < count; i++ )
+ {
+ SelectionCellRangeWithItems rangeWithItem = m_list[ i ];
+ SelectionCellRange rangeIntersection = rangeWithItem.CellRange.Intersect( cellRangeToCompare );
+
+ if( !rangeIntersection.IsEmpty )
+ {
+ if( cellRangeWithItemsToCompare.ItemRangeWithItems.IsItemsEqual(
+ rangeIntersection.ItemRange, rangeWithItem.ItemRangeWithItems ) )
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public bool Remove( SelectionCellRangeWithItems cellRangeWithItemsToRemove )
+ {
+ if( cellRangeWithItemsToRemove.CellRange.IsEmpty )
+ return true;
+
+ bool removed = false;
+ SelectionCellRange cellRangeToRemove = cellRangeWithItemsToRemove.CellRange;
+ object[] itemsToRemove = cellRangeWithItemsToRemove.ItemRangeWithItems.Items;
+ int itemsToRemoveCount = ( itemsToRemove == null ) ? 0 : itemsToRemove.Length;
+ int itemOffsetToRemove = 0;
+ bool itemRangeToRemoveIsEmpty = cellRangeToRemove.ItemRange.IsEmpty;
+
+ do
+ {
+ for( int i = m_list.Count - 1; i >= 0; i-- )
+ {
+ SelectionCellRangeWithItems cellRangeWithItems = m_list[ i ];
+ SelectionRange itemRange = cellRangeWithItems.ItemRange;
+ SelectionCellRange cellRange = cellRangeWithItems.CellRange;
+ SelectionRangeWithItems itemRangeWithItems = cellRangeWithItems.ItemRangeWithItems;
+ SelectionCellRange cellRangeIntersection;
+ object[] oldRangeItems = itemRangeWithItems.Items;
+
+ if( itemRangeToRemoveIsEmpty )
+ {
+ int itemOffset = Array.IndexOf( oldRangeItems, itemsToRemove[ itemOffsetToRemove ] );
+
+ if( itemOffset == -1 )
+ continue;
+
+ int itemIndex = itemRange.GetIndexFromItemOffset( itemOffset );
+
+ cellRangeIntersection = new SelectionCellRange(
+ new SelectionRange( itemIndex, itemIndex ),
+ cellRangeWithItems.ColumnRange.Intersect( cellRangeToRemove.ColumnRange ) );
+ }
+ else
+ {
+ cellRangeIntersection = cellRange.Intersect( cellRangeToRemove );
+
+ if( cellRangeIntersection.IsEmpty )
+ continue;
+
+ if( !itemRangeWithItems.IsItemsEqual(
+ cellRangeIntersection.ItemRange, cellRangeWithItemsToRemove.ItemRangeWithItems ) )
+ {
+ continue;
+ }
+ }
+
+ removed = true;
+ m_cellsCount -= cellRangeIntersection.Length;
+ SelectionCellRange[] newCellRanges = cellRange.Exclude( cellRangeIntersection );
+
+ if( newCellRanges.Length == 0 )
+ {
+ m_list.RemoveAt( i );
+ }
+ else
+ {
+ SelectionCellRange newCellRange = newCellRanges[ 0 ];
+
+ m_list[ i ] = new SelectionCellRangeWithItems(
+ newCellRange.ItemRange, itemRangeWithItems.GetItems( newCellRange.ItemRange ), newCellRange.ColumnRange );
+
+ for( int j = 1; j < newCellRanges.Length; j++ )
+ {
+ newCellRange = newCellRanges[ j ];
+
+ m_list.Insert( i + j, new SelectionCellRangeWithItems(
+ newCellRange.ItemRange, itemRangeWithItems.GetItems( newCellRange.ItemRange ), newCellRange.ColumnRange ) );
+ }
+ }
+ }
+
+ itemOffsetToRemove++;
+ } while( ( itemRangeToRemoveIsEmpty ) && ( itemOffsetToRemove < itemsToRemoveCount ) );
+
+ return removed;
+ }
+
+ public void OffsetIndex( int itemStartIndex, int offset )
+ {
+ // Used to offset index after an add or remove from the data source of the grid.
+
+ SelectionRange offsetRange = new SelectionRange(
+ itemStartIndex, itemStartIndex + Math.Abs( offset ) - 1 );
+
+ for( int i = this.Count - 1; i >= 0; i-- )
+ {
+ SelectionCellRangeWithItems cellRangeWithItems = m_list[ i ];
+ SelectionRange itemRange = cellRangeWithItems.ItemRange;
+ SelectionRange columnRange = cellRangeWithItems.ColumnRange;
+
+ if( offsetRange > itemRange )
+ continue;
+
+ SelectionRange itemRangeIntersection = itemRange.Intersect( offsetRange );
+ object[] originalItems = cellRangeWithItems.ItemRangeWithItems.Items;
+
+ if( !itemRangeIntersection.IsEmpty )
+ {
+ // Should only happen when adding since when we remove data from the source, we remove the
+ // the range from the list.
+ Debug.Assert( offset > 0 );
+
+ // Offset the index higher than the start index of the new added item
+ SelectionRange topRange;
+ SelectionRange bottomRange;
+
+ if( itemRange.StartIndex > itemRange.EndIndex )
+ {
+ if( itemRangeIntersection.EndIndex == itemRange.EndIndex )
+ {
+ SelectionRange newItemRange = new SelectionRange( itemRange.StartIndex + offset, itemRange.EndIndex + offset );
+ m_list[ i ] = new SelectionCellRangeWithItems( newItemRange, originalItems, columnRange );
+ continue;
+ }
+ else
+ {
+ int bottomRangeEndIndex = itemStartIndex + offset;
+ bottomRange = new SelectionRange( itemStartIndex - 1, itemRange.EndIndex );
+ topRange = new SelectionRange( itemRange.Length - bottomRange.Length - 1 + bottomRangeEndIndex, bottomRangeEndIndex );
+ }
+ }
+ else
+ {
+ if( itemRangeIntersection.StartIndex == itemRange.StartIndex )
+ {
+ SelectionRange newItemRange = new SelectionRange( itemRange.StartIndex + offset, itemRange.EndIndex + offset );
+ m_list[ i ] = new SelectionCellRangeWithItems( newItemRange, originalItems, columnRange );
+ continue;
+ }
+ else
+ {
+ int bottomRangeStartIndex = itemStartIndex + offset;
+ topRange = new SelectionRange( itemRange.StartIndex, itemStartIndex - 1 );
+ bottomRange = new SelectionRange( bottomRangeStartIndex, itemRange.Length - topRange.Length - 1 + bottomRangeStartIndex );
+ }
+ }
+
+ object[] topItems = null;
+ object[] bottomItems = null;
+
+ if( originalItems != null )
+ {
+ topItems = new object[ topRange.Length ];
+ Array.Copy( originalItems, 0, topItems, 0, topItems.Length );
+ bottomItems = new object[ bottomRange.Length ];
+ Array.Copy( originalItems, topItems.Length, bottomItems, 0, bottomItems.Length );
+ }
+
+ m_list[ i ] = new SelectionCellRangeWithItems( topRange, topItems, columnRange );
+ m_list.Insert( i + 1, new SelectionCellRangeWithItems( bottomRange, bottomItems, columnRange ) );
+ }
+ else
+ {
+ // Offset the index by the count added
+ SelectionRange newItemRange = new SelectionRange( itemRange.StartIndex + offset, itemRange.EndIndex + offset );
+ m_list[ i ] = new SelectionCellRangeWithItems( newItemRange, originalItems, columnRange );
+ }
+ }
+ }
+
+ public void OffsetIndexBasedOnSourceNewIndex( int maxOffset )
+ {
+ if( m_dataGridContext == null )
+ throw new InvalidOperationException( "We must have a DataGridContext to find the new index." );
+
+ CollectionView sourceItems = m_dataGridContext.Items;
+
+ for( int i = this.Count - 1; i >= 0; i-- )
+ {
+ SelectionCellRangeWithItems cellRangeWithItems = m_list[ i ];
+ SelectionRangeWithItems itemRangeWithItems = cellRangeWithItems.ItemRangeWithItems;
+ object[] items = itemRangeWithItems.Items;
+
+ if( items == null )
+ throw new InvalidOperationException( "We should have items to find the new index." );
+
+ object item = items[ 0 ];
+ SelectionRange itemRange = itemRangeWithItems.Range;
+ int startIndex = Math.Min( itemRange.StartIndex, itemRange.EndIndex );
+
+ if( maxOffset < 0 )
+ {
+ for( int j = 0; j >= maxOffset; j-- )
+ {
+ if( object.Equals( sourceItems.GetItemAt( startIndex ), item ) )
+ {
+ if( j != 0 )
+ {
+ SelectionRange newItemRange = new SelectionRange( itemRange.StartIndex + j, itemRange.EndIndex + j );
+ m_list[ i ] = new SelectionCellRangeWithItems( newItemRange, items, cellRangeWithItems.ColumnRange );
+ }
+
+ break;
+ }
+
+ startIndex--;
+
+ if( startIndex < 0 )
+ break;
+ }
+ }
+ else
+ {
+ int sourceItemCount = sourceItems.Count;
+
+ for( int j = 0; j <= maxOffset; j++ )
+ {
+ if( object.Equals( sourceItems.GetItemAt( startIndex ), item ) )
+ {
+ if( j != 0 )
+ {
+ SelectionRange newItemRange = new SelectionRange( itemRange.StartIndex + j, itemRange.EndIndex + j );
+ m_list[ i ] = new SelectionCellRangeWithItems( newItemRange, items, cellRangeWithItems.ColumnRange );
+ }
+
+ break;
+ }
+
+ startIndex++;
+
+ if( startIndex >= sourceItemCount )
+ break;
+ }
+ }
+ }
+ }
+
+ public List GetIntersectedColumnRanges( SelectionCellRange cellRange )
+ {
+ List intersectionRanges = new List();
+ int count = m_list.Count;
+
+ for( int i = 0; i < count; i++ )
+ {
+ SelectionCellRangeWithItems range = m_list[ i ];
+ SelectionCellRange cellRangeIntersection = range.CellRange.Intersect( cellRange );
+
+ if( !cellRangeIntersection.IsEmpty )
+ {
+ intersectionRanges.Add( cellRangeIntersection.ColumnRange );
+ }
+ }
+
+ return intersectionRanges;
+ }
+
+ public List GetIntersectedCellRangesWithItems( SelectionCellRange cellRange )
+ {
+ List intersectionCellRanges = new List();
+ int count = m_list.Count;
+
+ for( int i = 0; i < count; i++ )
+ {
+ SelectionCellRangeWithItems range = m_list[ i ];
+ SelectionCellRange cellRangeIntersection = range.CellRange.Intersect( cellRange );
+
+ if( !cellRangeIntersection.IsEmpty )
+ {
+ intersectionCellRanges.Add(
+ new SelectionCellRangeWithItems(
+ cellRangeIntersection.ItemRange,
+ range.ItemRangeWithItems.GetItems( cellRangeIntersection.ItemRange ),
+ cellRangeIntersection.ColumnRange ) );
+ }
+ }
+
+ return intersectionCellRanges;
+ }
+
+ #endregion PUBLIC METHODS
+
+ private List m_list;
+ private DataGridContext m_dataGridContext;
+ private int m_cellsCount;
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectedItemsStorage.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectedItemsStorage.cs
new file mode 100644
index 00000000..3978e448
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectedItemsStorage.cs
@@ -0,0 +1,532 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Collections;
+using System.Diagnostics;
+using System.Windows.Data;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class SelectedItemsStorage : IEnumerable
+ {
+ #region CONSTRUCTORS
+
+ internal SelectedItemsStorage( DataGridContext dataGridContext, int capacity )
+ {
+ // dataGridContext can be null for store that we don't want to auto-get Items from index
+ m_dataGridContext = dataGridContext;
+ m_list = new List( capacity );
+ }
+
+ internal SelectedItemsStorage( SelectedItemsStorage collection )
+ {
+ m_dataGridContext = collection.m_dataGridContext;
+ m_list = new List( collection.m_list );
+ m_itemsCount = collection.m_itemsCount;
+ }
+
+ #endregion CONSTRUCTORS
+
+ #region PUBLIC PROPERTIES
+
+ public int Count
+ {
+ get
+ {
+ return m_list.Count;
+ }
+ }
+
+ public int ItemsCount
+ {
+ get
+ {
+ return m_itemsCount;
+ }
+ }
+
+ public DataGridContext DataGridContext
+ {
+ get
+ {
+ return m_dataGridContext;
+ }
+ }
+
+ public SelectionRangeWithItems this[ int index ]
+ {
+ get
+ {
+ return m_list[ index ];
+ }
+ set
+ {
+ SelectionRangeWithItems oldRangeWithItems = m_list[ index ];
+ m_list[ index ] = value;
+ m_itemsCount += value.Length - oldRangeWithItems.Length;
+ }
+ }
+
+ #endregion PUBLIC PROPERTIES
+
+ #region IEnumerable
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return m_list.GetEnumerator();
+ }
+
+ #endregion IEnumerable
+
+ #region PUBLIC METHODS
+
+ public static void UpdateSelectionRangeWithItemsFromAdd(
+ DataGridContext dataGridContext,
+ SelectionRangeWithItems newRangeWithItems,
+ ref SelectionRangeWithItems lastRangeWithItems )
+ {
+ // Only work for adding at the end of an existing range.
+
+ object[] newRangeItems = newRangeWithItems.Items;
+
+ if( ( ( dataGridContext == null )
+ || ( ( dataGridContext.ItemsSourceCollection == null ) && ( newRangeItems == null ) )
+ || ( dataGridContext.ItemsSourceCollection is DataGridVirtualizingCollectionViewBase ) ) )
+ {
+ lastRangeWithItems = new SelectionRangeWithItems(
+ new SelectionRange( lastRangeWithItems.Range.StartIndex, newRangeWithItems.Range.EndIndex ),
+ null );
+
+ return;
+ }
+
+ object[] rangeToUpdateItems = lastRangeWithItems.Items;
+ object[] newItems;
+ int newItemsStartPosition;
+
+ if( newRangeWithItems == lastRangeWithItems )
+ {
+ if( newRangeItems != null )
+ return;
+
+ newItemsStartPosition = 0;
+ newItems = new object[ lastRangeWithItems.Length ];
+ }
+ else
+ {
+ newItemsStartPosition = lastRangeWithItems.Length;
+ newItems = new object[ lastRangeWithItems.Length + newRangeWithItems.Length ];
+ Array.Copy( rangeToUpdateItems, newItems, newItemsStartPosition );
+ }
+
+ CollectionView items = ( dataGridContext == null ) ? null : dataGridContext.Items;
+ int rangeToUpdateStartIndex = lastRangeWithItems.Range.StartIndex;
+ int rangeToUpdateEndIndex = lastRangeWithItems.Range.EndIndex;
+ int newRangeStartIndex = newRangeWithItems.Range.StartIndex;
+ int newRangeEndIndex = newRangeWithItems.Range.EndIndex;
+
+ // If new range have no items set, found the items and set it.
+ if( newRangeItems == null )
+ {
+ if( newRangeEndIndex > newRangeStartIndex )
+ {
+ for( int i = newRangeStartIndex; i <= newRangeEndIndex; i++ )
+ {
+ newItems[ newItemsStartPosition ] = items.GetItemAt( i );
+ newItemsStartPosition++;
+ }
+ }
+ else
+ {
+ for( int i = newRangeStartIndex; i >= newRangeEndIndex; i-- )
+ {
+ newItems[ newItemsStartPosition ] = items.GetItemAt( i );
+ newItemsStartPosition++;
+ }
+ }
+ }
+ else
+ {
+ for( int i = 0; i < newRangeItems.Length; i++ )
+ {
+ newItems[ newItemsStartPosition ] = newRangeItems[ i ];
+ newItemsStartPosition++;
+ }
+ }
+
+ lastRangeWithItems = new SelectionRangeWithItems(
+ new SelectionRange( rangeToUpdateStartIndex, newRangeEndIndex ),
+ newItems );
+ }
+
+ public int Add( SelectionRangeWithItems rangeWithItems )
+ {
+ Debug.Assert( !this.Contains( rangeWithItems.Range ) );
+
+ m_itemsCount += rangeWithItems.Length;
+ int count = m_list.Count;
+
+ if( count > 0 )
+ {
+ int lastIndex = count - 1;
+ SelectionRangeWithItems lastRangeWithItems = m_list[ lastIndex ];
+ SelectionRange lastRange = lastRangeWithItems.Range;
+
+ if( ( lastRange.EndIndex + 1 ) == rangeWithItems.Range.StartIndex )
+ {
+ Debug.Assert( rangeWithItems.Range.EndIndex > lastRange.EndIndex );
+
+ SelectedItemsStorage.UpdateSelectionRangeWithItemsFromAdd(
+ m_dataGridContext, rangeWithItems, ref lastRangeWithItems );
+
+ m_list[ lastIndex ] = lastRangeWithItems;
+ return lastIndex;
+ }
+ else if( ( lastRange.EndIndex - 1 ) == rangeWithItems.Range.StartIndex )
+ {
+ Debug.Assert( rangeWithItems.Range.EndIndex < lastRange.EndIndex );
+
+ SelectedItemsStorage.UpdateSelectionRangeWithItemsFromAdd(
+ m_dataGridContext, rangeWithItems, ref lastRangeWithItems );
+
+ m_list[ lastIndex ] = lastRangeWithItems;
+ return lastIndex;
+ }
+ }
+
+ SelectedItemsStorage.UpdateSelectionRangeWithItemsFromAdd(
+ m_dataGridContext, rangeWithItems, ref rangeWithItems );
+
+ m_list.Add( rangeWithItems );
+ return m_list.Count - 1;
+ }
+
+ public void Insert( int index, SelectionRangeWithItems rangeWithItems )
+ {
+ Debug.Assert( !this.Contains( rangeWithItems.Range ) );
+ m_itemsCount += rangeWithItems.Length;
+
+ SelectedItemsStorage.UpdateSelectionRangeWithItemsFromAdd(
+ m_dataGridContext, rangeWithItems, ref rangeWithItems );
+
+ m_list.Insert( index, rangeWithItems );
+ }
+
+ public void RemoveAt( int index )
+ {
+ m_itemsCount -= m_list[ index ].Length;
+ m_list.RemoveAt( index );
+ }
+
+ public void Clear()
+ {
+ m_itemsCount = 0;
+ m_list.Clear();
+ }
+
+ public SelectionRangeWithItems[] ToArray()
+ {
+ return m_list.ToArray();
+ }
+
+ public SelectionRange[] ToSelectionRangeArray()
+ {
+ int count = m_list.Count;
+ SelectionRange[] selections = new SelectionRange[ count ];
+
+ for( int i = 0; i < count; i++ )
+ {
+ selections[ i ] = m_list[ i ].Range;
+ }
+
+ return selections;
+ }
+
+ public bool Contains( int itemIndex )
+ {
+ SelectionRange itemRange = new SelectionRange( itemIndex );
+ int count = m_list.Count;
+
+ for( int i = 0; i < count; i++ )
+ {
+ if( !m_list[ i ].Range.Intersect( itemRange ).IsEmpty )
+ return true;
+ }
+
+ return false;
+ }
+
+ public bool Contains( SelectionRange itemRange )
+ {
+ int count = m_list.Count;
+
+ for( int i = 0; i < count; i++ )
+ {
+ if( !m_list[ i ].Range.Intersect( itemRange ).IsEmpty )
+ return true;
+ }
+
+ return false;
+ }
+
+ public bool Contains( SelectionRangeWithItems rangeWithItemsToCompare )
+ {
+ int count = m_list.Count;
+ SelectionRange rangeToCompare = rangeWithItemsToCompare.Range;
+
+ for( int i = 0; i < count; i++ )
+ {
+ SelectionRangeWithItems rangeWithItem = m_list[ i ];
+ SelectionRange rangeIntersection = rangeWithItem.Range.Intersect( rangeToCompare );
+
+ if( !rangeIntersection.IsEmpty )
+ {
+ if( rangeWithItemsToCompare.IsItemsEqual( rangeIntersection, rangeWithItem ) )
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public bool Remove( SelectionRangeWithItems rangeWithItemsToRemove )
+ {
+ bool removed = false;
+ SelectionRange rangeToRemove = rangeWithItemsToRemove.Range;
+ object[] itemsToRemove = rangeWithItemsToRemove.Items;
+ int itemsToRemoveCount = ( itemsToRemove == null ) ? 0 : itemsToRemove.Length;
+ int itemOffsetToRemove = 0;
+ bool rangeToRemoveIsEmpty = rangeToRemove.IsEmpty;
+
+ do
+ {
+ for( int i = m_list.Count - 1; i >= 0; i-- )
+ {
+ SelectionRangeWithItems rangeWithItems = m_list[ i ];
+ SelectionRange range = rangeWithItems.Range;
+ SelectionRange rangeIntersection;
+ object[] oldRangeItems = rangeWithItems.Items;
+
+ if( rangeToRemoveIsEmpty )
+ {
+ int itemOffset = Array.IndexOf( oldRangeItems, itemsToRemove[ itemOffsetToRemove ] );
+
+ if( itemOffset == -1 )
+ continue;
+
+ rangeIntersection = new SelectionRange( range.GetIndexFromItemOffset( itemOffset ) );
+ }
+ else
+ {
+ rangeIntersection = range.Intersect( rangeToRemove );
+
+ if( rangeIntersection.IsEmpty )
+ continue;
+
+ if( !rangeWithItems.IsItemsEqual( rangeIntersection, rangeWithItemsToRemove ) )
+ continue;
+ }
+
+ removed = true;
+ m_itemsCount -= rangeIntersection.Length;
+ SelectionRange[] newRanges = range.Exclude( rangeIntersection );
+
+ if( newRanges.Length == 0 )
+ {
+ m_list.RemoveAt( i );
+ }
+ else
+ {
+ SelectionRange newRange = newRanges[ 0 ];
+ m_list[ i ] = new SelectionRangeWithItems( newRange, rangeWithItems.GetItems( newRange ) );
+
+ if( newRanges.Length > 1 )
+ {
+ Debug.Assert( newRanges.Length == 2 );
+ newRange = newRanges[ 1 ];
+ m_list.Insert( i + 1, new SelectionRangeWithItems( newRange, rangeWithItems.GetItems( newRange ) ) );
+ }
+ }
+ }
+
+ itemOffsetToRemove++;
+ } while( ( rangeToRemoveIsEmpty ) && ( itemOffsetToRemove < itemsToRemoveCount ) );
+
+ return removed;
+ }
+
+ public void OffsetIndex( int startIndex, int offset )
+ {
+ // Used to offset index after an add or remove from the data source of the grid.
+
+ SelectionRange offsetRange = new SelectionRange(
+ startIndex, startIndex + Math.Abs( offset ) - 1 );
+
+ for( int i = this.Count - 1; i >= 0; i-- )
+ {
+ SelectionRangeWithItems rangeWithItems = m_list[ i ];
+ SelectionRange range = rangeWithItems.Range;
+
+ if( offsetRange > range )
+ continue;
+
+ SelectionRange rangeIntersection = range.Intersect( offsetRange );
+ object[] originalItems = rangeWithItems.Items;
+
+ if( !rangeIntersection.IsEmpty )
+ {
+ // Should only happen when adding since when we remove data from the source, we remove the
+ // the range from the list.
+ Debug.Assert( offset > 0 );
+
+ // Offset the index higher than the start index of the new added item
+ SelectionRange topRange;
+ SelectionRange bottomRange;
+
+ if( range.StartIndex > range.EndIndex )
+ {
+ if( rangeIntersection.EndIndex == range.EndIndex )
+ {
+ SelectionRange newRange = new SelectionRange( range.StartIndex + offset, range.EndIndex + offset );
+ m_list[ i ] = new SelectionRangeWithItems( newRange, originalItems );
+ continue;
+ }
+ else
+ {
+ int bottomRangeEndIndex = startIndex + offset;
+ bottomRange = new SelectionRange( startIndex - 1, range.EndIndex );
+ topRange = new SelectionRange( range.Length - bottomRange.Length - 1 + bottomRangeEndIndex, bottomRangeEndIndex );
+ }
+ }
+ else
+ {
+ if( rangeIntersection.StartIndex == range.StartIndex )
+ {
+ SelectionRange newRange = new SelectionRange( range.StartIndex + offset, range.EndIndex + offset );
+ m_list[ i ] = new SelectionRangeWithItems( newRange, originalItems );
+ continue;
+ }
+ else
+ {
+ int bottomRangeStartIndex = startIndex + offset;
+ topRange = new SelectionRange( range.StartIndex, startIndex - 1 );
+ bottomRange = new SelectionRange( bottomRangeStartIndex, range.Length - topRange.Length - 1 + bottomRangeStartIndex );
+ }
+ }
+
+ object[] topItems = null;
+ object[] bottomItems = null;
+
+ if( originalItems != null )
+ {
+ topItems = new object[ topRange.Length ];
+ Array.Copy( originalItems, 0, topItems, 0, topItems.Length );
+ bottomItems = new object[ bottomRange.Length ];
+ Array.Copy( originalItems, topItems.Length, bottomItems, 0, bottomItems.Length );
+ }
+
+ m_list[ i ] = new SelectionRangeWithItems( topRange, topItems );
+ m_list.Insert( i + 1, new SelectionRangeWithItems( bottomRange, bottomItems ) );
+ }
+ else
+ {
+ // Offset the index by the count added
+ SelectionRange newRange = new SelectionRange( range.StartIndex + offset, range.EndIndex + offset );
+ m_list[ i ] = new SelectionRangeWithItems( newRange, originalItems );
+ }
+ }
+ }
+
+ public void OffsetIndexBasedOnSourceNewIndex( int maxOffset )
+ {
+ if( m_dataGridContext == null )
+ throw new InvalidOperationException( "We must have a DataGridContext to find the new index." );
+
+ CollectionView sourceItems = m_dataGridContext.Items;
+
+ for( int i = this.Count - 1; i >= 0; i-- )
+ {
+ SelectionRangeWithItems rangeWithItems = m_list[ i ];
+ object[] items = rangeWithItems.Items;
+
+ if( items == null )
+ throw new InvalidOperationException( "We should have items to find the new index." );
+
+ object item = items[ 0 ];
+ SelectionRange range = rangeWithItems.Range;
+ int startIndex = Math.Min( range.StartIndex, range.EndIndex );
+
+ if( maxOffset < 0 )
+ {
+ for( int j = 0; j >= maxOffset; j-- )
+ {
+ if( object.Equals( sourceItems.GetItemAt( startIndex ), item ) )
+ {
+ if( j != 0 )
+ {
+ SelectionRange newRange = new SelectionRange( range.StartIndex + j, range.EndIndex + j );
+ m_list[ i ] = new SelectionRangeWithItems( newRange, items );
+ }
+
+ break;
+ }
+
+ startIndex--;
+
+ if( startIndex < 0 )
+ break;
+ }
+ }
+ else
+ {
+ int sourceItemCount = sourceItems.Count;
+
+ for( int j = 0; j <= maxOffset; j++ )
+ {
+ if( object.Equals( sourceItems.GetItemAt( startIndex ), item ) )
+ {
+ if( j != 0 )
+ {
+ SelectionRange newRange = new SelectionRange( range.StartIndex + j, range.EndIndex + j );
+ m_list[ i ] = new SelectionRangeWithItems( newRange, items );
+ }
+
+ break;
+ }
+
+ startIndex++;
+
+ if( startIndex >= sourceItemCount )
+ break;
+ }
+ }
+ }
+ }
+
+ #endregion PUBLIC METHODS
+
+ private List m_list;
+ private DataGridContext m_dataGridContext;
+ private int m_itemsCount;
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionCellRange.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionCellRange.cs
new file mode 100644
index 00000000..48593f76
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionCellRange.cs
@@ -0,0 +1,219 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Diagnostics;
+
+namespace Xceed.Wpf.DataGrid
+{
+ public struct SelectionCellRange
+ {
+ public static readonly SelectionCellRange Empty = new SelectionCellRange( -1, -1 );
+
+ #region CONSTRUCTORS
+
+ public SelectionCellRange( int itemIndex, int columnIndex )
+ {
+ if( ( itemIndex == -1 || columnIndex == -1 ) && ( itemIndex != -1 || columnIndex != -1 ) )
+ throw new ArgumentException( "itemIndex and columnIndex must be equal if the value of one is -1." );
+
+ if( itemIndex < -1 )
+ throw new ArgumentOutOfRangeException( "itemIndex", "itemIndex must be equal to or greater than -1." );
+
+ if( columnIndex < -1 )
+ throw new ArgumentOutOfRangeException( "columnIndex", "columnIndex must be equal to or greater than -1." );
+
+ m_itemRange = new SelectionRange( itemIndex );
+ m_columnRange = new SelectionRange( columnIndex );
+ }
+
+ public SelectionCellRange( int itemStartIndex, int columnStartIndex, int itemEndIndex, int columnEndIndex )
+ {
+ if( itemStartIndex < 0 )
+ throw new ArgumentOutOfRangeException( "itemStartIndex", "itemStartIndex must be equal to or greater than zero." );
+
+ if( itemEndIndex < 0 )
+ throw new ArgumentOutOfRangeException( "itemEndIndex", "itemEndIndex must be equal to or greater than zero." );
+
+ if( columnStartIndex < 0 )
+ throw new ArgumentOutOfRangeException( "columnStartIndex", "columnStartIndex must be equal to or greater than zero." );
+
+ if( columnEndIndex < 0 )
+ throw new ArgumentOutOfRangeException( "columnEndIndex", "columnEndIndex must be equal to or greater than zero." );
+
+ m_itemRange = new SelectionRange( itemStartIndex, itemEndIndex );
+ m_columnRange = new SelectionRange( columnStartIndex, columnEndIndex );
+ }
+
+ public SelectionCellRange( SelectionRange itemRange, SelectionRange columnRange )
+ {
+ m_itemRange = itemRange;
+ m_columnRange = columnRange;
+ }
+
+ #endregion CONSTRUCTORS
+
+ #region ItemRange Property
+
+ public SelectionRange ItemRange
+ {
+ get
+ {
+ return m_itemRange;
+ }
+ set
+ {
+ m_itemRange = value;
+ }
+ }
+
+ private SelectionRange m_itemRange;
+
+ #endregion ItemRange Property
+
+ #region ColumnRange Property
+
+ public SelectionRange ColumnRange
+ {
+ get
+ {
+ return m_columnRange;
+ }
+ set
+ {
+ m_columnRange = value;
+ }
+ }
+
+ private SelectionRange m_columnRange;
+
+ #endregion ColumnRange Property
+
+ #region Length Property
+
+ public int Length
+ {
+ get
+ {
+ return m_itemRange.Length * m_columnRange.Length;
+ }
+ }
+
+ #endregion Length Property
+
+ #region IsEmpty Property
+
+ public bool IsEmpty
+ {
+ get
+ {
+ return m_itemRange.IsEmpty;
+ }
+ }
+
+ #endregion IsEmpty Property
+
+ #region PUBLIC METHODS
+
+ public static bool operator ==( SelectionCellRange range1, SelectionCellRange range2 )
+ {
+ return range1.Equals( range2 );
+ }
+
+ public static bool operator !=( SelectionCellRange range1, SelectionCellRange range2 )
+ {
+ return !range1.Equals( range2 );
+ }
+
+ public SelectionCellRange Intersect( SelectionCellRange range )
+ {
+ SelectionCellRange cellRangeIntersection = SelectionCellRange.Empty;
+
+ SelectionRange itemRange = range.ItemRange;
+ SelectionRange itemRangeIntersection = m_itemRange.Intersect( itemRange );
+
+ if( itemRangeIntersection.IsEmpty )
+ return SelectionCellRange.Empty;
+
+ SelectionRange columnRange = range.ColumnRange;
+ SelectionRange columnRangeIntersection = m_columnRange.Intersect( columnRange );
+
+ if( columnRangeIntersection.IsEmpty )
+ return SelectionCellRange.Empty;
+
+ return new SelectionCellRange( itemRangeIntersection, columnRangeIntersection );
+ }
+
+ public SelectionCellRange[] Exclude( SelectionCellRange cellRangeToExclude )
+ {
+ if( cellRangeToExclude.IsEmpty )
+ return new SelectionCellRange[] { this };
+
+ SelectionCellRange cellRangeIntersection = this.Intersect( cellRangeToExclude );
+
+ if( cellRangeIntersection.IsEmpty )
+ return new SelectionCellRange[] { this };
+
+ SelectionRange[] itemRanges = m_itemRange.Exclude( cellRangeToExclude.ItemRange );
+ SelectionRange[] columnRanges = m_columnRange.Exclude( cellRangeToExclude.ColumnRange );
+ SelectionCellRange[] cellRanges = new SelectionCellRange[ itemRanges.Length + columnRanges.Length ];
+ int index = 0;
+
+ foreach( SelectionRange itemRange in itemRanges )
+ {
+ cellRanges[ index ] = new SelectionCellRange( itemRange, m_columnRange );
+ index++;
+ }
+
+ foreach( SelectionRange columnRange in columnRanges )
+ {
+ cellRanges[ index ] = new SelectionCellRange( cellRangeIntersection.ItemRange, columnRange );
+ index++;
+ }
+
+ Debug.Assert( index == cellRanges.Length );
+ return cellRanges;
+ }
+
+ public override bool Equals( object obj )
+ {
+ if( !( obj is SelectionCellRange ) )
+ return false;
+
+ SelectionCellRange selectionRange = ( SelectionCellRange )obj;
+
+ return ( selectionRange.m_itemRange == m_itemRange )
+ && ( selectionRange.m_columnRange == m_columnRange );
+ }
+
+ public override int GetHashCode()
+ {
+ return ( m_itemRange.GetHashCode() ^ m_columnRange.GetHashCode() );
+ }
+
+ #endregion PUBLIC METHODS
+
+ #region INTERNAL METHODS
+
+ #endregion INTERNAL METHODS
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionCellRangeCollection.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionCellRangeCollection.cs
new file mode 100644
index 00000000..a548eec9
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionCellRangeCollection.cs
@@ -0,0 +1,308 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Collections.ObjectModel;
+using System.Collections;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class SelectionCellRangeCollection : IList, IList
+ {
+ public SelectionCellRangeCollection( SelectedCellsStorage list )
+ {
+ m_list = list;
+ }
+
+ public SelectionCellRange this[ int index ]
+ {
+ get
+ {
+ return m_list[ index ].CellRange;
+ }
+ set
+ {
+ throw new NotSupportedException();
+ }
+ }
+
+ public int Count
+ {
+ get
+ {
+ return m_list.Count;
+ }
+ }
+
+ public bool IsReadOnly
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ public bool IsFixedSize
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ #region IList Members
+
+ object IList.this[ int index ]
+ {
+ get
+ {
+ return this[ index ];
+ }
+ set
+ {
+ this[ index ] = ( SelectionCellRange )value;
+ }
+ }
+
+ int IList.Add( object item )
+ {
+ this.Add( ( SelectionCellRange )item );
+ return m_list.Count - 1;
+ }
+
+ void IList.Clear()
+ {
+ this.Clear();
+ }
+
+ void IList.Insert( int index, object item )
+ {
+ this.Insert( index, ( SelectionCellRange )item );
+ }
+
+ void IList.Remove( object item )
+ {
+ this.Remove( ( SelectionCellRange )item );
+ }
+
+ void IList.RemoveAt( int index )
+ {
+ this.RemoveAt( index );
+ }
+
+ bool IList.Contains( object item )
+ {
+ return this.Contains( ( SelectionCellRange )item );
+ }
+
+ int IList.IndexOf( object item )
+ {
+ return this.IndexOf( ( SelectionCellRange )item );
+ }
+
+ #endregion IList Members
+
+ #region ICollection Members
+
+ object ICollection.SyncRoot
+ {
+ get
+ {
+ return m_list;
+ }
+ }
+
+ bool ICollection.IsSynchronized
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ void ICollection.CopyTo( Array array, int arrayIndex )
+ {
+ int count = m_list.Count;
+
+ for( int i = 0; i < count; i++ )
+ {
+ array.SetValue( m_list[ i ].CellRange, arrayIndex );
+ arrayIndex++;
+ }
+ }
+
+ #endregion ICollection Members
+
+ #region IList Members
+
+ public int IndexOf( SelectionCellRange item )
+ {
+ int count = m_list.Count;
+
+ for( int i = 0; i < count; i++ )
+ {
+ if( m_list[ i ].CellRange == item )
+ return i;
+ }
+
+ return -1;
+ }
+
+ public void Insert( int index, SelectionCellRange item )
+ {
+ if( ( index < 0 ) || ( index > m_list.Count ) )
+ throw new ArgumentOutOfRangeException( "index", index, "index must be greater than or equal to zero and less than or equal to Count." );
+
+ DataGridContext dataGridContext = m_list.DataGridContext;
+ SelectionManager selectionManager = dataGridContext.DataGridControl.SelectionChangerManager;
+ selectionManager.Begin();
+
+ try
+ {
+ selectionManager.SelectCells( dataGridContext, new SelectionCellRangeWithItems( item.ItemRange, null, item.ColumnRange ) );
+ }
+ finally
+ {
+ selectionManager.End( false, true, true );
+ }
+ }
+
+ public void RemoveAt( int index )
+ {
+ if( ( index < 0 ) || ( index >= m_list.Count ) )
+ throw new ArgumentOutOfRangeException( "index", index, "index must be greater than or equal to zero and less than Count." );
+
+ DataGridContext dataGridContext = m_list.DataGridContext;
+ SelectionManager selectionManager = dataGridContext.DataGridControl.SelectionChangerManager;
+ selectionManager.Begin();
+
+ try
+ {
+ SelectionCellRange cellRange = this[ index ];
+ selectionManager.UnselectCells( dataGridContext, new SelectionCellRangeWithItems( cellRange.ItemRange, null, cellRange.ColumnRange ) );
+ }
+ finally
+ {
+ selectionManager.End( false, true, true );
+ }
+ }
+
+ #endregion
+
+ #region ICollection Members
+
+ public void Add( SelectionCellRange item )
+ {
+ DataGridContext dataGridContext = m_list.DataGridContext;
+ DataGridControl dataGridControl = dataGridContext.DataGridControl;
+
+ if( dataGridControl.SelectionUnit == SelectionUnit.Row )
+ throw new InvalidOperationException( "Can't add cell range when SelectionUnit is Row." );
+
+ SelectionManager selectionManager = dataGridControl.SelectionChangerManager;
+ selectionManager.Begin();
+
+ try
+ {
+ selectionManager.SelectCells( dataGridContext, new SelectionCellRangeWithItems( item.ItemRange, null, item.ColumnRange ) );
+ }
+ finally
+ {
+ selectionManager.End( false, true, true );
+ }
+ }
+
+ public void Clear()
+ {
+ DataGridContext dataGridContext = m_list.DataGridContext;
+ SelectionManager selectionManager = dataGridContext.DataGridControl.SelectionChangerManager;
+ selectionManager.Begin();
+
+ try
+ {
+ selectionManager.UnselectAllCells( dataGridContext );
+ }
+ finally
+ {
+ selectionManager.End( false, true, true );
+ }
+ }
+
+ public bool Contains( SelectionCellRange item )
+ {
+ return this.IndexOf( item ) != -1;
+ }
+
+ public void CopyTo( SelectionCellRange[] array, int arrayIndex )
+ {
+ int count = m_list.Count;
+
+ for( int i = 0; i < count; i++ )
+ {
+ array[ arrayIndex ] = m_list[ i ].CellRange;
+ arrayIndex++;
+ }
+ }
+
+ public bool Remove( SelectionCellRange item )
+ {
+ DataGridContext dataGridContext = m_list.DataGridContext;
+ SelectionManager selectionManager = dataGridContext.DataGridControl.SelectionChangerManager;
+ selectionManager.Begin();
+
+ try
+ {
+ return selectionManager.UnselectCells( dataGridContext, new SelectionCellRangeWithItems( item.ItemRange, null, item.ColumnRange ) );
+ }
+ finally
+ {
+ selectionManager.End( false, true, true );
+ }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ public IEnumerator GetEnumerator()
+ {
+ // We use a foreach to get the exception when the list is changed
+ foreach( SelectionCellRangeWithItems cellRangeWithItems in m_list )
+ {
+ yield return cellRangeWithItems.CellRange;
+ }
+ }
+
+ #endregion
+
+ #region IEnumerable Members
+
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ #endregion
+
+ private SelectedCellsStorage m_list;
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionCellRangeWithItems.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionCellRangeWithItems.cs
new file mode 100644
index 00000000..a99f036e
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionCellRangeWithItems.cs
@@ -0,0 +1,141 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal struct SelectionCellRangeWithItems
+ {
+ public static readonly SelectionCellRangeWithItems Empty = new SelectionCellRangeWithItems(
+ SelectionRange.Empty, null, SelectionRange.Empty );
+
+ public SelectionCellRangeWithItems( int itemIndex, object item, int columnIndex )
+ : this( new SelectionRange( itemIndex ), new object[] { item }, new SelectionRange( columnIndex ) )
+ {
+ }
+
+ public SelectionCellRangeWithItems( SelectionRange itemRange, object[] items, SelectionRange columnRange )
+ {
+ if( ( items != null ) && ( items.Length != itemRange.Length ) )
+ throw new ArgumentException( "itemRange and items must have the same length." );
+
+ m_itemRangeWithItems = new SelectionRangeWithItems( itemRange, items );
+ m_columnRange = columnRange;
+ }
+
+ #region ItemRangeWithItems Property
+
+ public SelectionRangeWithItems ItemRangeWithItems
+ {
+ get
+ {
+ return m_itemRangeWithItems;
+ }
+ }
+
+ SelectionRangeWithItems m_itemRangeWithItems;
+
+ #endregion ItemRangeWithItems Property
+
+ #region ItemRange Property
+
+ public SelectionRange ItemRange
+ {
+ get
+ {
+ return m_itemRangeWithItems.Range;
+ }
+ }
+
+ #endregion ItemRange Property
+
+ #region ColumnRange Property
+
+ public SelectionRange ColumnRange
+ {
+ get
+ {
+ return m_columnRange;
+ }
+ }
+
+ SelectionRange m_columnRange;
+
+ #endregion ColumnRange Property
+
+ #region CellRange Property
+
+ public SelectionCellRange CellRange
+ {
+ get
+ {
+ return new SelectionCellRange( m_itemRangeWithItems.Range, m_columnRange );
+ }
+ }
+
+ #endregion CellRange Property
+
+ #region Length Property
+
+ public int Length
+ {
+ get
+ {
+ return m_itemRangeWithItems.Range.Length * m_columnRange.Length;
+ }
+ }
+
+ #endregion Length Property
+
+ #region PUBLIC METHODS
+
+ public static bool operator ==( SelectionCellRangeWithItems rangeWithItems1, SelectionCellRangeWithItems rangeWithItems2 )
+ {
+ return
+ ( rangeWithItems1.m_columnRange == rangeWithItems2.m_columnRange )
+ && ( rangeWithItems1.m_itemRangeWithItems == rangeWithItems2.m_itemRangeWithItems );
+ }
+
+ public static bool operator !=( SelectionCellRangeWithItems rangeWithItems1, SelectionCellRangeWithItems rangeWithItems2 )
+ {
+ return
+ ( rangeWithItems1.m_columnRange != rangeWithItems2.m_columnRange )
+ || ( rangeWithItems1.m_itemRangeWithItems != rangeWithItems2.m_itemRangeWithItems );
+ }
+
+ public override int GetHashCode()
+ {
+ return ( m_itemRangeWithItems.GetHashCode() ^ m_columnRange.GetHashCode() );
+ }
+
+ public override bool Equals( object obj )
+ {
+ if( !( obj is SelectionCellRangeWithItems ) )
+ return false;
+
+ return ( ( SelectionCellRangeWithItems )obj ) == this;
+ }
+
+ #endregion PUBLIC METHODS
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionChangedEventManager.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionChangedEventManager.cs
new file mode 100644
index 00000000..a7d64785
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionChangedEventManager.cs
@@ -0,0 +1,81 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Windows;
+using System.Windows.Controls.Primitives;
+using System.Windows.Controls;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class SelectionChangedEventManager : WeakEventManager
+ {
+ private SelectionChangedEventManager()
+ {
+ }
+
+ public static void AddListener( Selector source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedAddListener( source, listener );
+ }
+
+ public static void RemoveListener( Selector source, IWeakEventListener listener )
+ {
+ CurrentManager.ProtectedRemoveListener( source, listener );
+ }
+
+ protected override void StartListening( object source )
+ {
+ Selector selector = ( Selector )source;
+
+ if( selector != null )
+ selector.SelectionChanged += new SelectionChangedEventHandler( this.Selector_SelectionChanged );
+ }
+
+ protected override void StopListening( object source )
+ {
+ Selector selector = ( Selector )source;
+
+ if( selector != null )
+ selector.SelectionChanged -= new SelectionChangedEventHandler( this.Selector_SelectionChanged );
+ }
+
+ private static SelectionChangedEventManager CurrentManager
+ {
+ get
+ {
+ Type managerType = typeof( SelectionChangedEventManager );
+ SelectionChangedEventManager currentManager = ( SelectionChangedEventManager )WeakEventManager.GetCurrentManager( managerType );
+
+ if( currentManager == null )
+ {
+ currentManager = new SelectionChangedEventManager();
+ WeakEventManager.SetCurrentManager( managerType, currentManager );
+ }
+
+ return currentManager;
+ }
+ }
+
+ private void Selector_SelectionChanged( object sender, SelectionChangedEventArgs e )
+ {
+ this.DeliverEvent( sender, e );
+ }
+ }
+}
diff --git a/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionChanger.cs b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionChanger.cs
new file mode 100644
index 00000000..e252c930
--- /dev/null
+++ b/ExtendedWPFToolkitSolution/Src/Xceed.Wpf.DataGrid/SelectionChanger.cs
@@ -0,0 +1,1265 @@
+/************************************************************************
+
+ Extended WPF Toolkit
+
+ Copyright (C) 2010-2012 Xceed Software Inc.
+
+ This program is provided to you under the terms of the Microsoft Public
+ License (Ms-PL) as published at http://wpftoolkit.codeplex.com/license
+
+ This program can be provided to you by Xceed Software Inc. under a
+ proprietary commercial license agreement for use in non-Open Source
+ projects. The commercial version of Extended WPF Toolkit also includes
+ priority technical support, commercial updates, and many additional
+ useful WPF controls if you license Xceed Business Suite for WPF.
+
+ Visit http://xceed.com and follow @datagrid on Twitter.
+
+ **********************************************************************/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Collections;
+using System.Windows.Controls;
+using System.Diagnostics;
+using System.Windows;
+using System.Collections.Specialized;
+using System.Windows.Data;
+
+namespace Xceed.Wpf.DataGrid
+{
+ internal class SelectionChanger
+ {
+ #region CONSTRUCTORS
+
+ public SelectionChanger( DataGridContext owner )
+ {
+ m_owner = owner;
+ m_itemsToSelect = new SelectedItemsStorage( owner, 2 );
+ m_itemsToUnselect = new SelectedItemsStorage( owner, 2 );
+ m_cellsToSelect = new SelectedCellsStorage( owner, 2 );
+ m_cellsToUnselect = new SelectedCellsStorage( owner, 2 );
+ m_toDeferSelect = new List( 1 );
+ m_sourceChanges = new List( 2 );
+ }
+
+ #endregion CONSTRUCTORS
+
+ #region Owner Property
+
+ public DataGridContext Owner
+ {
+ get
+ {
+ return m_owner;
+ }
+ }
+
+ private DataGridContext m_owner;
+
+ #endregion Owner Property
+
+ #region PUBLIC METHODS
+
+ public bool SelectItems( SelectionRangeWithItems rangeWithItems )
+ {
+ SelectionRange range = rangeWithItems.Range;
+
+ if( !( m_owner.ItemsSourceCollection is DataGridVirtualizingCollectionViewBase ) )
+ {
+ if( range.IsEmpty )
+ {
+ foreach( object item in rangeWithItems.Items )
+ {
+ if( !m_toDeferSelect.Contains( item ) )
+ m_toDeferSelect.Add( item );
+ }
+
+ return false;
+ }
+ }
+ else
+ {
+ if( range.IsEmpty )
+ throw new ArgumentException( "rangeWithItems.Range can't be empty when we are using a DataGridVirtualizingCollectionView", "rangeWithItems" );
+ }
+
+ if( rangeWithItems.Length == 1 )
+ {
+ if( !m_itemsToUnselect.Remove( rangeWithItems ) )
+ {
+ if( m_owner.SelectedItemsStore.Contains( rangeWithItems ) )
+ return false;
+
+ if( m_itemsToSelect.Contains( range ) )
+ return false;
+
+ this.m_itemsToSelect.Add( rangeWithItems );
+ }
+
+ return true;
+ }
+ else
+ {
+ bool selectionChanged = m_itemsToUnselect.Remove( rangeWithItems );
+
+ SelectedItemsStorage tempStorage = new SelectedItemsStorage( m_owner, 8 );
+ tempStorage.Add( rangeWithItems );
+
+ // Remove the currently selected item from the new range to select
+ foreach( SelectionRangeWithItems existingSelectionRangeWithItems in m_owner.SelectedItemsStore )
+ {
+ tempStorage.Remove( existingSelectionRangeWithItems );
+ }
+
+ // Remove the pending item to be selected from the new range to select
+ foreach( SelectionRangeWithItems existingSelectionRangeWithItems in m_itemsToSelect )
+ {
+ tempStorage.Remove( existingSelectionRangeWithItems );
+ }
+
+ if( tempStorage.Count > 0 )
+ {
+ selectionChanged = true;
+
+ foreach( SelectionRangeWithItems rangeWithItemsToAdd in tempStorage )
+ {
+ m_itemsToSelect.Add( rangeWithItemsToAdd );
+ }
+ }
+
+ return selectionChanged;
+ }
+ }
+
+ public bool SelectCells( SelectionCellRangeWithItems cellRangeWithItems )
+ {
+ SelectionRange itemRange = cellRangeWithItems.ItemRange;
+
+ if( itemRange.IsEmpty )
+ throw new ArgumentException( "cellRangeWithItems.ItemRange can't be empty", "cellRangeWithItems" );
+
+ if( cellRangeWithItems.Length == 1 )
+ {
+ if( !m_cellsToUnselect.Remove( cellRangeWithItems ) )
+ {
+ if( m_owner.SelectedCellsStore.Contains( cellRangeWithItems ) )
+ return false;
+
+ if( m_cellsToSelect.Contains( cellRangeWithItems.CellRange ) )
+ return false;
+
+ this.m_cellsToSelect.Add( cellRangeWithItems );
+ }
+
+ return true;
+ }
+ else
+ {
+ bool selectionChanged = m_cellsToUnselect.Remove( cellRangeWithItems );
+
+ SelectedCellsStorage tempStorage = new SelectedCellsStorage( m_owner, 8 );
+ tempStorage.Add( cellRangeWithItems );
+
+ // Remove the currently selected item from the new range to select
+ foreach( SelectionCellRangeWithItems existingSelectionCellRangeWithItems in m_owner.SelectedCellsStore )
+ {
+ tempStorage.Remove( existingSelectionCellRangeWithItems );
+ }
+
+ // Remove the pending item to be selected from the new range to select
+ foreach( SelectionCellRangeWithItems existingSelectionCellRangeWithItems in m_cellsToSelect )
+ {
+ tempStorage.Remove( existingSelectionCellRangeWithItems );
+ }
+
+ if( tempStorage.Count > 0 )
+ {
+ selectionChanged = true;
+
+ foreach( SelectionCellRangeWithItems cellRangeWithItemsToAdd in tempStorage )
+ {
+ m_cellsToSelect.Add( cellRangeWithItemsToAdd );
+ }
+ }
+
+ return selectionChanged;
+ }
+ }
+
+ public bool UnselectItems( SelectionRangeWithItems rangeWithItems )
+ {
+ if( !( m_owner.ItemsSourceCollection is DataGridVirtualizingCollectionViewBase ) )
+ {
+ if( m_toDeferSelect.Count > 0 )
+ {
+ foreach( object item in rangeWithItems.Items )
+ {
+ m_toDeferSelect.Remove( item );
+ }
+ }
+ }
+
+ SelectionRange range = rangeWithItems.Range;
+
+ if( range.IsEmpty )
+ {
+ // We have no index we have to remove based on item
+ bool selectionChanged = false;
+
+ List itemsRangeToRemove = new List();
+ List itemsToUnselect = new List( rangeWithItems.Items );
+ int count = itemsToUnselect.Count;
+
+ for( int i = count - 1; i >= 0; i-- )
+ {
+ object itemToUnselect = itemsToUnselect[ i ];
+ bool selectionAdded = false;
+
+ foreach( SelectionRangeWithItems existingSelectionRangeWithItems in m_itemsToSelect )
+ {
+ int index = Array.IndexOf( existingSelectionRangeWithItems.Items, itemToUnselect );
+
+ if( index > -1 )
+ {
+ selectionAdded = true;
+
+ itemsRangeToRemove.Add(
+ new SelectionRangeWithItems(
+ existingSelectionRangeWithItems.Range.GetIndexFromItemOffset( index ),
+ itemToUnselect ) );
+ }
+ }
+
+ if( selectionAdded )
+ {
+ itemsToUnselect.RemoveAt( i );
+ }
+ }
+
+ // Remove the currently unselected item from the new range to select
+ foreach( SelectionRangeWithItems itemRangeToRemove in itemsRangeToRemove )
+ {
+ selectionChanged |= m_itemsToSelect.Remove( itemRangeToRemove );
+ }
+
+ count = itemsToUnselect.Count;
+
+ for( int i = 0; i < count; i++ )
+ {
+ object itemToUnselect = itemsToUnselect[ i ];
+
+ foreach( SelectionRangeWithItems existingSelectionRangeWithItems in m_owner.SelectedItemsStore )
+ {
+ int index = Array.IndexOf( existingSelectionRangeWithItems.Items, itemToUnselect );
+
+ if( index > -1 )
+ {
+ index = existingSelectionRangeWithItems.Range.GetIndexFromItemOffset( index );
+
+ if( !m_itemsToUnselect.Contains( index ) )
+ {
+ selectionChanged = true;
+ m_itemsToUnselect.Add( new SelectionRangeWithItems( index, itemToUnselect ) );
+ }
+ }
+ }
+ }
+
+ return selectionChanged;
+ }
+
+ if( range.Length == 1 )
+ {
+ if( !m_itemsToSelect.Remove( rangeWithItems ) )
+ {
+ if( !m_owner.SelectedItemsStore.Contains( range ) )
+ return false;
+
+ if( m_itemsToUnselect.Contains( range ) )
+ return false;
+
+ m_itemsToUnselect.Add( rangeWithItems );
+ }
+
+ return true;
+ }
+ else
+ {
+ SelectedItemsStorage tempStorage = new SelectedItemsStorage( m_owner, 8 );
+ tempStorage.Add( rangeWithItems );
+
+ // Remove the currently selected item from the new range to select
+ foreach( SelectionRangeWithItems existingSelectionRangeWithItems in m_itemsToSelect )
+ {
+ if( !range.Intersect( existingSelectionRangeWithItems.Range ).IsEmpty )
+ {
+ tempStorage.Remove( existingSelectionRangeWithItems );
+ }
+ }
+
+ bool selectionChanged = m_itemsToSelect.Remove( rangeWithItems );
+
+ if( tempStorage.Count > 0 )
+ {
+ selectionChanged = true;
+
+ foreach( SelectionRangeWithItems rangeWithItemsToAdd in tempStorage )
+ {
+ Debug.Assert( !m_itemsToUnselect.Contains( rangeWithItemsToAdd.Range ) );
+ m_itemsToUnselect.Add( rangeWithItemsToAdd );
+ }
+ }
+
+ return selectionChanged;
+ }
+ }
+
+ public bool UnselectCells( SelectionCellRangeWithItems cellRangeWithItems )
+ {
+ SelectionRange itemRange = cellRangeWithItems.ItemRange;
+ SelectionRange columnRange = cellRangeWithItems.ColumnRange;
+ SelectionCellRange cellRange = cellRangeWithItems.CellRange;
+
+ if( itemRange.IsEmpty )
+ {
+ // We have no index we have to remove based on item
+ bool selectionChanged = false;
+
+ List cellsRangeToRemove = new List();
+ List itemsToUnselect = new List( cellRangeWithItems.ItemRangeWithItems.Items );
+ int count = itemsToUnselect.Count;
+
+ for( int i = count - 1; i >= 0; i-- )
+ {
+ object itemToUnselect = itemsToUnselect[ i ];
+
+ foreach( SelectionCellRangeWithItems existingSelectionCellRangeWithItems in m_cellsToSelect )
+ {
+ SelectionRange columnIntersection = columnRange.Intersect( existingSelectionCellRangeWithItems.ColumnRange );
+
+ if( columnIntersection.IsEmpty )
+ continue;
+
+ int index = Array.IndexOf( existingSelectionCellRangeWithItems.ItemRangeWithItems.Items, itemToUnselect );
+
+ if( index > -1 )
+ {
+ cellsRangeToRemove.Add(
+ new SelectionCellRangeWithItems(
+ new SelectionRange( existingSelectionCellRangeWithItems.ItemRange.GetIndexFromItemOffset( index ) ),
+ new object[] { itemToUnselect },
+ columnIntersection ) );
+ }
+ }
+ }
+
+ // Remove the currently unselected item from the new range to select
+ foreach( SelectionCellRangeWithItems cellRangeToRemove in cellsRangeToRemove )
+ {
+ selectionChanged |= m_cellsToSelect.Remove( cellRangeToRemove );
+ }
+
+ count = itemsToUnselect.Count;
+
+ for( int i = 0; i < count; i++ )
+ {
+ object itemToUnselect = itemsToUnselect[ i ];
+
+ foreach( SelectionCellRangeWithItems existingSelectionCellRangeWithItems in m_owner.SelectedCellsStore )
+ {
+ SelectionRange columnIntersection = columnRange.Intersect( existingSelectionCellRangeWithItems.ColumnRange );
+
+ if( columnIntersection.IsEmpty )
+ continue;
+
+ int index = Array.IndexOf( existingSelectionCellRangeWithItems.ItemRangeWithItems.Items, itemToUnselect );
+
+ if( index > -1 )
+ {
+ index = existingSelectionCellRangeWithItems.ItemRange.GetIndexFromItemOffset( index );
+
+ SelectionCellRange cellRangeTemp = new SelectionCellRange(
+ new SelectionRange( existingSelectionCellRangeWithItems.ItemRange.GetIndexFromItemOffset( index ) ),
+ columnIntersection );
+
+ if( !m_cellsToUnselect.Contains( cellRangeTemp ) )
+ {
+ selectionChanged = true;
+
+ m_cellsToUnselect.Add( new SelectionCellRangeWithItems(
+ cellRangeTemp.ItemRange, new object[] { itemToUnselect }, cellRangeTemp.ColumnRange ) );
+ }
+ }
+ }
+ }
+
+ return selectionChanged;
+ }
+
+ if( cellRangeWithItems.Length == 1 )
+ {
+ if( !m_cellsToSelect.Remove( cellRangeWithItems ) )
+ {
+ if( !m_owner.SelectedCellsStore.Contains( cellRange ) )
+ return false;
+
+ if( m_cellsToUnselect.Contains( cellRange ) )
+ return false;
+
+ m_cellsToUnselect.Add( cellRangeWithItems );
+ }
+
+ return true;
+ }
+ else
+ {
+ SelectedCellsStorage tempStorage = new SelectedCellsStorage( m_owner, 8 );
+ tempStorage.Add( cellRangeWithItems );
+
+ // Remove the currently selected item from the new range to select
+ foreach( SelectionCellRangeWithItems existingSelectionCellRangeWithItems in m_cellsToSelect )
+ {
+ tempStorage.Remove( existingSelectionCellRangeWithItems );
+ }
+
+ bool selectionChanged = m_cellsToSelect.Remove( cellRangeWithItems );
+
+ if( tempStorage.Count > 0 )
+ {
+ selectionChanged = true;
+
+ foreach( SelectionCellRangeWithItems cellRangeWithItemsToAdd in tempStorage )
+ {
+ Debug.Assert( !m_cellsToUnselect.Contains( cellRangeWithItemsToAdd.CellRange ) );
+ m_cellsToUnselect.Add( cellRangeWithItemsToAdd );
+ }
+ }
+
+ return selectionChanged;
+ }
+ }
+
+ public bool SelectJustThisItem( int itemIndex, object item )
+ {
+ bool selectionDone = true;
+ m_toDeferSelect.Clear();
+
+ SelectionRangeWithItems rangeWithItemsToSelect = new SelectionRangeWithItems( itemIndex, item );
+ SelectionRange range = rangeWithItemsToSelect.Range;
+
+ if( m_itemsToSelect.Contains( range ) )
+ selectionDone = false;
+
+ m_itemsToSelect.Clear();
+ SelectedItemsStorage selectedItemsInChange = m_owner.SelectedItemsStore;
+
+ if( selectedItemsInChange.Contains( range ) )
+ {
+ if( !m_itemsToUnselect.Contains( range ) )
+ selectionDone = false;
+
+ m_itemsToUnselect.Clear();
+
+ foreach( SelectionRangeWithItems selectedRangeWithItems in selectedItemsInChange )
+ {
+ m_itemsToUnselect.Add( selectedRangeWithItems );
+ }
+
+ m_itemsToUnselect.Remove( rangeWithItemsToSelect );
+ }
+ else
+ {
+ m_itemsToSelect.Add( rangeWithItemsToSelect );
+ m_itemsToUnselect.Clear();
+
+ foreach( SelectionRangeWithItems selectedRangeWithItems in selectedItemsInChange )
+ {
+ m_itemsToUnselect.Add( selectedRangeWithItems );
+ }
+ }
+
+ this.UnselectAllCells();
+ return selectionDone;
+ }
+
+ public bool SelectJustThisCell( int itemIndex, object item, int columnIndex )
+ {
+ bool selectionDone = true;
+ m_toDeferSelect.Clear();
+
+ SelectionCellRangeWithItems rangeWithItemsToSelect =
+ new SelectionCellRangeWithItems( itemIndex, item, columnIndex );
+
+ SelectionCellRange cellRange = rangeWithItemsToSelect.CellRange;
+
+ if( m_cellsToSelect.Contains( cellRange ) )
+ selectionDone = false;
+
+ m_cellsToSelect.Clear();
+ SelectedCellsStorage selectedCellsInChange = m_owner.SelectedCellsStore;
+
+ if( selectedCellsInChange.Contains( cellRange ) )
+ {
+ if( !m_cellsToUnselect.Contains( cellRange ) )
+ selectionDone = false;
+
+ m_cellsToUnselect.Clear();
+
+ foreach( SelectionCellRangeWithItems selectedCellRangeWithItems in selectedCellsInChange )
+ {
+ m_cellsToUnselect.Add( selectedCellRangeWithItems );
+ }
+
+ m_cellsToUnselect.Remove( rangeWithItemsToSelect );
+ }
+ else
+ {
+ m_cellsToSelect.Add( rangeWithItemsToSelect );
+ m_cellsToUnselect.Clear();
+
+ foreach( SelectionCellRangeWithItems selectedCellRangeWithItems in selectedCellsInChange )
+ {
+ m_cellsToUnselect.Add( selectedCellRangeWithItems );
+ }
+ }
+
+ this.UnselectAllItems();
+ return selectionDone;
+ }
+
+ public bool SelectItemCells(
+ int itemIndex,
+ object item,
+ HashedLinkedList columnsByVisiblePosition,
+ bool preserveSelection )
+ {
+ bool selectionDone = true;
+ int columnsCount = columnsByVisiblePosition.Count;
+ m_toDeferSelect.Clear();
+
+ SelectionCellRangeWithItems rangeWithItemsToSelect =
+ new SelectionCellRangeWithItems(
+ new SelectionRange( itemIndex ),
+ new object[] { item },
+ new SelectionRange( 0, columnsCount - 1 ) );
+
+ SelectionCellRange cellRange = rangeWithItemsToSelect.CellRange;
+
+ m_cellsToSelect.Clear();
+ SelectedCellsStorage selectedCellsInChange = m_owner.SelectedCellsStore;
+
+ // Remove all currently selected Cells from the new selectionRange
+ // to avoid duplicate SelectionCellRange
+ SelectedCellsStorage tempStorage = new SelectedCellsStorage( null, 1 );
+ tempStorage.Add( rangeWithItemsToSelect );
+
+ for( int i = 0; i < selectedCellsInChange.Count; i++ )
+ {
+ tempStorage.Remove( selectedCellsInChange[ i ] );
+ }
+
+ foreach( ColumnBase column in columnsByVisiblePosition )
+ {
+ if( !column.Visible )
+ tempStorage.Remove( new SelectionCellRangeWithItems( itemIndex, item, column.VisiblePosition ) );
+ }
+
+ int tempStorageCount = tempStorage.Count;
+
+ // All Cells are already selected
+ if( tempStorageCount == 0 )
+ {
+ if( !m_cellsToUnselect.Contains( cellRange ) )
+ selectionDone = false;
+
+ m_cellsToUnselect.Clear();
+
+ if( !preserveSelection )
+ {
+ foreach( SelectionCellRangeWithItems selectedCellRangeWithItems in selectedCellsInChange )
+ {
+ m_cellsToUnselect.Add( selectedCellRangeWithItems );
+ }
+ }
+
+ m_cellsToUnselect.Remove( rangeWithItemsToSelect );
+ }
+ else
+ {
+ // Add each range to selection
+ for( int i = 0; i < tempStorageCount; i++ )
+ {
+ m_cellsToSelect.Add( tempStorage[ i ] );
+ }
+
+ m_cellsToUnselect.Clear();
+
+ if( !preserveSelection )
+ {
+ foreach( SelectionCellRangeWithItems selectedCellRangeWithItems in selectedCellsInChange )
+ {
+ tempStorage = new SelectedCellsStorage( null, 1 );
+ tempStorage.Add( selectedCellRangeWithItems );
+ tempStorage.Remove( rangeWithItemsToSelect );
+ tempStorageCount = tempStorage.Count;
+
+ for( int i = 0; i < tempStorageCount; i++ )
+ {
+ m_cellsToUnselect.Add( tempStorage[ i ] );
+ }
+ }
+ }
+ }
+
+ if( !preserveSelection )
+ this.UnselectAllItems();
+
+ return selectionDone;
+ }
+
+ public void UnselectAllItems()
+ {
+ m_toDeferSelect.Clear();
+ m_itemsToSelect.Clear();
+ m_itemsToUnselect.Clear();
+
+ foreach( SelectionRangeWithItems selectedRangeWithItems in m_owner.SelectedItemsStore )
+ {
+ m_itemsToUnselect.Add( selectedRangeWithItems );
+ }
+ }
+
+ public void UnselectAllCells()
+ {
+ m_cellsToSelect.Clear();
+ m_cellsToUnselect.Clear();
+
+ foreach( SelectionCellRangeWithItems selectedCellRangeWithItems in m_owner.SelectedCellsStore )
+ {
+ m_cellsToUnselect.Add( selectedCellRangeWithItems );
+ }
+ }
+
+ public void Cleanup()
+ {
+ m_itemsToSelect.Clear();
+ m_itemsToUnselect.Clear();
+ m_cellsToSelect.Clear();
+ m_cellsToUnselect.Clear();
+ }
+
+ public void CleanupDeferSelection()
+ {
+ m_toDeferSelect.Clear();
+ }
+
+ public void UpdateSelectedItemsInChangeOfDataGridContext( bool itemsSourceChanged )
+ {
+ List removedRangeWithItems;
+ List unselectedItemsFromRemove = this.GetUnselectedItemsFromRemove( out removedRangeWithItems );
+ SelectedItemsStorage ownerSelectedItems = m_owner.SelectedItemsStore;
+ int count = m_sourceChanges.Count;
+
+ for( int i = 0; i < count; i++ )
+ {
+ SourceChangeInfo sourceChangeInfo = m_sourceChanges[ i ];
+
+ switch( sourceChangeInfo.Action )
+ {
+ case NotifyCollectionChangedAction.Add:
+ {
+ if( sourceChangeInfo.StartIndex != -1 )
+ {
+ ownerSelectedItems.OffsetIndex( sourceChangeInfo.StartIndex, sourceChangeInfo.Count );
+ }
+
+ break;
+ }
+
+ case NotifyCollectionChangedAction.Remove:
+ break;
+
+ default:
+ throw new NotSupportedException( "Only Add and Remove are supported." );
+ }
+ }
+
+ foreach( SelectionRangeWithItems rangeWithItems in removedRangeWithItems )
+ {
+ ownerSelectedItems.Remove( rangeWithItems );
+ ownerSelectedItems.OffsetIndex( rangeWithItems.Range.StartIndex, -rangeWithItems.Length );
+ }
+
+ count = m_toDeferSelect.Count;
+
+ for( int i = count - 1; i >= 0; i-- )
+ {
+ object item = m_toDeferSelect[ i ];
+ int itemIndex = m_owner.Items.IndexOf( item );
+
+ if( itemIndex != -1 )
+ {
+ if( !m_itemsToUnselect.Contains( itemIndex ) )
+ {
+ m_itemsToSelect.Add( new SelectionRangeWithItems( itemIndex, item ) );
+ }
+
+ m_toDeferSelect.RemoveAt( i );
+ }
+ }
+
+ if( ( m_itemsToUnselect.Count > 0 ) || ( m_itemsToSelect.Count > 0 ) || ( unselectedItemsFromRemove.Count > 0 ) )
+ {
+ Dictionary realizedDataRows = new Dictionary();
+
+ // Only want to update the realizedDataRows if the selection change is from user interaction and not
+ // from the source being updated. When the source is updated, the generator will recreate needed container,
+ // so the old one will have the correct selection state.
+ foreach( DependencyObject container in m_owner.CustomItemContainerGenerator.RealizedContainers )
+ {
+ DataRow dataRow = container as DataRow;
+
+ if( ( dataRow != null ) && ( DataGridControl.GetDataGridContext( dataRow ) == m_owner ) )
+ {
+ realizedDataRows[ DataGridVirtualizingPanel.GetItemIndex( dataRow ) ] = dataRow;
+ }
+ }
+
+ bool sourceItemIsDataRow = false;
+
+ foreach( SelectionRangeWithItems rangeWithItems in m_itemsToUnselect )
+ {
+ sourceItemIsDataRow |= this.SetIsSelectedOnDataRow( realizedDataRows, rangeWithItems, false );
+ ownerSelectedItems.Remove( rangeWithItems );
+ }
+
+ foreach( SelectionRangeWithItems rangeWithItems in m_itemsToSelect )
+ {
+ sourceItemIsDataRow |= this.SetIsSelectedOnDataRow( realizedDataRows, rangeWithItems, true );
+ ownerSelectedItems.Add( rangeWithItems );
+ }
+
+ foreach( SelectionRangeWithItems rangeWithItems in unselectedItemsFromRemove )
+ {
+ m_itemsToUnselect.Add( rangeWithItems );
+ }
+
+ if( !sourceItemIsDataRow )
+ {
+ foreach( KeyValuePair realizedItemPair in realizedDataRows )
+ {
+ if( ownerSelectedItems.Contains( new SelectionRange( realizedItemPair.Key ) ) )
+ {
+ realizedItemPair.Value.SetIsSelected( true );
+ }
+ else
+ {
+ realizedItemPair.Value.SetIsSelected( false );
+ }
+ }
+ }
+ }
+ }
+
+ public void UpdateSelectedCellsInChangeOfDataGridContext( bool itemsSourceChanged )
+ {
+ List removedCellsRangeWithItems;
+ List unselectedCellsFromRemove = this.GetUnselectedCellsFromRemove( out removedCellsRangeWithItems );
+ SelectedCellsStorage ownerSelectedCells = m_owner.SelectedCellsStore;
+ int count = m_sourceChanges.Count;
+
+ for( int i = 0; i < count; i++ )
+ {
+ SourceChangeInfo sourceChangeInfo = m_sourceChanges[ i ];
+
+ switch( sourceChangeInfo.Action )
+ {
+ case NotifyCollectionChangedAction.Add:
+ {
+ if( sourceChangeInfo.StartIndex != -1 )
+ {
+ ownerSelectedCells.OffsetIndex( sourceChangeInfo.StartIndex, sourceChangeInfo.Count );
+ }
+
+ break;
+ }
+
+ case NotifyCollectionChangedAction.Remove:
+ break;
+
+ default:
+ throw new NotSupportedException( "Only Add and Remove are supported." );
+ }
+ }
+
+ foreach( SelectionCellRangeWithItems cellRangeWithItems in removedCellsRangeWithItems )
+ {
+ ownerSelectedCells.Remove( cellRangeWithItems );
+ ownerSelectedCells.OffsetIndex( cellRangeWithItems.ItemRange.StartIndex, -cellRangeWithItems.ItemRange.Length );
+ }
+
+ if( ( m_cellsToUnselect.Count > 0 ) || ( m_cellsToSelect.Count > 0 ) || ( unselectedCellsFromRemove.Count > 0 ) )
+ {
+ Dictionary realizedDataRows = new Dictionary | | | | | | |