├── .gitignore
├── LICENSE.htm
├── README.md
└── TabControl
├── TabControl
├── Converters.cs
├── Helper.cs
├── Images
│ └── newtab.ico
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── TabControl.cs
├── TabEventArgs.cs
├── TabItem.cs
├── TabPanel.cs
├── Themes
│ └── Generic.xaml
└── Wpf.TabControl.csproj
├── Test
├── App.xaml
├── App.xaml.cs
├── Images
│ └── ie.ico
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── Settings.Designer.cs
│ └── Settings.settings
├── Test.csproj
├── Window1.xaml
├── Window1.xaml.cs
├── WindowUsingItemsProperty.xaml
├── WindowUsingItemsProperty.xaml.cs
├── WindowUsingItemsSourceProperty.xaml
└── WindowUsingItemsSourceProperty.xaml.cs
└── Wpf.TabControl.sln
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 | [Dd]ebug/
11 | [Dd]ebugPublic/
12 | [Rr]elease/
13 | [Rr]eleases/
14 | x64/
15 | x86/
16 | build/
17 | bld/
18 | [Bb]in/
19 | [Oo]bj/
20 |
21 | # Roslyn cache directories
22 | *.ide/
23 |
24 | # MSTest test Results
25 | [Tt]est[Rr]esult*/
26 | [Bb]uild[Ll]og.*
27 |
28 | #NUNIT
29 | *.VisualState.xml
30 | TestResult.xml
31 |
32 | # Build Results of an ATL Project
33 | [Dd]ebugPS/
34 | [Rr]eleasePS/
35 | dlldata.c
36 |
37 | *_i.c
38 | *_p.c
39 | *_i.h
40 | *.ilk
41 | *.meta
42 | *.obj
43 | *.pch
44 | *.pdb
45 | *.pgc
46 | *.pgd
47 | *.rsp
48 | *.sbr
49 | *.tlb
50 | *.tli
51 | *.tlh
52 | *.tmp
53 | *.tmp_proj
54 | *.log
55 | *.vspscc
56 | *.vssscc
57 | .builds
58 | *.pidb
59 | *.svclog
60 | *.scc
61 |
62 | # Chutzpah Test files
63 | _Chutzpah*
64 |
65 | # Visual C++ cache files
66 | ipch/
67 | *.aps
68 | *.ncb
69 | *.opensdf
70 | *.sdf
71 | *.cachefile
72 |
73 | # Visual Studio profiler
74 | *.psess
75 | *.vsp
76 | *.vspx
77 |
78 | # TFS 2012 Local Workspace
79 | $tf/
80 |
81 | # Guidance Automation Toolkit
82 | *.gpState
83 |
84 | # ReSharper is a .NET coding add-in
85 | _ReSharper*/
86 | *.[Rr]e[Ss]harper
87 | *.DotSettings.user
88 |
89 | # JustCode is a .NET coding addin-in
90 | .JustCode
91 |
92 | # TeamCity is a build add-in
93 | _TeamCity*
94 |
95 | # DotCover is a Code Coverage Tool
96 | *.dotCover
97 |
98 | # NCrunch
99 | _NCrunch_*
100 | .*crunch*.local.xml
101 |
102 | # MightyMoose
103 | *.mm.*
104 | AutoTest.Net/
105 |
106 | # Web workbench (sass)
107 | .sass-cache/
108 |
109 | # Installshield output folder
110 | [Ee]xpress/
111 |
112 | # DocProject is a documentation generator add-in
113 | DocProject/buildhelp/
114 | DocProject/Help/*.HxT
115 | DocProject/Help/*.HxC
116 | DocProject/Help/*.hhc
117 | DocProject/Help/*.hhk
118 | DocProject/Help/*.hhp
119 | DocProject/Help/Html2
120 | DocProject/Help/html
121 |
122 | # Click-Once directory
123 | publish/
124 |
125 | # Publish Web Output
126 | *.[Pp]ublish.xml
127 | *.azurePubxml
128 | # TODO: Comment the next line if you want to checkin your web deploy settings
129 | # but database connection strings (with potential passwords) will be unencrypted
130 | *.pubxml
131 | *.publishproj
132 |
133 | # NuGet Packages
134 | *.nupkg
135 | # The packages folder can be ignored because of Package Restore
136 | **/packages/*
137 | # except build/, which is used as an MSBuild target.
138 | !**/packages/build/
139 | # If using the old MSBuild-Integrated Package Restore, uncomment this:
140 | #!**/packages/repositories.config
141 |
142 | # Windows Azure Build Output
143 | csx/
144 | *.build.csdef
145 |
146 | # Windows Store app package directory
147 | AppPackages/
148 |
149 | # Others
150 | sql/
151 | *.Cache
152 | ClientBin/
153 | [Ss]tyle[Cc]op.*
154 | ~$*
155 | *~
156 | *.dbmdl
157 | *.dbproj.schemaview
158 | *.pfx
159 | *.publishsettings
160 | node_modules/
161 |
162 | # RIA/Silverlight projects
163 | Generated_Code/
164 |
165 | # Backup & report files from converting an old project file
166 | # to a newer Visual Studio version. Backup files are not needed,
167 | # because we have git ;-)
168 | _UpgradeReport_Files/
169 | Backup*/
170 | UpgradeLog*.XML
171 | UpgradeLog*.htm
172 |
173 | # SQL Server files
174 | *.mdf
175 | *.ldf
176 |
177 | # Business Intelligence projects
178 | *.rdl.data
179 | *.bim.layout
180 | *.bim_*.settings
181 |
182 | # Microsoft Fakes
183 | FakesAssemblies/
184 |
--------------------------------------------------------------------------------
/LICENSE.htm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FluentNotepad/TabControl/438271851a4bfbc0a57f8b16ba2212b0ceb59f2e/LICENSE.htm
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Tab Control
2 | ===========
3 | 
4 |
5 | An Extended **WPF TabControl**
6 |
7 | License
8 | -------
9 | The original _TabControl_ was created by [@alrh](http://www.codeproject.com/Articles/20860/An-Extended-WPF-TabControl) and was released under **The Code Project Open License (CPOL)**.
10 |
11 | This version, used in _FluentNotepad_, is maintained by [Salvatore Gentile](https://twitter.com/_sgentile).
12 |
--------------------------------------------------------------------------------
/TabControl/TabControl/Converters.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.Text;
5 | using System.Windows;
6 | using System.Windows.Controls;
7 | using System.Windows.Data;
8 |
9 | namespace Wpf.Controls
10 | {
11 | class InverseBooleanConverter : IValueConverter
12 | {
13 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
14 | {
15 | bool flag = false;
16 | if (value is bool)
17 | {
18 | flag = (bool)value;
19 | }
20 | else if (value is bool?)
21 | {
22 | bool? nullable = (bool?)value;
23 | flag = nullable.Value;
24 | }
25 | return (flag ? Visibility.Collapsed : Visibility.Visible);
26 |
27 | }
28 |
29 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
30 | {
31 | return ((value is Visibility) && (((Visibility)value) == Visibility.Collapsed));
32 |
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/TabControl/TabControl/Helper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Media;
4 | using System.Windows.Markup;
5 | using System.Xml;
6 | using System.IO;
7 |
8 | namespace Wpf.Controls
9 | {
10 | class Dimension
11 | {
12 | public double Height;
13 | public double MaxHeight = double.PositiveInfinity;
14 | public double MinHeight;
15 | public double Width;
16 | public double MaxWidth = double.PositiveInfinity;
17 | public double MinWidth;
18 | }
19 |
20 | class Helper
21 | {
22 | ///
23 | /// Find a specific parent object type in the visual tree
24 | ///
25 | public static T FindParentControl(DependencyObject outerDepObj) where T : DependencyObject
26 | {
27 | DependencyObject dObj = VisualTreeHelper.GetParent(outerDepObj);
28 | if (dObj == null)
29 | return null;
30 |
31 | if (dObj is T)
32 | return dObj as T;
33 |
34 | while ((dObj = VisualTreeHelper.GetParent(dObj)) != null)
35 | {
36 | if (dObj is T)
37 | return dObj as T;
38 | }
39 |
40 | return null;
41 | }
42 |
43 | ///
44 | /// Find the Panel for the TabControl
45 | ///
46 | public static TabPanel FindVirtualizingTabPanel(Visual visual)
47 | {
48 | if (visual == null)
49 | return null;
50 |
51 | for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
52 | {
53 | Visual child = VisualTreeHelper.GetChild(visual, i) as Visual;
54 |
55 | if (child != null)
56 | {
57 | if (child is TabPanel)
58 | {
59 | object temp = child;
60 | return (TabPanel)temp;
61 | }
62 |
63 | TabPanel panel = FindVirtualizingTabPanel(child);
64 | if (panel != null)
65 | {
66 | object temp = panel;
67 | return (TabPanel)temp; // return the panel up the call stack
68 | }
69 | }
70 | }
71 | return null;
72 | }
73 |
74 | ///
75 | /// Clone an element
76 | ///
77 | ///
78 | ///
79 | public static object CloneElement(object elementToClone)
80 | {
81 | string xaml = XamlWriter.Save(elementToClone);
82 | return XamlReader.Load(new XmlTextReader(new StringReader(xaml)));
83 | }
84 |
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/TabControl/TabControl/Images/newtab.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FluentNotepad/TabControl/438271851a4bfbc0a57f8b16ba2212b0ceb59f2e/TabControl/TabControl/Images/newtab.ico
--------------------------------------------------------------------------------
/TabControl/TabControl/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("Sid4.TabControl")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("Sid4.TabControl")]
15 | [assembly: AssemblyCopyright("Copyright © 2007")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/TabControl/TabControl/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:2.0.50727.1378
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Wpf.Controls.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Wpf.Controls.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/TabControl/TabControl/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/TabControl/TabControl/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:2.0.50727.1378
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Wpf.Controls.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/TabControl/TabControl/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/TabControl/TabControl/TabControl.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Specialized;
4 | using System.ComponentModel;
5 | using System.Windows;
6 | using System.Windows.Controls;
7 | using System.Windows.Controls.Primitives;
8 | using System.Windows.Data;
9 | using System.Windows.Media;
10 | using System.Windows.Input;
11 |
12 | namespace Wpf.Controls
13 | {
14 | [TemplatePart(Name = "PART_DropDown", Type = typeof(ToggleButton))]
15 | [TemplatePart(Name = "PART_RepeatLeft", Type = typeof(RepeatButton))]
16 | [TemplatePart(Name = "PART_RepeatRight", Type = typeof(RepeatButton))]
17 | [TemplatePart(Name = "PART_NewTabButton", Type = typeof(ButtonBase))]
18 | [TemplatePart(Name = "PART_ScrollViewer", Type = typeof(ScrollViewer))]
19 | public class TabControl : System.Windows.Controls.TabControl
20 | {
21 | // public Events
22 | #region Events
23 |
24 | public event EventHandler TabItemAdding;
25 | public event EventHandler TabItemAdded;
26 | public event EventHandler TabItemClosing;
27 | public event EventHandler TabItemClosed;
28 | public event EventHandler NewTabItem;
29 |
30 | #endregion
31 |
32 | // TemplatePart controls
33 | private ToggleButton _toggleButton;
34 | private ButtonBase _addNewButton;
35 |
36 | static TabControl()
37 | {
38 | DefaultStyleKeyProperty.OverrideMetadata(typeof(TabControl), new FrameworkPropertyMetadata(typeof(TabControl)));
39 | TabStripPlacementProperty.AddOwner(typeof(TabControl), new FrameworkPropertyMetadata(Dock.Top, new PropertyChangedCallback(OnTabStripPlacementChanged)));
40 | }
41 |
42 | public TabControl()
43 | {
44 | Loaded +=
45 | delegate
46 | {
47 | SetAddNewButtonVisibility();
48 | SetTabItemsCloseButtonVisibility();
49 | IsUsingItemsSource = BindingOperations.IsDataBound(this, ItemsSourceProperty);
50 |
51 | if (IsUsingItemsSource && IsFixedSize)
52 | AllowAddNew = AllowDelete = false;
53 | };
54 | }
55 |
56 | #region Properties
57 |
58 | private bool IsFixedSize
59 | {
60 | get
61 | {
62 | IEnumerable items = GetItems();
63 | return items as IList == null || (items as IList).IsFixedSize;
64 | }
65 | }
66 |
67 | #endregion
68 |
69 | #region Dependancy properties
70 |
71 | public bool IsUsingItemsSource
72 | {
73 | get { return (bool)GetValue(IsUsingItemsSourceProperty); }
74 | private set { SetValue(IsUsingItemsSourcePropertyKey, value); }
75 | }
76 |
77 | public static readonly DependencyPropertyKey IsUsingItemsSourcePropertyKey =
78 | DependencyProperty.RegisterReadOnly("IsUsingItemsSource", typeof(bool), typeof(TabControl), new UIPropertyMetadata(false));
79 |
80 | public static readonly DependencyProperty IsUsingItemsSourceProperty = IsUsingItemsSourcePropertyKey.DependencyProperty;
81 |
82 | #region Brushes
83 |
84 | public Brush TabItemNormalBackground
85 | {
86 | get { return (Brush)GetValue(TabItemNormalBackgroundProperty); }
87 | set { SetValue(TabItemNormalBackgroundProperty, value); }
88 | }
89 | public static readonly DependencyProperty TabItemNormalBackgroundProperty = DependencyProperty.Register("TabItemNormalBackground", typeof(Brush), typeof(TabControl), new UIPropertyMetadata(null));
90 |
91 | public Brush TabItemMouseOverBackground
92 | {
93 | get { return (Brush)GetValue(TabItemMouseOverBackgroundProperty); }
94 | set { SetValue(TabItemMouseOverBackgroundProperty, value); }
95 | }
96 | public static readonly DependencyProperty TabItemMouseOverBackgroundProperty = DependencyProperty.Register("TabItemMouseOverBackground", typeof(Brush), typeof(TabControl), new UIPropertyMetadata(null));
97 |
98 | public Brush TabItemSelectedBackground
99 | {
100 | get { return (Brush)GetValue(TabItemSelectedBackgroundProperty); }
101 | set { SetValue(TabItemSelectedBackgroundProperty, value); }
102 | }
103 | public static readonly DependencyProperty TabItemSelectedBackgroundProperty = DependencyProperty.Register("TabItemSelectedBackground", typeof(Brush), typeof(TabControl), new UIPropertyMetadata(null));
104 |
105 |
106 |
107 |
108 | #endregion
109 |
110 | /*
111 | * Based on the whether the ControlTemplate implements the NewTab button and Close Buttons determines the functionality of the AllowAddNew & AllowDelete properties
112 | * If they are in the control template, then the visibility of the AddNew & Header buttons are bound to these properties
113 | *
114 | */
115 | ///
116 | /// Allow the User to Add New TabItems
117 | ///
118 | public bool AllowAddNew
119 | {
120 | get { return (bool)GetValue(AllowAddNewProperty); }
121 | set { SetValue(AllowAddNewProperty, value); }
122 | }
123 | public static readonly DependencyProperty AllowAddNewProperty = DependencyProperty.Register("AllowAddNew", typeof(bool), typeof(TabControl),
124 | new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnAllowAddNewChanged), OnCoerceAllowAddNewCallback));
125 |
126 | private static void OnAllowAddNewChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
127 | {
128 | ((TabControl)d).SetAddNewButtonVisibility();
129 | }
130 |
131 | private static object OnCoerceAllowAddNewCallback(DependencyObject d, object basevalue)
132 | {
133 | return ((TabControl)d).OnCoerceAllowAddNewCallback(basevalue);
134 | }
135 |
136 | private object OnCoerceAllowAddNewCallback(object basevalue)
137 | {
138 | if (ItemsSource != null)
139 | {
140 | IList list = ItemsSource as IList;
141 | if (list != null)
142 | {
143 | if (list.IsFixedSize)
144 | return false;
145 | return basevalue;
146 | }
147 | return false;
148 | }
149 | return basevalue;
150 | }
151 |
152 | ///
153 | /// Allow the User to Delete TabItems
154 | ///
155 | public bool AllowDelete
156 | {
157 | get { return (bool)GetValue(AllowDeleteProperty); }
158 | set { SetValue(AllowDeleteProperty, value); }
159 | }
160 | public static readonly DependencyProperty AllowDeleteProperty = DependencyProperty.Register("AllowDelete", typeof(bool), typeof(TabControl),
161 | new FrameworkPropertyMetadata(true, new PropertyChangedCallback(OnAllowDeleteChanged), OnCoerceAllowDeleteNewCallback));
162 |
163 | private static void OnAllowDeleteChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
164 | {
165 | TabControl tc = (TabControl)d;
166 | tc.SetTabItemsCloseButtonVisibility();
167 | }
168 |
169 | private static object OnCoerceAllowDeleteNewCallback(DependencyObject d, object basevalue)
170 | {
171 | return ((TabControl)d).OnCoerceAllowDeleteCallback(basevalue);
172 | }
173 | private object OnCoerceAllowDeleteCallback(object basevalue)
174 | {
175 | if (ItemsSource != null)
176 | {
177 | IList list = ItemsSource as IList;
178 | if (list != null)
179 | {
180 | if (list.IsFixedSize)
181 | return false;
182 | return basevalue;
183 | }
184 | return false;
185 | }
186 | return basevalue;
187 | }
188 |
189 | ///
190 | /// Set new Header as the current selection
191 | ///
192 | public bool SelectNewTabOnCreate
193 | {
194 | get { return (bool)GetValue(SelectNewTabOnCreateProperty); }
195 | set { SetValue(SelectNewTabOnCreateProperty, value); }
196 | }
197 | public static readonly DependencyProperty SelectNewTabOnCreateProperty = DependencyProperty.Register("SelectNewTabOnCreate", typeof(bool), typeof(TabControl), new UIPropertyMetadata(true));
198 |
199 |
200 | ///
201 | /// Determines where new TabItems are added to the TabControl
202 | ///
203 | ///
204 | /// Set to true (default) to add all new Tabs to the end of the TabControl
205 | /// Set to False to insert new tabs after the current selection
206 | ///
207 | public bool AddNewTabToEnd
208 | {
209 | get { return (bool)GetValue(AddNewTabToEndProperty); }
210 | set { SetValue(AddNewTabToEndProperty, value); }
211 | }
212 | public static readonly DependencyProperty AddNewTabToEndProperty = DependencyProperty.Register("AddNewTabToEnd", typeof(bool), typeof(TabControl), new UIPropertyMetadata(true));
213 |
214 | ///
215 | /// defines the Minimum width of a Header
216 | ///
217 | [DefaultValue(20.0)]
218 | [Category("Layout")]
219 | [Description("Gets or Sets the minimum Width Constraint shared by all Items in the Control, individual child elements MinWidth property will overide this property")]
220 | public double TabItemMinWidth
221 | {
222 | get { return (double)GetValue(TabItemMinWidthProperty); }
223 | set { SetValue(TabItemMinWidthProperty, value); }
224 | }
225 | public static readonly DependencyProperty TabItemMinWidthProperty = DependencyProperty.Register("TabItemMinWidth", typeof(double), typeof(TabControl),
226 | new FrameworkPropertyMetadata(20.0, new PropertyChangedCallback(OnMinMaxChanged), CoerceMinWidth));
227 |
228 | private static object CoerceMinWidth(DependencyObject d, object value)
229 | {
230 | TabControl tc = (TabControl)d;
231 | double newValue = (double)value;
232 |
233 | if (newValue > tc.TabItemMaxWidth)
234 | return tc.TabItemMaxWidth;
235 |
236 | return (newValue > 0 ? newValue : 0);
237 | }
238 |
239 | ///
240 | /// defines the Minimum height of a Header
241 | ///
242 | [DefaultValue(20.0)]
243 | [Category("Layout")]
244 | [Description("Gets or Sets the minimum Height Constraint shared by all Items in the Control, individual child elements MinHeight property will override this value")]
245 | public double TabItemMinHeight
246 | {
247 | get { return (double)GetValue(TabItemMinHeightProperty); }
248 | set { SetValue(TabItemMinHeightProperty, value); }
249 | }
250 | public static readonly DependencyProperty TabItemMinHeightProperty = DependencyProperty.Register("TabItemMinHeight", typeof(double), typeof(TabControl),
251 | new FrameworkPropertyMetadata(20.0, new PropertyChangedCallback(OnMinMaxChanged), CoerceMinHeight));
252 |
253 | private static object CoerceMinHeight(DependencyObject d, object value)
254 | {
255 | TabControl tc = (TabControl)d;
256 | double newValue = (double)value;
257 |
258 | if (newValue > tc.TabItemMaxHeight)
259 | return tc.TabItemMaxHeight;
260 |
261 | return (newValue > 0 ? newValue : 0);
262 | }
263 |
264 | ///
265 | /// defines the Maximum width of a Header
266 | ///
267 | [DefaultValue(double.PositiveInfinity)]
268 | [Category("Layout")]
269 | [Description("Gets or Sets the maximum width Constraint shared by all Items in the Control, individual child elements MaxWidth property will override this value")]
270 | public double TabItemMaxWidth
271 | {
272 | get { return (double)GetValue(TabItemMaxWidthProperty); }
273 | set { SetValue(TabItemMaxWidthProperty, value); }
274 | }
275 | public static readonly DependencyProperty TabItemMaxWidthProperty = DependencyProperty.Register("TabItemMaxWidth", typeof(double), typeof(TabControl),
276 | new FrameworkPropertyMetadata(double.PositiveInfinity, new PropertyChangedCallback(OnMinMaxChanged), CoerceMaxWidth));
277 |
278 | private static object CoerceMaxWidth(DependencyObject d, object value)
279 | {
280 | TabControl tc = (TabControl)d;
281 | double newValue = (double)value;
282 |
283 | if (newValue < tc.TabItemMinWidth)
284 | return tc.TabItemMinWidth;
285 |
286 | return newValue;
287 | }
288 |
289 | ///
290 | /// defines the Maximum width of a Header
291 | ///
292 | [DefaultValue(double.PositiveInfinity)]
293 | [Category("Layout")]
294 | [Description("Gets or Sets the maximum height Constraint shared by all Items in the Control, individual child elements MaxHeight property will override this value")]
295 | public double TabItemMaxHeight
296 | {
297 | get { return (double)GetValue(TabItemMaxHeightProperty); }
298 | set { SetValue(TabItemMaxHeightProperty, value); }
299 | }
300 | public static readonly DependencyProperty TabItemMaxHeightProperty = DependencyProperty.Register("TabItemMaxHeight", typeof(double), typeof(TabControl),
301 | new FrameworkPropertyMetadata(double.PositiveInfinity, new PropertyChangedCallback(OnMinMaxChanged), CoerceMaxHeight));
302 |
303 | private static object CoerceMaxHeight(DependencyObject d, object value)
304 | {
305 | TabControl tc = (TabControl)d;
306 | double newValue = (double)value;
307 |
308 | if (newValue < tc.TabItemMinHeight)
309 | return tc.TabItemMinHeight;
310 |
311 | return newValue;
312 | }
313 |
314 | ///
315 | /// OnMinMaxChanged callback responds to any of the Min/Max dependancy properties changing
316 | ///
317 | ///
318 | ///
319 | private static void OnMinMaxChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
320 | {
321 | TabControl tc = (TabControl)d;
322 | if (tc.Template == null) return;
323 |
324 | foreach (TabItem child in tc.InternalChildren())
325 | {
326 | if (child != null)
327 | child.Dimension = null;
328 | }
329 |
330 |
331 | // var tabsCount = tc.GetTabsCount();
332 | // for (int i = 0; i < tabsCount; i++)
333 | // {
334 | // Header ti = tc.GetTabItem(i);
335 | // if (ti != null)
336 | // ti.Dimension = null;
337 | // }
338 |
339 | TabPanel tp = Helper.FindVirtualizingTabPanel(tc);
340 | if (tp != null)
341 | tp.InvalidateMeasure();
342 | }
343 |
344 | ///
345 | /// OnTabStripPlacementChanged property callback
346 | ///
347 | ///
348 | /// We need to supplement the base implementation with this method as the base method does not work when
349 | /// we are using virtualization in the tabpanel, it only updates visible items
350 | ///
351 | private static void OnTabStripPlacementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
352 | {
353 | TabControl tc = (TabControl)d;
354 |
355 | foreach (TabItem tabItem in tc.InternalChildren())
356 | {
357 | if (tabItem != null)
358 | {
359 | tabItem.Dimension = null;
360 | tabItem.CoerceValue(System.Windows.Controls.TabItem.TabStripPlacementProperty);
361 | }
362 | }
363 | }
364 |
365 | #endregion
366 |
367 | /*
368 | * Protected override methods
369 | *
370 | */
371 |
372 | #region Overrides
373 |
374 | ///
375 | /// OnApplyTemplate override
376 | ///
377 | public override void OnApplyTemplate()
378 | {
379 | base.OnApplyTemplate();
380 |
381 | // set up the event handler for the template parts
382 | _toggleButton = this.Template.FindName("PART_DropDown", this) as ToggleButton;
383 | if (_toggleButton != null)
384 | {
385 | // create a context menu for the togglebutton
386 | ContextMenu cm = new ContextMenu { PlacementTarget = _toggleButton, Placement = PlacementMode.Bottom };
387 |
388 | // create a binding between the togglebutton's IsChecked Property
389 | // and the Context Menu's IsOpen Property
390 | Binding b = new Binding
391 | {
392 | Source = _toggleButton,
393 | Mode = BindingMode.TwoWay,
394 | Path = new PropertyPath(ToggleButton.IsCheckedProperty)
395 | };
396 |
397 | cm.SetBinding(ContextMenu.IsOpenProperty, b);
398 |
399 | _toggleButton.ContextMenu = cm;
400 | _toggleButton.Checked += DropdownButton_Checked;
401 | }
402 |
403 | ScrollViewer scrollViewer = this.Template.FindName("PART_ScrollViewer", this) as ScrollViewer;
404 |
405 | // set up event handlers for the RepeatButtons Click event
406 | RepeatButton repeatLeft = this.Template.FindName("PART_RepeatLeft", this) as RepeatButton;
407 | if (repeatLeft != null)
408 | {
409 | repeatLeft.Click += delegate
410 | {
411 | if (scrollViewer != null)
412 | scrollViewer.LineLeft();
413 | };
414 | }
415 |
416 | RepeatButton repeatRight = this.Template.FindName("PART_RepeatRight", this) as RepeatButton;
417 | if (repeatRight != null)
418 | {
419 | repeatRight.Click += delegate
420 | {
421 | if (scrollViewer != null)
422 | scrollViewer.LineRight();
423 | };
424 | }
425 |
426 | // set up the event handler for the 'New Tab' Button Click event
427 | _addNewButton = this.Template.FindName("PART_NewTabButton", this) as ButtonBase;
428 | if (_addNewButton != null)
429 | _addNewButton.Click += ((sender, routedEventArgs) => AddTabItem());
430 | }
431 |
432 | ///
433 | /// IsItemItsOwnContainerOverride
434 | ///
435 | ///
436 | ///
437 | protected override bool IsItemItsOwnContainerOverride(object item)
438 | {
439 | return item is TabItem;
440 | }
441 | ///
442 | /// GetContainerForItemOverride
443 | ///
444 | ///
445 | protected override DependencyObject GetContainerForItemOverride()
446 | {
447 | return new TabItem();
448 | }
449 |
450 |
451 | protected override void OnPreviewKeyDown(KeyEventArgs e)
452 | {
453 | var tabsCount = GetTabsCount();
454 | if (tabsCount == 0)
455 | return;
456 |
457 | TabItem ti = null;
458 |
459 | switch (e.Key)
460 | {
461 | case Key.Home:
462 | ti = GetTabItem(0);
463 | break;
464 |
465 | case Key.End:
466 | ti = GetTabItem(tabsCount - 1);
467 | break;
468 |
469 | case Key.Tab:
470 | if (e.KeyboardDevice.Modifiers == ModifierKeys.Control)
471 | {
472 | var index = SelectedIndex;
473 | var direction = e.KeyboardDevice.Modifiers == ModifierKeys.Shift ? -1 : 1;
474 |
475 | while (true)
476 | {
477 | index += direction;
478 | if (index < 0)
479 | index = tabsCount - 1;
480 | else if (index > tabsCount - 1)
481 | index = 0;
482 |
483 | FrameworkElement ui = GetTabItem(index);
484 | if (ui != null)
485 | {
486 | if (ui.Visibility == Visibility.Visible && ui.IsEnabled)
487 | {
488 | ti = GetTabItem(index);
489 | break;
490 | }
491 | }
492 | }
493 | }
494 | break;
495 | }
496 |
497 | TabPanel panel = Helper.FindVirtualizingTabPanel(this);
498 | if (panel != null && ti != null)
499 | {
500 | panel.MakeVisible(ti, Rect.Empty);
501 | SelectedItem = ti;
502 |
503 | e.Handled = ti.Focus();
504 | }
505 | base.OnPreviewKeyDown(e);
506 | }
507 |
508 | protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
509 | {
510 | base.OnItemsChanged(e);
511 |
512 | if (e.Action == NotifyCollectionChangedAction.Add && SelectNewTabOnCreate)
513 | {
514 | TabItem tabItem = (TabItem)this.ItemContainerGenerator.ContainerFromItem(e.NewItems[e.NewItems.Count - 1]);
515 | SelectedItem = tabItem;
516 |
517 | TabPanel itemsHost = Helper.FindVirtualizingTabPanel(this);
518 | if (itemsHost != null)
519 | itemsHost.MakeVisible(tabItem, Rect.Empty);
520 |
521 | tabItem.Focus();
522 | }
523 | }
524 |
525 | protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
526 | {
527 | base.OnItemsSourceChanged(oldValue, newValue);
528 |
529 | IsUsingItemsSource = newValue != null;
530 | if (IsFixedSize)
531 | AllowAddNew = AllowDelete = false;
532 |
533 | SetAddNewButtonVisibility();
534 | SetTabItemsCloseButtonVisibility();
535 | }
536 |
537 | #endregion
538 |
539 | ///
540 | /// Handle the ToggleButton Checked event that displays a context menu of Header Headers
541 | ///
542 | ///
543 | ///
544 | void DropdownButton_Checked(object sender, RoutedEventArgs e)
545 | {
546 | if (_toggleButton == null) return;
547 |
548 | _toggleButton.ContextMenu.Items.Clear();
549 | _toggleButton.ContextMenu.Placement = TabStripPlacement == Dock.Bottom ? PlacementMode.Top : PlacementMode.Bottom;
550 |
551 | int index = 0;
552 | foreach (TabItem tabItem in this.InternalChildren())
553 | {
554 | if (tabItem != null)
555 | {
556 | var header = Helper.CloneElement(tabItem.Header);
557 | var icon = tabItem.Icon == null ? null : Helper.CloneElement(tabItem.Icon);
558 |
559 | var mi = new MenuItem { Header = header, Icon = icon, Tag = index++.ToString() };
560 | mi.Click += ContextMenuItem_Click;
561 |
562 | _toggleButton.ContextMenu.Items.Add(mi);
563 | }
564 | }
565 | }
566 |
567 | ///
568 | /// Handle the MenuItem's Click event
569 | ///
570 | ///
571 | ///
572 | void ContextMenuItem_Click(object sender, RoutedEventArgs e)
573 | {
574 | MenuItem mi = sender as MenuItem;
575 | if (mi == null) return;
576 |
577 | int index;
578 | // get the index of the Header from the manuitems Tag property
579 | bool b = int.TryParse(mi.Tag.ToString(), out index);
580 |
581 | if (b)
582 | {
583 | TabItem tabItem = GetTabItem(index);
584 | if (tabItem != null)
585 | {
586 | TabPanel itemsHost = Helper.FindVirtualizingTabPanel(this);
587 | if (itemsHost != null)
588 | itemsHost.MakeVisible(tabItem, Rect.Empty);
589 |
590 | tabItem.Focus();
591 | }
592 | }
593 | }
594 |
595 | ///
596 | /// Add a new Header
597 | ///
598 | public void AddTabItem()
599 | {
600 | if (IsFixedSize)
601 | throw new InvalidOperationException("ItemsSource is Fixed Size");
602 |
603 | int i = this.SelectedIndex;
604 |
605 | // give an opertunity to cancel the adding of the tabitem
606 | CancelEventArgs c = new CancelEventArgs();
607 | if (TabItemAdding != null)
608 | TabItemAdding(this, c);
609 |
610 | if (c.Cancel)
611 | return;
612 |
613 | TabItem tabItem;
614 |
615 | // Using ItemsSource property
616 | if (ItemsSource != null)
617 | {
618 | IList list = (IList)ItemsSource;
619 | NewTabItemEventArgs n = new NewTabItemEventArgs();
620 | if (NewTabItem == null)
621 | throw new InvalidOperationException("You must implement the NewTabItem event to supply the item to be added to the tab control.");
622 |
623 | NewTabItem(this, n);
624 | if (n.Content == null)
625 | return;
626 |
627 | if (i == -1 || i == list.Count - 1 || AddNewTabToEnd)
628 | list.Add(n.Content);
629 | else
630 | list.Insert(++i, n.Content);
631 |
632 | tabItem = (TabItem)this.ItemContainerGenerator.ContainerFromItem(n.Content);
633 | }
634 | else
635 | {
636 | // Using Items Property
637 | tabItem = new TabItem { Header = "New Tab" };
638 |
639 | if (i == -1 || i == this.Items.Count - 1 || AddNewTabToEnd)
640 | this.Items.Add(tabItem);
641 | else
642 | this.Items.Insert(++i, tabItem);
643 | }
644 |
645 | if (TabItemAdded != null)
646 | TabItemAdded(this, new TabItemEventArgs(tabItem));
647 | }
648 |
649 | ///
650 | /// Called by a child Header that wants to remove itself by clicking on the close button
651 | ///
652 | ///
653 | public void RemoveTabItem(TabItem tabItem)
654 | {
655 | if (IsFixedSize)
656 | throw new InvalidOperationException("ItemsSource is Fixed Size");
657 |
658 | // gives an opertunity to cancel the removal of the tabitem
659 | var c = new TabItemCancelEventArgs(tabItem);
660 | if (TabItemClosing != null)
661 | TabItemClosing(tabItem, c);
662 |
663 | if (c.Cancel)
664 | return;
665 |
666 | if (ItemsSource != null)
667 | {
668 | var list = ItemsSource as IList;
669 | object listItem = ItemContainerGenerator.ItemFromContainer(tabItem);
670 | if (listItem != null && list != null)
671 | list.Remove(listItem);
672 | }
673 | else
674 | this.Items.Remove(tabItem);
675 |
676 | if (TabItemClosed != null)
677 | TabItemClosed(this, new TabItemEventArgs(tabItem));
678 | }
679 |
680 | private void SetAddNewButtonVisibility()
681 | {
682 | if (this.Template == null)
683 | return;
684 |
685 | ButtonBase button = this.Template.FindName("PART_NewTabButton", this) as ButtonBase;
686 | if (button == null) return;
687 |
688 | if (IsFixedSize)
689 | button.Visibility = Visibility.Collapsed;
690 | else
691 | button.Visibility = AllowAddNew
692 | ? Visibility.Visible
693 | : Visibility.Collapsed;
694 | }
695 |
696 | private void SetTabItemsCloseButtonVisibility()
697 | {
698 | bool isFixedSize = IsFixedSize;
699 |
700 | var tabsCount = GetTabsCount();
701 | for (int i = 0; i < tabsCount; i++)
702 | {
703 | TabItem ti = GetTabItem(i);
704 | if (ti != null)
705 | ti.AllowDelete = !isFixedSize && this.AllowDelete;
706 | }
707 | }
708 |
709 | internal IEnumerable GetItems()
710 | {
711 | if (IsUsingItemsSource)
712 | return ItemsSource;
713 | return Items;
714 | }
715 |
716 | internal int GetTabsCount()
717 | {
718 | if (BindingOperations.IsDataBound(this, ItemsSourceProperty))
719 | {
720 | IList list = ItemsSource as IList;
721 | if (list != null)
722 | return list.Count;
723 |
724 | // ItemsSource is only an IEnumerable
725 | int i = 0;
726 | IEnumerator enumerator = ItemsSource.GetEnumerator();
727 | while (enumerator.MoveNext())
728 | i++;
729 | return i;
730 | }
731 |
732 | if (Items != null)
733 | return Items.Count;
734 |
735 | return 0;
736 | }
737 |
738 | internal TabItem GetTabItem(int index)
739 | {
740 | if (BindingOperations.IsDataBound(this, ItemsSourceProperty))
741 | {
742 | IList list = ItemsSource as IList;
743 | if (list != null)
744 | return this.ItemContainerGenerator.ContainerFromItem(list[index]) as TabItem;
745 |
746 | // ItemsSource is at least an IEnumerable
747 | int i = 0;
748 | IEnumerator enumerator = ItemsSource.GetEnumerator();
749 | while (enumerator.MoveNext())
750 | {
751 | if (i == index)
752 | return this.ItemContainerGenerator.ContainerFromItem(enumerator.Current) as TabItem;
753 | i++;
754 | }
755 | return null;
756 | }
757 | return Items[index] as TabItem;
758 | }
759 |
760 | private IEnumerable InternalChildren()
761 | {
762 | IEnumerator enumerator = GetItems().GetEnumerator();
763 | while (enumerator.MoveNext())
764 | {
765 | if (enumerator.Current is TabItem)
766 | yield return enumerator.Current;
767 | else
768 | yield return this.ItemContainerGenerator.ContainerFromItem(enumerator.Current) as TabItem;
769 | }
770 | }
771 | }
772 | }
773 |
--------------------------------------------------------------------------------
/TabControl/TabControl/TabEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.ComponentModel;
5 |
6 | namespace Wpf.Controls
7 | {
8 | public class TabItemEventArgs : EventArgs
9 | {
10 | public TabItem TabItem { get; private set; }
11 |
12 | public TabItemEventArgs(TabItem item)
13 | {
14 | TabItem = item;
15 | }
16 | }
17 | public class NewTabItemEventArgs : EventArgs
18 | {
19 | ///
20 | /// The object to be used as the Content for the new TabItem
21 | ///
22 | public object Content { get; set; }
23 | }
24 |
25 | public class TabItemCancelEventArgs : CancelEventArgs
26 | {
27 | public TabItem TabItem { get; private set; }
28 |
29 | public TabItemCancelEventArgs(TabItem item)
30 | {
31 | TabItem = item;
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/TabControl/TabControl/TabItem.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 | using System.Windows.Controls.Primitives;
3 | using System.Windows.Controls;
4 |
5 | namespace Wpf.Controls
6 | {
7 | [TemplatePart(Name = "PART_CloseButton", Type = typeof(ButtonBase))]
8 | public class TabItem : System.Windows.Controls.TabItem
9 | {
10 | static TabItem()
11 | {
12 | DefaultStyleKeyProperty.OverrideMetadata(typeof(Wpf.Controls.TabItem), new FrameworkPropertyMetadata(typeof(Wpf.Controls.TabItem)));
13 | }
14 |
15 | ///
16 | /// Provides a place to display an Icon on the Header and on the DropDown Context Menu
17 | ///
18 | public object Icon
19 | {
20 | get { return (object)GetValue(IconProperty); }
21 | set { SetValue(IconProperty, value); }
22 | }
23 | public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(object), typeof(TabItem), new UIPropertyMetadata(null));
24 |
25 | ///
26 | /// Allow the Header to be Deleted by the end user
27 | ///
28 | public bool AllowDelete
29 | {
30 | get { return (bool)GetValue(AllowDeleteProperty); }
31 | set { SetValue(AllowDeleteProperty, value); }
32 | }
33 | public static readonly DependencyProperty AllowDeleteProperty = DependencyProperty.Register("AllowDelete", typeof(bool), typeof(TabItem), new UIPropertyMetadata(true));
34 |
35 | ///
36 | /// OnApplyTemplate override
37 | ///
38 | public override void OnApplyTemplate()
39 | {
40 | base.OnApplyTemplate();
41 |
42 | // wire up the CloseButton's Click event if the button exists
43 | ButtonBase button = this.Template.FindName("PART_CloseButton", this) as ButtonBase;
44 | if (button != null)
45 | {
46 | button.Click += delegate
47 | {
48 | // get the parent tabcontrol
49 | TabControl tc = Helper.FindParentControl(this);
50 | if (tc == null) return;
51 |
52 | // remove this tabitem from the parent tabcontrol
53 | tc.RemoveTabItem(this);
54 | };
55 | }
56 | }
57 |
58 | ///
59 | /// Used by the TabPanel for sizing
60 | ///
61 | internal Dimension Dimension { get; set; }
62 |
63 | ///
64 | /// OnMouseEnter, Create and Display a Tooltip
65 | ///
66 | ///
67 | protected override void OnMouseEnter(System.Windows.Input.MouseEventArgs e)
68 | {
69 | base.OnMouseEnter(e);
70 |
71 | this.ToolTip = Helper.CloneElement(Header);
72 | e.Handled = true;
73 | }
74 |
75 | ///
76 | /// OnMouseLeave, remove the tooltip
77 | ///
78 | ///
79 | protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e)
80 | {
81 | base.OnMouseLeave(e);
82 |
83 | this.ToolTip = null;
84 | e.Handled = true;
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/TabControl/TabControl/TabPanel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Windows.Controls;
5 | using System.Windows;
6 | using System.ComponentModel;
7 | using System.Windows.Controls.Primitives;
8 | using System.Windows.Media;
9 |
10 | namespace Wpf.Controls
11 | {
12 | ///
13 | /// TabPanel
14 | ///
15 | public class TabPanel : Panel, IScrollInfo
16 | {
17 | private int _maxVisibleItems;
18 | // private double _maxChildWidthOrHeight;
19 | private readonly List _childRects;
20 | private Dock _tabStripPlacement;
21 |
22 | // scrolling
23 | private readonly TranslateTransform _translateTransform = new TranslateTransform();
24 | private Size _extent = new Size(0, 0);
25 | private Size _oldExtent = new Size(0, 0);
26 | private Size _viewPort = new Size(0, 0);
27 | private Size _lastSize = new Size(0, 0);
28 | private Point _offset = new Point(0, 0);
29 | private ScrollViewer _scrollOwner;
30 |
31 | private int _firstVisibleIndex;
32 | // private int _childCount;
33 |
34 | public TabPanel()
35 | {
36 | _childRects = new List(4);
37 | base.RenderTransform = _translateTransform;
38 | }
39 |
40 | #region CLR Properties
41 | private Dock TabStripPlacement
42 | {
43 | get
44 | {
45 | Dock dock = Dock.Top;
46 | TabControl templatedParent = base.TemplatedParent as TabControl;
47 | if (templatedParent != null)
48 | {
49 | dock = templatedParent.TabStripPlacement;
50 |
51 | }
52 | return dock;
53 | }
54 | }
55 | private double MinimumChildWidth
56 | {
57 | get
58 | {
59 | TabControl templatedParent = base.TemplatedParent as TabControl;
60 | if (templatedParent != null)
61 | return templatedParent.TabItemMinWidth;
62 | return 0;
63 | }
64 | }
65 | private double MinimumChildHeight
66 | {
67 | get
68 | {
69 | TabControl templatedParent = base.TemplatedParent as TabControl;
70 | if (templatedParent != null)
71 | return templatedParent.TabItemMinHeight;
72 | return 0;
73 | }
74 | }
75 | private double MaximumChildWidth
76 | {
77 | get
78 | {
79 | TabControl templatedParent = base.TemplatedParent as TabControl;
80 | if (templatedParent != null)
81 | return templatedParent.TabItemMaxWidth;
82 | return double.PositiveInfinity;
83 | }
84 | }
85 | private double MaximumChildHeight
86 | {
87 | get
88 | {
89 | TabControl templatedParent = base.TemplatedParent as TabControl;
90 | if (templatedParent != null)
91 | return templatedParent.TabItemMaxHeight;
92 | return double.PositiveInfinity;
93 | }
94 | }
95 |
96 | private int FirstVisibleIndex
97 | {
98 | get { return _firstVisibleIndex; }
99 | set
100 | {
101 | if (_firstVisibleIndex == value)
102 | return;
103 |
104 | if (value < 0)
105 | {
106 | _firstVisibleIndex = 0;
107 | return;
108 | }
109 |
110 | _firstVisibleIndex = value;
111 | if (LastVisibleIndex > InternalChildren.Count - 1)
112 | FirstVisibleIndex--;
113 | }
114 | }
115 |
116 | private int LastVisibleIndex
117 | {
118 | get { return _firstVisibleIndex + _maxVisibleItems - 1; }
119 | }
120 | #endregion
121 |
122 | #region Dependancy Properties
123 |
124 | ///
125 | /// CanScrollLeftOrUp Dependancy Property
126 | ///
127 | [Browsable(false)]
128 | internal bool CanScrollLeftOrUp
129 | {
130 | get { return (bool)GetValue(CanScrollLeftOrUpProperty); }
131 | set { SetValue(CanScrollLeftOrUpProperty, value); }
132 | }
133 | internal static readonly DependencyProperty CanScrollLeftOrUpProperty = DependencyProperty.Register("CanScrollLeftOrUp", typeof(bool), typeof(TabPanel), new UIPropertyMetadata(false));
134 |
135 | ///
136 | /// CanScrollRightOrDown Dependancy Property
137 | ///
138 | [Browsable(false)]
139 | internal bool CanScrollRightOrDown
140 | {
141 | get { return (bool)GetValue(CanScrollRightOrDownProperty); }
142 | set { SetValue(CanScrollRightOrDownProperty, value); }
143 | }
144 | internal static readonly DependencyProperty CanScrollRightOrDownProperty = DependencyProperty.Register("CanScrollRightOrDown", typeof(bool), typeof(TabPanel), new UIPropertyMetadata(false));
145 |
146 | #endregion
147 |
148 | #region MeasureOverride
149 | ///
150 | /// Measure Override
151 | ///
152 | protected override Size MeasureOverride(Size availableSize)
153 | {
154 | _viewPort = availableSize;
155 | _tabStripPlacement = TabStripPlacement;
156 |
157 | switch (_tabStripPlacement)
158 | {
159 | case Dock.Top:
160 | case Dock.Bottom:
161 | return MeasureHorizontal(availableSize);
162 |
163 | case Dock.Left:
164 | case Dock.Right:
165 | return MeasureVertical(availableSize);
166 | }
167 |
168 | return new Size();
169 | }
170 | #endregion
171 |
172 | #region MeasureHorizontal
173 | ///
174 | /// Measure the tab items for docking at the top or bottom
175 | ///
176 | private Size MeasureHorizontal(Size availableSize)
177 | {
178 | double maxChildWidthOrHeight = 0d;
179 | int childCount = InternalChildren.Count;
180 | EnsureChildRects();
181 |
182 | double extentWidth = 0d;
183 | double[] widths = new double[childCount]; // stores the widths of the items for use in the arrange pass
184 |
185 | for (int i = 0; i < childCount; i++)
186 | {
187 | TabItem tabItem = InternalChildren[i] as TabItem;
188 | if (tabItem == null) return new Size();
189 |
190 | SetDimensions(tabItem);
191 |
192 | tabItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
193 |
194 | ClearDimensions(tabItem);
195 |
196 | // calculate the maximum child height
197 | maxChildWidthOrHeight = Math.Max(maxChildWidthOrHeight, Math.Ceiling(tabItem.DesiredSize.Height));
198 |
199 | // calculate the child width while respecting the Maximum & Minimum width constraints
200 | widths[i] = Math.Min(MaximumChildWidth, Math.Max(MinimumChildWidth, Math.Ceiling(tabItem.DesiredSize.Width)));
201 |
202 | // determines how much horizontal space we require
203 | extentWidth += widths[i];
204 | }
205 | maxChildWidthOrHeight = Math.Max(MinimumChildHeight, Math.Min(MaximumChildHeight, maxChildWidthOrHeight)); // observe the constraints
206 | _extent = new Size(extentWidth, maxChildWidthOrHeight);
207 |
208 | bool flag = false;
209 | // 1). all the children fit into the available space using there desired widths
210 | if (extentWidth <= availableSize.Width)
211 | {
212 | _maxVisibleItems = childCount;
213 | FirstVisibleIndex = 0;
214 |
215 | double left = 0;
216 | for (int i = 0; i < childCount; i++)
217 | {
218 | _childRects[i] = new Rect(left, 0, widths[i], maxChildWidthOrHeight);
219 | left += widths[i];
220 |
221 | FrameworkElement child = InternalChildren[i] as FrameworkElement;
222 | if (child != null) child.Measure(new Size(widths[i], maxChildWidthOrHeight));
223 | }
224 |
225 | CanScrollLeftOrUp = false;
226 | CanScrollRightOrDown = false;
227 |
228 | flag = true;
229 | }
230 |
231 | // 2). all the children fit in the available space if we reduce their widths to a uniform value
232 | // while staying within the MinimumChildWidth and MaximumChildWidth constraints
233 | if (!flag)
234 | {
235 | // make sure the width is not greater than the MaximumChildWidth constraints
236 | double targetWidth = Math.Min(MaximumChildWidth, availableSize.Width / childCount);
237 |
238 | // target width applies now if whether we can fit all items in the available space or whether we are scrolling
239 | if (targetWidth >= MinimumChildWidth)
240 | {
241 | _maxVisibleItems = childCount;
242 | FirstVisibleIndex = 0;
243 |
244 | extentWidth = 0;
245 | double left = 0;
246 |
247 | for (int i = 0; i < childCount; i++)
248 | {
249 | extentWidth += targetWidth;
250 | widths[i] = targetWidth;
251 | _childRects[i] = new Rect(left, 0, widths[i], maxChildWidthOrHeight);
252 | left += widths[i];
253 |
254 | FrameworkElement child = InternalChildren[i] as FrameworkElement;
255 | if (child != null) child.Measure(new Size(widths[i], maxChildWidthOrHeight));
256 | }
257 | _extent = new Size(extentWidth, maxChildWidthOrHeight);
258 |
259 | flag = true;
260 |
261 | CanScrollLeftOrUp = false;
262 | CanScrollRightOrDown = false;
263 | }
264 | }
265 |
266 | // 3) we can not fit all the children in the viewport, so now we will enable scrolling/virtualizing items
267 | if (!flag)
268 | {
269 | _maxVisibleItems = (int)Math.Floor(_viewPort.Width / MinimumChildWidth); // calculate how many visible children we can show at once
270 | if (_maxVisibleItems == 0)
271 | _maxVisibleItems = 1;
272 |
273 | double targetWidth = availableSize.Width / _maxVisibleItems; // calculate the new target width
274 | FirstVisibleIndex = _firstVisibleIndex;
275 |
276 | extentWidth = 0;
277 | double left = 0;
278 | for (int i = 0; i < childCount; i++)
279 | {
280 | extentWidth += targetWidth;
281 | widths[i] = targetWidth;
282 |
283 | _childRects[i] = new Rect(left, 0, widths[i], maxChildWidthOrHeight);
284 | left += widths[i];
285 |
286 |
287 | FrameworkElement child = InternalChildren[i] as FrameworkElement;
288 | if (child != null) child.Measure(new Size(widths[i], maxChildWidthOrHeight));
289 | }
290 | _extent = new Size(extentWidth, maxChildWidthOrHeight);
291 |
292 | CanScrollLeftOrUp = LastVisibleIndex < childCount - 1;
293 | CanScrollRightOrDown = FirstVisibleIndex > 0;
294 | }
295 |
296 | return new Size(double.IsInfinity(availableSize.Width) ? _extent.Width : availableSize.Width, maxChildWidthOrHeight);
297 | }
298 |
299 | #endregion
300 |
301 | #region MeasureVertical
302 |
303 | ///
304 | /// Measure the tab items for docking at the left or right
305 | ///
306 | private Size MeasureVertical(Size availableSize)
307 | {
308 | int childCount = InternalChildren.Count;
309 | double maxChildWidthOrHeight = 0d;
310 |
311 | EnsureChildRects();
312 |
313 | double extentHeight = 0d;
314 | double[] heights = new double[childCount];
315 |
316 | // we will first measure all the children with unlimited space to get their desired sizes
317 | // this will also get us the height required for all TabItems
318 | for (int i = 0; i < childCount; i++)
319 | {
320 | TabItem tabItem = InternalChildren[i] as TabItem;
321 | if (tabItem == null) return new Size();
322 |
323 | SetDimensions(tabItem);
324 |
325 | tabItem.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
326 |
327 | ClearDimensions(tabItem);
328 |
329 | // calculate the maximum child width
330 | maxChildWidthOrHeight = Math.Max(maxChildWidthOrHeight, Math.Ceiling(tabItem.DesiredSize.Width));
331 |
332 | // calculate the child width while respecting the Maximum & Minimum width constraints
333 | heights[i] = Math.Min(MaximumChildHeight, Math.Max(MinimumChildHeight, Math.Ceiling(tabItem.DesiredSize.Height)));
334 |
335 | // determines how much horizontal space we require
336 | extentHeight += heights[i];
337 | }
338 | maxChildWidthOrHeight = Math.Max(MinimumChildWidth, Math.Min(MaximumChildWidth, maxChildWidthOrHeight)); // observe the constraints
339 | _extent = new Size(maxChildWidthOrHeight, extentHeight);
340 |
341 | bool flag = false;
342 | // 1). all the children fit into the available space using there desired widths
343 | if (extentHeight <= availableSize.Height)
344 | {
345 | _maxVisibleItems = childCount;
346 | FirstVisibleIndex = 0;
347 |
348 | double top = 0;
349 | for (int i = 0; i < childCount; i++)
350 | {
351 | _childRects[i] = new Rect(0, top, maxChildWidthOrHeight, heights[i]);
352 | top += heights[i];
353 |
354 | FrameworkElement child = InternalChildren[i] as FrameworkElement;
355 | if (child != null) child.Measure(new Size(maxChildWidthOrHeight, heights[i]));
356 | }
357 |
358 | CanScrollLeftOrUp = false;
359 | CanScrollRightOrDown = false;
360 |
361 | flag = true;
362 | }
363 |
364 | // 2). all the children fit in the available space if we reduce their widths to a uniform value
365 | // while staying within the MinimumChildWidth and MaximumChildWidth constraints
366 | if (!flag)
367 | {
368 | // make sure the width is not greater than the MaximumChildWidth constraints
369 | double targetHeight = Math.Min(MaximumChildHeight, availableSize.Height / childCount);
370 |
371 | // target width applies now if whether we can fit all items in the available space or whether we are scrolling
372 | if (targetHeight >= MinimumChildHeight)
373 | {
374 | _maxVisibleItems = childCount;
375 | FirstVisibleIndex = 0;
376 |
377 | extentHeight = 0;
378 | double top = 0;
379 |
380 | for (int i = 0; i < childCount; i++)
381 | {
382 | extentHeight += targetHeight;
383 | heights[i] = targetHeight;
384 | _childRects[i] = new Rect(0, top, maxChildWidthOrHeight, heights[i]);
385 | top += heights[i];
386 |
387 | FrameworkElement child = InternalChildren[i] as FrameworkElement;
388 | if (child != null) child.Measure(new Size(maxChildWidthOrHeight, heights[i]));
389 | }
390 | _extent = new Size(maxChildWidthOrHeight, extentHeight);
391 |
392 | flag = true;
393 |
394 | CanScrollLeftOrUp = false;
395 | CanScrollRightOrDown = false;
396 | }
397 | }
398 |
399 | // 3) we can not fit all the children in the viewport, so now we will enable scrolling/virtualizing items
400 | if (!flag)
401 | {
402 | _maxVisibleItems = (int)Math.Floor(_viewPort.Height / MinimumChildHeight); // calculate how many visible children we can show at once
403 | double targetHeight = availableSize.Height / _maxVisibleItems; // calculate the new target width
404 | FirstVisibleIndex = _firstVisibleIndex;
405 |
406 | extentHeight = 0;
407 | double top = 0;
408 | for (int i = 0; i < childCount; i++)
409 | {
410 | extentHeight += targetHeight;
411 | heights[i] = targetHeight;
412 | _childRects[i] = new Rect(0, top, maxChildWidthOrHeight, heights[i]);
413 | top += heights[i];
414 |
415 | FrameworkElement child = InternalChildren[i] as FrameworkElement;
416 | if (child != null) child.Measure(new Size(maxChildWidthOrHeight, heights[i]));
417 | }
418 | _extent = new Size(maxChildWidthOrHeight, extentHeight);
419 |
420 | CanScrollLeftOrUp = LastVisibleIndex < childCount - 1;
421 | CanScrollRightOrDown = FirstVisibleIndex > 0;
422 | }
423 |
424 | return new Size(maxChildWidthOrHeight, double.IsInfinity(availableSize.Height) ? _extent.Height : availableSize.Height);
425 |
426 | }
427 | #endregion
428 |
429 | #region ArrangeOverride
430 |
431 | ///
432 | /// Arrange Override
433 | ///
434 | protected override Size ArrangeOverride(Size finalSize)
435 | {
436 | // monitors changes to the ScrollViewer extent value
437 | if (_oldExtent != _extent)
438 | {
439 | _oldExtent = _extent;
440 | if (_scrollOwner != null)
441 | _scrollOwner.InvalidateScrollInfo();
442 | }
443 |
444 | // monitors changes to the parent container size, (ie window resizes)
445 | if (finalSize != _lastSize)
446 | {
447 | _lastSize = finalSize;
448 | if (_scrollOwner != null)
449 | _scrollOwner.InvalidateScrollInfo();
450 | }
451 |
452 | // monitor scrolling being removed
453 | bool invalidateMeasure = false;
454 | if (_extent.Width <= _viewPort.Width && _offset.X > 0)
455 | {
456 | _offset.X = 0;
457 | _translateTransform.X = 0;
458 |
459 | if (_scrollOwner != null)
460 | _scrollOwner.InvalidateScrollInfo();
461 |
462 | invalidateMeasure = true;
463 | }
464 | if (_extent.Height <= _viewPort.Height && _offset.Y > 0)
465 | {
466 | _offset.Y = 0;
467 | _translateTransform.Y = 0;
468 |
469 | if (_scrollOwner != null)
470 | _scrollOwner.InvalidateScrollInfo();
471 |
472 | invalidateMeasure = true;
473 | }
474 | if (invalidateMeasure)
475 | InvalidateMeasure();
476 |
477 |
478 |
479 | // arrange the children
480 | for (var i = 0; i < InternalChildren.Count; i++)
481 | {
482 | InternalChildren[i].Arrange(_childRects[i]);
483 | }
484 |
485 | // we need these lines as when the Scroll Buttons get Shown/Hidden,
486 | // the _offset value gets out of line, this will ensure that our scroll position stays in line
487 | if (InternalChildren.Count > 0)
488 | {
489 | _offset = _childRects[FirstVisibleIndex].TopLeft;
490 | _translateTransform.X = -_offset.X;
491 | _translateTransform.Y = -_offset.Y;
492 | }
493 |
494 | return finalSize;
495 | }
496 | #endregion
497 |
498 | private void EnsureChildRects()
499 | {
500 | while (InternalChildren.Count > _childRects.Count)
501 | _childRects.Add(new Rect());
502 | }
503 |
504 | #region IScrollInfo Members
505 |
506 | public bool CanHorizontallyScroll { get; set; }
507 |
508 | public bool CanVerticallyScroll { get; set; }
509 |
510 | public double ExtentHeight
511 | {
512 | get { return _extent.Height; }
513 | }
514 |
515 | public double ExtentWidth
516 | {
517 | get { return _extent.Width; }
518 | }
519 |
520 | public double HorizontalOffset
521 | {
522 | get { return _offset.X; }
523 | }
524 |
525 | public void LineDown()
526 | {
527 | LineRight();
528 | }
529 |
530 | public void LineLeft()
531 | {
532 | // this works because we can guarantee that when we are in scroll mode,
533 | // there will be children, and they will all be of equal size
534 | FirstVisibleIndex++;
535 |
536 | if (_tabStripPlacement == Dock.Top || _tabStripPlacement == Dock.Bottom)
537 | SetHorizontalOffset(HorizontalOffset + _childRects[0].Width);
538 | else
539 | SetVerticalOffset(HorizontalOffset + _childRects[0].Height);
540 | }
541 |
542 | public void LineRight()
543 | {
544 | FirstVisibleIndex--;
545 |
546 | if (_tabStripPlacement == Dock.Top || _tabStripPlacement == Dock.Bottom)
547 | SetHorizontalOffset(HorizontalOffset - _childRects[0].Width);
548 | else
549 | SetVerticalOffset(HorizontalOffset - _childRects[0].Height);
550 | }
551 |
552 | public void LineUp()
553 | {
554 | LineLeft();
555 | }
556 |
557 | public Rect MakeVisible(Visual visual, Rect rectangle)
558 | {
559 | InvalidateMeasure();
560 | UpdateLayout();
561 |
562 | TabControl ic = ItemsControl.GetItemsOwner(this) as TabControl;
563 | if (ic == null) return Rect.Empty;
564 |
565 | int index = -1;
566 | var tabsCount = ic.GetTabsCount();
567 | for (int i = 0; i < tabsCount; i++)
568 | {
569 | if (visual.Equals(ic.GetTabItem(i)))
570 | {
571 | index = i;
572 | break;
573 | }
574 | }
575 | if (index > -1)
576 | {
577 | if (index < FirstVisibleIndex)
578 | FirstVisibleIndex = index;
579 | else if (index > LastVisibleIndex)
580 | {
581 | while (index > LastVisibleIndex)
582 | FirstVisibleIndex++;
583 | }
584 |
585 | InvalidateArrange();
586 | }
587 |
588 | return Rect.Empty;
589 | }
590 |
591 | public void MouseWheelDown()
592 | {
593 | LineDown();
594 | }
595 |
596 | public void MouseWheelLeft()
597 | {
598 | LineLeft();
599 | }
600 |
601 | public void MouseWheelRight()
602 | {
603 | LineRight();
604 | }
605 |
606 | public void MouseWheelUp()
607 | {
608 | LineUp();
609 | }
610 |
611 | public void PageDown()
612 | {
613 | throw new NotImplementedException();
614 | }
615 |
616 | public void PageLeft()
617 | {
618 | throw new NotImplementedException();
619 | }
620 |
621 | public void PageRight()
622 | {
623 | throw new NotImplementedException();
624 | }
625 |
626 | public void PageUp()
627 | {
628 | throw new NotImplementedException();
629 | }
630 |
631 | public ScrollViewer ScrollOwner
632 | {
633 | get { return _scrollOwner; }
634 | set { _scrollOwner = value; }
635 | }
636 |
637 | public void SetHorizontalOffset(double offset)
638 | {
639 | if (offset < 0 || _viewPort.Width >= _extent.Width)
640 | offset = 0;
641 | else
642 | {
643 | if (offset + _viewPort.Width > _extent.Width)
644 | offset = _extent.Width - _viewPort.Width;
645 | }
646 |
647 | _offset.X = offset;
648 | if (_scrollOwner != null)
649 | _scrollOwner.InvalidateScrollInfo();
650 |
651 | _translateTransform.X = -offset;
652 |
653 | InvalidateMeasure();
654 | }
655 |
656 | public void SetVerticalOffset(double offset)
657 | {
658 | if (offset < 0 || _viewPort.Height >= _extent.Height)
659 | offset = 0;
660 | else
661 | {
662 | if (offset + _viewPort.Height > _extent.Height)
663 | offset = _extent.Height - _viewPort.Height;
664 | }
665 |
666 | _offset.Y = offset;
667 | if (_scrollOwner != null)
668 | _scrollOwner.InvalidateScrollInfo();
669 |
670 | _translateTransform.Y = -offset;
671 |
672 | InvalidateMeasure();
673 | }
674 |
675 | public double VerticalOffset
676 | {
677 | get { return _offset.Y; }
678 | }
679 |
680 | public double ViewportHeight
681 | {
682 | get { return _viewPort.Height; }
683 | }
684 |
685 | public double ViewportWidth
686 | {
687 | get { return _viewPort.Width; }
688 | }
689 |
690 | #endregion
691 |
692 | #region Helpers
693 |
694 | private static void SetDimensions(TabItem tabItem)
695 | {
696 | if (tabItem.Dimension == null)
697 | {
698 | // store the original size specifications of the tab
699 | tabItem.Dimension =
700 | new Dimension
701 | {
702 | Height = tabItem.Height,
703 | Width = tabItem.Width,
704 | MaxHeight = tabItem.MaxHeight,
705 | MaxWidth = tabItem.MaxWidth,
706 | MinHeight = tabItem.MinHeight,
707 | MinWidth = tabItem.MinWidth
708 | };
709 | }
710 | else
711 | {
712 | // restore the original values for the tab
713 | tabItem.BeginInit();
714 | tabItem.Height = tabItem.Dimension.Height;
715 | tabItem.Width = tabItem.Dimension.Width;
716 | tabItem.MaxHeight = tabItem.Dimension.MaxHeight;
717 | tabItem.MaxWidth = tabItem.Dimension.MaxWidth;
718 | tabItem.MinHeight = tabItem.Dimension.MinHeight;
719 | tabItem.MinWidth = tabItem.Dimension.MinWidth;
720 | tabItem.EndInit();
721 | }
722 | }
723 |
724 | private static void ClearDimensions(FrameworkElement tabItem)
725 | {
726 | // remove any size restrictions from the Header,
727 | // this is because the TabControl's size restriction properties takes precedence over
728 | // the individual tab items
729 | // eg, if the TabControl sets the TabItemMaxWidth property to 300, but the Header
730 | // has a minWidth of 400, the TabControls value of 300 should be used
731 | tabItem.BeginInit();
732 | tabItem.Height = double.NaN;
733 | tabItem.Width = double.NaN;
734 | tabItem.MaxHeight = double.PositiveInfinity;
735 | tabItem.MaxWidth = double.PositiveInfinity;
736 | tabItem.MinHeight = 0;
737 | tabItem.MinWidth = 0;
738 | tabItem.EndInit();
739 | }
740 |
741 | #endregion
742 | }
743 | }
744 |
--------------------------------------------------------------------------------
/TabControl/TabControl/Themes/Generic.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
78 |
79 |
80 |
136 |
137 |
138 |
177 |
178 |
179 |
218 |
219 |
220 |
261 |
262 |
263 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
427 |
428 |
442 |
443 |
444 |
455 |
456 |
457 |
458 |
459 |
460 |
470 |
471 |
472 |
480 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
517 |
518 |
532 |
533 |
534 |
545 |
546 |
547 |
548 |
549 |
550 |
561 |
562 |
563 |
572 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
613 |
614 |
615 |
617 |
618 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
637 |
638 |
639 |
647 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
688 |
689 |
690 |
692 |
693 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
713 |
714 |
715 |
723 |
729 |
730 |
731 |
732 |
733 |
775 |
776 |
--------------------------------------------------------------------------------
/TabControl/TabControl/Wpf.TabControl.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {509D06BD-AEB6-4E92-8E5B-21188EBDBD9C}
9 | library
10 | Properties
11 | Wpf.Controls
12 | Wpf.TabControl
13 | v3.0
14 | 512
15 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
16 | 4
17 |
18 |
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 | 3.0
38 |
39 |
40 | 3.0
41 |
42 |
43 | 3.0
44 |
45 |
46 | 3.0
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 | Code
60 |
61 |
62 | True
63 | True
64 | Resources.resx
65 |
66 |
67 | True
68 | Settings.settings
69 | True
70 |
71 |
72 |
73 |
74 |
75 |
76 | ResXFileCodeGenerator
77 | Resources.Designer.cs
78 | Designer
79 |
80 |
81 | SettingsSingleFileGenerator
82 | Settings.Designer.cs
83 |
84 |
85 |
86 |
87 |
88 | MSBuild:Compile
89 | Designer
90 |
91 |
92 |
93 |
94 |
95 |
96 |
103 |
--------------------------------------------------------------------------------
/TabControl/Test/App.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TabControl/Test/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Windows;
7 |
8 | namespace Test
9 | {
10 | ///
11 | /// Interaction logic for App.xaml
12 | ///
13 | public partial class App : Application
14 | {
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/TabControl/Test/Images/ie.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/FluentNotepad/TabControl/438271851a4bfbc0a57f8b16ba2212b0ceb59f2e/TabControl/Test/Images/ie.ico
--------------------------------------------------------------------------------
/TabControl/Test/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Resources;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 | using System.Windows;
6 |
7 | // General Information about an assembly is controlled through the following
8 | // set of attributes. Change these attribute values to modify the information
9 | // associated with an assembly.
10 | [assembly: AssemblyTitle("Test")]
11 | [assembly: AssemblyDescription("")]
12 | [assembly: AssemblyConfiguration("")]
13 | [assembly: AssemblyCompany("")]
14 | [assembly: AssemblyProduct("Test")]
15 | [assembly: AssemblyCopyright("Copyright © 2007")]
16 | [assembly: AssemblyTrademark("")]
17 | [assembly: AssemblyCulture("")]
18 |
19 | // Setting ComVisible to false makes the types in this assembly not visible
20 | // to COM components. If you need to access a type in this assembly from
21 | // COM, set the ComVisible attribute to true on that type.
22 | [assembly: ComVisible(false)]
23 |
24 | //In order to begin building localizable applications, set
25 | //CultureYouAreCodingWith in your .csproj file
26 | //inside a . For example, if you are using US english
27 | //in your source files, set the to en-US. Then uncomment
28 | //the NeutralResourceLanguage attribute below. Update the "en-US" in
29 | //the line below to match the UICulture setting in the project file.
30 |
31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
32 |
33 |
34 | [assembly: ThemeInfo(
35 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
36 | //(used if a resource is not found in the page,
37 | // or application resource dictionaries)
38 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
39 | //(used if a resource is not found in the page,
40 | // app, or any theme specific resource dictionaries)
41 | )]
42 |
43 |
44 | // Version information for an assembly consists of the following four values:
45 | //
46 | // Major Version
47 | // Minor Version
48 | // Build Number
49 | // Revision
50 | //
51 | // You can specify all the values or you can default the Build and Revision Numbers
52 | // by using the '*' as shown below:
53 | // [assembly: AssemblyVersion("1.0.*")]
54 | [assembly: AssemblyVersion("1.0.0.0")]
55 | [assembly: AssemblyFileVersion("1.0.0.0")]
56 |
--------------------------------------------------------------------------------
/TabControl/Test/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:2.0.50727.1378
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Test.Properties
12 | {
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources
26 | {
27 |
28 | private static global::System.Resources.ResourceManager resourceMan;
29 |
30 | private static global::System.Globalization.CultureInfo resourceCulture;
31 |
32 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
33 | internal Resources()
34 | {
35 | }
36 |
37 | ///
38 | /// Returns the cached ResourceManager instance used by this class.
39 | ///
40 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41 | internal static global::System.Resources.ResourceManager ResourceManager
42 | {
43 | get
44 | {
45 | if ((resourceMan == null))
46 | {
47 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Test.Properties.Resources", typeof(Resources).Assembly);
48 | resourceMan = temp;
49 | }
50 | return resourceMan;
51 | }
52 | }
53 |
54 | ///
55 | /// Overrides the current thread's CurrentUICulture property for all
56 | /// resource lookups using this strongly typed resource class.
57 | ///
58 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
59 | internal static global::System.Globalization.CultureInfo Culture
60 | {
61 | get
62 | {
63 | return resourceCulture;
64 | }
65 | set
66 | {
67 | resourceCulture = value;
68 | }
69 | }
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/TabControl/Test/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 | text/microsoft-resx
107 |
108 |
109 | 2.0
110 |
111 |
112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
113 |
114 |
115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
--------------------------------------------------------------------------------
/TabControl/Test/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:2.0.50727.1378
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace Test.Properties
12 | {
13 |
14 |
15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "9.0.0.0")]
17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
18 | {
19 |
20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
21 |
22 | public static Settings Default
23 | {
24 | get
25 | {
26 | return defaultInstance;
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/TabControl/Test/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/TabControl/Test/Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 9.0.30729
7 | 2.0
8 | {25344CF3-B7AA-4C75-B0EE-7C92ACFF3A9F}
9 | WinExe
10 | Properties
11 | Test
12 | Test
13 | v3.5
14 | 512
15 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
16 | 4
17 |
18 |
19 | true
20 | full
21 | false
22 | bin\Debug\
23 | DEBUG;TRACE
24 | prompt
25 | 4
26 |
27 |
28 | pdbonly
29 | true
30 | bin\Release\
31 | TRACE
32 | prompt
33 | 4
34 |
35 |
36 |
37 |
38 | 3.5
39 |
40 |
41 |
42 | 3.5
43 |
44 |
45 | 3.5
46 |
47 |
48 |
49 |
50 | 3.0
51 |
52 |
53 | 3.0
54 |
55 |
56 | 3.0
57 |
58 |
59 | 3.0
60 |
61 |
62 | 3.0
63 |
64 |
65 |
66 |
67 | MSBuild:Compile
68 | Designer
69 |
70 |
71 | Designer
72 | MSBuild:Compile
73 |
74 |
75 | MSBuild:Compile
76 | Designer
77 |
78 |
79 | MSBuild:Compile
80 | Designer
81 |
82 |
83 | App.xaml
84 | Code
85 |
86 |
87 | WindowUsingItemsProperty.xaml
88 | Code
89 |
90 |
91 |
92 |
93 | Code
94 |
95 |
96 | True
97 | True
98 | Resources.resx
99 |
100 |
101 | True
102 | Settings.settings
103 | True
104 |
105 |
106 | Window1.xaml
107 |
108 |
109 | WindowUsingItemsSourceProperty.xaml
110 |
111 |
112 | ResXFileCodeGenerator
113 | Resources.Designer.cs
114 | Designer
115 |
116 |
117 | SettingsSingleFileGenerator
118 | Settings.Designer.cs
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | {509D06BD-AEB6-4E92-8E5B-21188EBDBD9C}
128 | Wpf.TabControl
129 |
130 |
131 |
132 |
139 |
--------------------------------------------------------------------------------
/TabControl/Test/Window1.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/TabControl/Test/Window1.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 |
4 | namespace Test
5 | {
6 | ///
7 | /// Interaction logic for Window1.xaml
8 | ///
9 | public partial class Window1 : Window
10 | {
11 | public Window1()
12 | {
13 | InitializeComponent();
14 | }
15 |
16 | private void Items_Click(object sender, RoutedEventArgs e)
17 | {
18 | var win = new WindowUsingItemProperty();
19 | win.Show();
20 | }
21 | private void ItemsSource_Click(object sender, RoutedEventArgs e)
22 | {
23 | var win = new WindowUsingItemsSourceProperty();
24 | win.Show();
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/TabControl/Test/WindowUsingItemsProperty.xaml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
72 |
73 |
74 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/TabControl/Test/WindowUsingItemsProperty.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using System.Windows.Controls;
4 | using System.Windows.Input;
5 | using System.Windows.Media.Imaging;
6 | using System.Windows.Forms.Integration;
7 | using System.Windows.Forms;
8 | using Wpf.Controls;
9 |
10 | namespace Test
11 | {
12 | ///
13 | /// Interaction logic for WindowUsingItemProperty.xaml
14 | ///
15 | public partial class WindowUsingItemProperty : Window
16 | {
17 | public WindowUsingItemProperty()
18 | {
19 | InitializeComponent();
20 |
21 | tabControl.TabItemAdded += tabControl_TabItemAdded;
22 | tabControl.SelectionChanged += tabControl_SelectionChanged;
23 |
24 | // these 3 events ensure that all the contents of the textbox are selected on a mouseclick (as per IE)
25 | // code borrowed from the Windows Presentation Foundation Forum at http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2199428&SiteID=1
26 | textBox.GotKeyboardFocus += delegate(object sender, KeyboardFocusChangedEventArgs e)
27 | {
28 | System.Windows.Controls.TextBox tb = (sender as System.Windows.Controls.TextBox);
29 | if (tb != null)
30 | tb.SelectAll();
31 | };
32 | textBox.MouseDoubleClick += delegate(object sender, MouseButtonEventArgs e)
33 | {
34 | System.Windows.Controls.TextBox tb = (sender as System.Windows.Controls.TextBox);
35 | if (tb != null)
36 | tb.SelectAll();
37 | };
38 | textBox.PreviewMouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e)
39 | {
40 | System.Windows.Controls.TextBox tb = (sender as System.Windows.Controls.TextBox);
41 |
42 | if (tb != null)
43 | {
44 | if (!tb.IsKeyboardFocusWithin)
45 | {
46 | e.Handled = true;
47 | tb.Focus();
48 | }
49 | }
50 | };
51 | }
52 |
53 |
54 | private int count = 1;
55 | void tabControl_TabItemAdded(object sender, TabItemEventArgs e)
56 | {
57 | // Add an Icon to the tabItem
58 | BitmapImage image = new BitmapImage(new Uri("pack://application:,,,/Test;component/Images/ie.ico"));
59 | Image img = new Image();
60 | img.Source = image;
61 | img.Width = 16;
62 | img.Height = 16;
63 | img.Margin = new Thickness(2, 0, 2, 0);
64 |
65 | e.TabItem.Icon = img;
66 |
67 | // wrap the header in a textblock, this gives us the character ellipsis (...) when trimmed
68 | TextBlock tb = new TextBlock();
69 | tb.Text = "New Tab " + count++;
70 | tb.TextTrimming = TextTrimming.CharacterEllipsis;
71 | tb.TextWrapping = TextWrapping.NoWrap;
72 |
73 | e.TabItem.Header = tb;
74 |
75 | // add a WebControl to the TAbItem
76 | WindowsFormsHost host = new WindowsFormsHost();
77 | host.Margin = new Thickness(2);
78 | System.Windows.Forms.WebBrowser browser = new System.Windows.Forms.WebBrowser();
79 | browser.DocumentTitleChanged += Browser_DocumentTitleChanged;
80 | browser.Navigated += Browser_Navigated;
81 |
82 | host.Child = browser;
83 | e.TabItem.Content = host;
84 | }
85 |
86 |
87 | void tabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
88 | {
89 | System.Windows.Forms.WebBrowser browser = GetCurrentWebBrowser();
90 | if (browser == null) return;
91 |
92 | // set the Textbox to the Url of the selected tab
93 | if (browser.Url == null)
94 | textBox.Text = "about:blank";
95 | else
96 | textBox.Text = browser.Url.ToString();
97 |
98 | textBox.Focus();
99 | }
100 |
101 | void Browser_DocumentTitleChanged(object sender, EventArgs e)
102 | {
103 | System.Windows.Forms.WebBrowser browser = sender as System.Windows.Forms.WebBrowser;
104 | if (browser == null) return;
105 |
106 | // update the TabItems's Header property
107 | Wpf.Controls.TabItem item = tabControl.SelectedItem as Wpf.Controls.TabItem;
108 |
109 | // Add an Icon to the tabItem
110 | BitmapImage image = new BitmapImage(new Uri("pack://application:,,,/Test;component/Images/ie.ico"));
111 | Image img = new Image();
112 | img.Source = image;
113 | img.Width = 16;
114 | img.Height = 16;
115 | img.Margin = new Thickness(2, 0, 2, 0);
116 |
117 | if (item != null) item.Icon = img;
118 |
119 | // wrap the header in a textblock, this gives us the character ellipsis (...) when trimmed
120 | TextBlock tb = new TextBlock();
121 | tb.Text= browser.DocumentTitle;
122 | tb.TextTrimming = TextTrimming.CharacterEllipsis;
123 | tb.TextWrapping = TextWrapping.NoWrap;
124 |
125 | if (item != null) item.Header = tb;
126 | }
127 |
128 | void Browser_Navigated(object sender, WebBrowserNavigatedEventArgs e)
129 | {
130 | System.Windows.Forms.WebBrowser browser = sender as System.Windows.Forms.WebBrowser;
131 | if (browser == null) return;
132 |
133 | textBox.Text = browser.Url.ToString();
134 | }
135 |
136 | private void textBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
137 | {
138 | if (e.Key == Key.Return)
139 | {
140 | try
141 | {
142 | Cursor = System.Windows.Input.Cursors.Wait;
143 | System.Windows.Forms.WebBrowser browser = GetCurrentWebBrowser();
144 | if (browser == null) return;
145 |
146 | // navigate to the selected web address
147 | browser.Navigate(textBox.Text);
148 | }
149 | finally
150 | {
151 | Cursor = System.Windows.Input.Cursors.Arrow;
152 | }
153 | }
154 | }
155 |
156 | private System.Windows.Forms.WebBrowser GetCurrentWebBrowser()
157 | {
158 | Wpf.Controls.TabItem item = tabControl.SelectedItem as Wpf.Controls.TabItem;
159 | if (item == null) return null;
160 |
161 | WindowsFormsHost host = item.Content as WindowsFormsHost;
162 | if (host == null) return null;
163 |
164 | System.Windows.Forms.WebBrowser browser = host.Child as System.Windows.Forms.WebBrowser;
165 | return browser;
166 | }
167 |
168 | private void TextBox_GotFocus(object sender, RoutedEventArgs e)
169 | {
170 | System.Windows.Controls.TextBox tb = (sender as System.Windows.Controls.TextBox);
171 | if (tb != null)
172 | tb.SelectAll();
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/TabControl/Test/WindowUsingItemsSourceProperty.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/TabControl/Test/WindowUsingItemsSourceProperty.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Collections.ObjectModel;
5 | using System.ComponentModel;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using Wpf.Controls;
9 |
10 | namespace Test
11 | {
12 | ///
13 | /// Interaction logic for WindowUsingItemsSourceProperty.xaml
14 | ///
15 | public partial class WindowUsingItemsSourceProperty : Window
16 | {
17 | private IEnumerable _fixedItems, _dynamicItems;
18 | private int count = 4;
19 |
20 | public WindowUsingItemsSourceProperty()
21 | {
22 | InitializeComponent();
23 |
24 | // when adding new items, you MUST implement the NewTabItem event to provide the object for the content of the tabitem,
25 | // otherwise an exception will be thrown
26 | tabControl.NewTabItem +=
27 | delegate(object sender, NewTabItemEventArgs e)
28 | {
29 | // return a new MyObject to be used as the content of the new tabItem
30 | MyObject myObject = new MyObject { Header = "Tab Item " + count, Value = count };
31 | e.Content = myObject;
32 | count++;
33 | };
34 | }
35 |
36 | ///
37 | /// List that the TabControl's ItemsSource property is bound to
38 | ///
39 | public IEnumerable FixedObjects
40 | {
41 | get
42 | {
43 | if (_fixedItems == null)
44 | {
45 | _fixedItems = new[]
46 | {
47 | new MyObject {Value = 1, Header = "Tab Item 1"},
48 | new MyObject {Value = 2, Header = "Tab Item 2"},
49 | new MyObject {Value = 3, Header = "Tab Item 3"},
50 | };
51 | }
52 |
53 | return _fixedItems;
54 | }
55 | }
56 | ///
57 | /// List that the TabControl's ItemsSource property is bound to
58 | ///
59 | public IEnumerable DynamicObjects
60 | {
61 | get
62 | {
63 | if (_dynamicItems == null)
64 | {
65 | _dynamicItems = new ObservableCollection
66 | {
67 | new MyObject {Value = 1, Header = "Tab Item 1"},
68 | new MyObject {Value = 2, Header = "Tab Item 2"},
69 | new MyObject {Value = 3, Header = "Tab Item 3"},
70 | };
71 | }
72 |
73 | return _dynamicItems;
74 | }
75 | }
76 |
77 | private void DynamicRadioButton_Checked(object sender, RoutedEventArgs e)
78 | {
79 | if (tabControl != null)
80 | tabControl.ItemsSource = DynamicObjects;
81 | }
82 |
83 | private void FixedRadioButton_Checked(object sender, RoutedEventArgs e)
84 | {
85 | if (tabControl != null)
86 | tabControl.ItemsSource = FixedObjects;
87 | }
88 |
89 | private void TextBox_GotFocus(object sender, RoutedEventArgs e)
90 | {
91 | TextBox tb = (sender as TextBox);
92 | if (tb != null)
93 | tb.SelectAll();
94 | }
95 | }
96 |
97 | ///
98 | /// a simple class to use as the ItemsSource for the tabcontrol
99 | ///
100 | public class MyObject : INotifyPropertyChanged
101 | {
102 | private string _header;
103 | private int _value;
104 |
105 | ///
106 | /// Header Property
107 | ///
108 | public string Header
109 | {
110 | get { return _header; }
111 | set
112 | {
113 | if (_header != value)
114 | {
115 | _header = value;
116 | OnPropertyChanged("Header");
117 | }
118 | }
119 | }
120 |
121 | ///
122 | /// Value Property
123 | ///
124 | public int Value
125 | {
126 | get { return _value; }
127 | set
128 | {
129 | if (_value != value)
130 | {
131 | _value = value;
132 | OnPropertyChanged("Value");
133 | }
134 | }
135 | }
136 |
137 | #region INotifyPropertyChanged Members
138 |
139 | public event PropertyChangedEventHandler PropertyChanged;
140 |
141 | #endregion
142 |
143 | private void OnPropertyChanged(string propName)
144 | {
145 | PropertyChangedEventHandler propertyChanged = PropertyChanged;
146 | if (propertyChanged != null) propertyChanged(this, new PropertyChangedEventArgs(propName));
147 | }
148 | }
149 | }
--------------------------------------------------------------------------------
/TabControl/Wpf.TabControl.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 10.00
3 | # Visual Studio 2008
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test", "Test\Test.csproj", "{25344CF3-B7AA-4C75-B0EE-7C92ACFF3A9F}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wpf.TabControl", "TabControl\Wpf.TabControl.csproj", "{509D06BD-AEB6-4E92-8E5B-21188EBDBD9C}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {25344CF3-B7AA-4C75-B0EE-7C92ACFF3A9F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {25344CF3-B7AA-4C75-B0EE-7C92ACFF3A9F}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {25344CF3-B7AA-4C75-B0EE-7C92ACFF3A9F}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {25344CF3-B7AA-4C75-B0EE-7C92ACFF3A9F}.Release|Any CPU.Build.0 = Release|Any CPU
18 | {509D06BD-AEB6-4E92-8E5B-21188EBDBD9C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {509D06BD-AEB6-4E92-8E5B-21188EBDBD9C}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {509D06BD-AEB6-4E92-8E5B-21188EBDBD9C}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {509D06BD-AEB6-4E92-8E5B-21188EBDBD9C}.Release|Any CPU.Build.0 = Release|Any CPU
22 | EndGlobalSection
23 | GlobalSection(SolutionProperties) = preSolution
24 | HideSolutionNode = FALSE
25 | EndGlobalSection
26 | EndGlobal
27 |
--------------------------------------------------------------------------------