diff --git a/MahApps.Metro/Controls/MetroAnimatedSingleRowTabControl.cs b/MahApps.Metro/Controls/MetroAnimatedSingleRowTabControl.cs index 27895ccdad..cdb1ed831f 100644 --- a/MahApps.Metro/Controls/MetroAnimatedSingleRowTabControl.cs +++ b/MahApps.Metro/Controls/MetroAnimatedSingleRowTabControl.cs @@ -1,38 +1,15 @@ using System.Windows; using System.Windows.Controls; +using System.Windows.Data; using System.Windows.Input; namespace MahApps.Metro.Controls { - public class MetroAnimatedSingleRowTabControl : TabControl + public class MetroAnimatedSingleRowTabControl : BaseMetroTabControl { public MetroAnimatedSingleRowTabControl() { DefaultStyleKey = typeof(MetroAnimatedSingleRowTabControl); } - - public Thickness TabStripMargin - { - get { return (Thickness)GetValue(TabStripMarginProperty); } - set { SetValue(TabStripMarginProperty, value); } - } - - // Using a DependencyProperty as the backing store for TabStripMargin. This enables animation, styling, binding, etc... - public static readonly DependencyProperty TabStripMarginProperty = - DependencyProperty.Register("TabStripMargin", typeof(Thickness), typeof(MetroAnimatedSingleRowTabControl), new PropertyMetadata(new Thickness(0))); - - protected override DependencyObject GetContainerForItemOverride() - { - return new MetroTabItem() { OwningTabControl = this }; //Overrides the TabControl's default behavior and returns a MetroTabItem instead of a regular one. - } - - public ICommand CloseTabCommand - { - get { return (ICommand)GetValue(CloseTabCommandProperty); } - set { SetValue(CloseTabCommandProperty, value); } - } - - public static readonly DependencyProperty CloseTabCommandProperty = - DependencyProperty.Register("CloseTabCommand", typeof(ICommand), typeof(MetroAnimatedSingleRowTabControl), new PropertyMetadata(new MahApps.Metro.Controls.MetroTabControl.DefaultCloseTabCommand())); } } diff --git a/MahApps.Metro/Controls/MetroAnimatedTabControl.cs b/MahApps.Metro/Controls/MetroAnimatedTabControl.cs index 03c33f2897..ffc2aae1b7 100644 --- a/MahApps.Metro/Controls/MetroAnimatedTabControl.cs +++ b/MahApps.Metro/Controls/MetroAnimatedTabControl.cs @@ -1,38 +1,15 @@ using System.Windows; using System.Windows.Controls; +using System.Windows.Data; using System.Windows.Input; namespace MahApps.Metro.Controls { - public class MetroAnimatedTabControl : TabControl + public class MetroAnimatedTabControl : BaseMetroTabControl { public MetroAnimatedTabControl() { DefaultStyleKey = typeof(MetroAnimatedTabControl); } - - public Thickness TabStripMargin - { - get { return (Thickness)GetValue(TabStripMarginProperty); } - set { SetValue(TabStripMarginProperty, value); } - } - - // Using a DependencyProperty as the backing store for TabStripMargin. This enables animation, styling, binding, etc... - public static readonly DependencyProperty TabStripMarginProperty = - DependencyProperty.Register("TabStripMargin", typeof(Thickness), typeof(MetroAnimatedTabControl), new PropertyMetadata(new Thickness(0))); - - protected override DependencyObject GetContainerForItemOverride() - { - return new MetroTabItem() { OwningTabControl = this }; //Overrides the TabControl's default behavior and returns a MetroTabItem instead of a regular one. - } - - public ICommand CloseTabCommand - { - get { return (ICommand)GetValue(CloseTabCommandProperty); } - set { SetValue(CloseTabCommandProperty, value); } - } - - public static readonly DependencyProperty CloseTabCommandProperty = - DependencyProperty.Register("CloseTabCommand", typeof(ICommand), typeof(MetroAnimatedTabControl), new PropertyMetadata(new MahApps.Metro.Controls.MetroTabControl.DefaultCloseTabCommand())); } } diff --git a/MahApps.Metro/Controls/MetroTabControl.cs b/MahApps.Metro/Controls/MetroTabControl.cs index d7ef90fde8..9640c56f76 100644 --- a/MahApps.Metro/Controls/MetroTabControl.cs +++ b/MahApps.Metro/Controls/MetroTabControl.cs @@ -1,15 +1,49 @@ -using System.Windows; +using System; +using System.ComponentModel; +using System.Windows; using System.Windows.Controls; +using System.Windows.Data; using System.Windows.Input; namespace MahApps.Metro.Controls { - public class MetroTabControl : TabControl + public class MetroTabControl : BaseMetroTabControl { public MetroTabControl() + : base() { DefaultStyleKey = typeof(MetroTabControl); } + } + + public abstract class BaseMetroTabControl : TabControl + { + public BaseMetroTabControl() + { + InternalCloseTabCommand = new DefaultCloseTabCommand(this); + + this.Loaded += BaseMetroTabControl_Loaded; + this.Unloaded += BaseMetroTabControl_Unloaded; + } + + void BaseMetroTabControl_Unloaded(object sender, RoutedEventArgs e) + { + this.Loaded -= BaseMetroTabControl_Loaded; + this.Unloaded -= BaseMetroTabControl_Unloaded; + } + + void BaseMetroTabControl_Loaded(object sender, RoutedEventArgs e) + { + this.Loaded += BaseMetroTabControl_Loaded; + + //Ensure each tabitem knows what the owning tab is. + + if (ItemsSource == null) + foreach (TabItem item in Items) + if (item is MetroTabItem) + ((MetroTabItem)item).OwningTabControl = this; + + } public Thickness TabStripMargin { @@ -19,13 +53,26 @@ public Thickness TabStripMargin // Using a DependencyProperty as the backing store for TabStripMargin. This enables animation, styling, binding, etc... public static readonly DependencyProperty TabStripMarginProperty = - DependencyProperty.Register("TabStripMargin", typeof(Thickness), typeof(MetroTabControl), new PropertyMetadata(new Thickness(0))); + DependencyProperty.Register("TabStripMargin", typeof(Thickness), typeof(BaseMetroTabControl), new PropertyMetadata(new Thickness(0))); + + protected override bool IsItemItsOwnContainerOverride(object item) + { + return item is TabItem; + } protected override DependencyObject GetContainerForItemOverride() { return new MetroTabItem() { OwningTabControl = this }; //Overrides the TabControl's default behavior and returns a MetroTabItem instead of a regular one. } + protected override void PrepareContainerForItemOverride(DependencyObject element, object item) + { + if (element != item) + element.SetCurrentValue(MetroTabItem.DataContextProperty, item); //dont want to set the datacontext to itself. + + base.PrepareContainerForItemOverride(element, item); + } + public ICommand CloseTabCommand { get { return (ICommand)GetValue(CloseTabCommandProperty); } @@ -33,10 +80,51 @@ public ICommand CloseTabCommand } public static readonly DependencyProperty CloseTabCommandProperty = - DependencyProperty.Register("CloseTabCommand", typeof(ICommand), typeof(MetroTabControl), new PropertyMetadata(new DefaultCloseTabCommand())); + DependencyProperty.Register("CloseTabCommand", typeof(ICommand), typeof(BaseMetroTabControl), new PropertyMetadata(null)); + + internal ICommand InternalCloseTabCommand + { + get { return (ICommand)GetValue(InternalCloseTabCommandProperty); } + set { SetValue(InternalCloseTabCommandProperty, value); } + } + private static readonly DependencyProperty InternalCloseTabCommandProperty = + DependencyProperty.Register("InternalCloseTabCommand", typeof(ICommand), typeof(BaseMetroTabControl), new PropertyMetadata(null)); + + + public delegate void TabItemClosingEventHandler(object sender, TabItemClosingEventArgs e); + public event TabItemClosingEventHandler TabItemClosingEvent; + + internal bool RaiseTabItemClosingEvent(MetroTabItem closingItem) + { + foreach (TabItemClosingEventHandler subHandler in TabItemClosingEvent.GetInvocationList()) + { + TabItemClosingEventArgs args = new TabItemClosingEventArgs(closingItem); + subHandler.Invoke(this, args); + if (args.Cancel) + return true; + } + + return false; + } + + public class TabItemClosingEventArgs : CancelEventArgs + { + internal TabItemClosingEventArgs(MetroTabItem item) + { + ClosingTabItem = item; + } + + public MetroTabItem ClosingTabItem { get; private set; } + } internal class DefaultCloseTabCommand : ICommand { + private BaseMetroTabControl owner = null; + internal DefaultCloseTabCommand(BaseMetroTabControl Owner) + { + owner = Owner; + } + public bool CanExecute(object parameter) { return true; @@ -46,11 +134,23 @@ public bool CanExecute(object parameter) public void Execute(object parameter) { - if (parameter != null && parameter is MetroTabItem) + if (parameter != null) { - var tabItem = (MetroTabItem)parameter; + Tuple paramData = (Tuple)parameter; - ((TabControl)tabItem.OwningTabControl).Items.Remove(tabItem); + if (owner.CloseTabCommand != null && !(paramData.Item1 is TextBlock)) //best way I could tell if the tabitem is from databinding or not. + owner.CloseTabCommand.Execute(paramData.Item1); + else + { + if (paramData.Item2 is MetroTabItem) + { + if (!owner.RaiseTabItemClosingEvent((MetroTabItem)paramData.Item2)) //Allows the user to cancel closing a tab. + { + var tabItem = (MetroTabItem)paramData.Item2; + owner.Items.Remove(tabItem); + } + } + } } } } diff --git a/MahApps.Metro/Controls/MetroTabItem.cs b/MahApps.Metro/Controls/MetroTabItem.cs index 018b338193..4e870168d6 100644 --- a/MahApps.Metro/Controls/MetroTabItem.cs +++ b/MahApps.Metro/Controls/MetroTabItem.cs @@ -1,6 +1,7 @@ using System; using System.Windows; using System.Windows.Controls; +using System.Windows.Media; namespace MahApps.Metro.Controls { @@ -9,6 +10,39 @@ public class MetroTabItem : TabItem public MetroTabItem() { DefaultStyleKey = typeof(MetroTabItem); + this.Unloaded += MetroTabItem_Unloaded; + this.Loaded += MetroTabItem_Loaded; + } + + void MetroTabItem_Loaded(object sender, RoutedEventArgs e) + { + if (closeButton != null && closeButtonClickUnloaded) + { + closeButton.Click += closeButton_Click; + + closeButtonClickUnloaded = false; + } + } + + void MetroTabItem_Unloaded(object sender, RoutedEventArgs e) + { + this.Unloaded -= MetroTabItem_Unloaded; + closeButton.Click -= closeButton_Click; + + closeButtonClickUnloaded = true; + } + + private delegate void EmptyDelegate(); + ~MetroTabItem() + { + try + { + Application.Current.Dispatcher.Invoke(new EmptyDelegate(() => + { + this.Loaded -= MetroTabItem_Loaded; + })); + } + catch (Exception) { } } public double HeaderFontSize @@ -54,17 +88,68 @@ public bool CloseButtonEnabled internal Button closeButton = null; internal Thickness newButtonMargin; internal Label rootLabel = null; + private bool closeButtonClickUnloaded = false; public override void OnApplyTemplate() { base.OnApplyTemplate(); + bool closeButtonNullBefore = closeButton == null; //TabControl's multi-loading/unloading issue + closeButton = GetTemplateChild("PART_CloseButton") as Button; closeButton.Margin = newButtonMargin; + if (closeButtonNullBefore) + closeButton.Click += closeButton_Click; + + + closeButton.Visibility = CloseButtonEnabled ? System.Windows.Visibility.Visible : System.Windows.Visibility.Hidden; + rootLabel = GetTemplateChild("root") as Label; } - public TabControl OwningTabControl { get; internal set; } + void closeButton_Click(object sender, RoutedEventArgs e) + { + //Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}, Path=InternalCloseTabCommand + OwningTabControl.InternalCloseTabCommand.Execute(new Tuple(this.Content, this)); + } + + public BaseMetroTabControl OwningTabControl { get; internal set; } + + protected override void OnSelected(RoutedEventArgs e) + { + if (closeButton != null) + if (CloseButtonEnabled) + closeButton.Visibility = System.Windows.Visibility.Visible; + + base.OnSelected(e); + } + + protected override void OnUnselected(RoutedEventArgs e) + { + if (closeButton != null) + closeButton.Visibility = System.Windows.Visibility.Hidden; + + base.OnUnselected(e); + } + + protected override void OnMouseEnter(System.Windows.Input.MouseEventArgs e) + { + if (closeButton != null) + if (CloseButtonEnabled) + closeButton.Visibility = System.Windows.Visibility.Visible; + + base.OnMouseEnter(e); + } + + protected override void OnMouseLeave(System.Windows.Input.MouseEventArgs e) + { + if (!this.IsSelected) + if (closeButton != null) + if (CloseButtonEnabled) + closeButton.Visibility = System.Windows.Visibility.Hidden; + + base.OnMouseLeave(e); + } } } diff --git a/MahApps.Metro/Themes/MetroAnimatedSingleRowTabControl.xaml b/MahApps.Metro/Themes/MetroAnimatedSingleRowTabControl.xaml index a69e2a94e2..ba49ff1a1e 100644 --- a/MahApps.Metro/Themes/MetroAnimatedSingleRowTabControl.xaml +++ b/MahApps.Metro/Themes/MetroAnimatedSingleRowTabControl.xaml @@ -75,17 +75,17 @@ - - + + - - + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MahApps.Metro/Themes/MetroTabControl.xaml b/MahApps.Metro/Themes/MetroTabControl.xaml index 50e1a8acac..af1a3cfda3 100644 --- a/MahApps.Metro/Themes/MetroTabControl.xaml +++ b/MahApps.Metro/Themes/MetroTabControl.xaml @@ -23,6 +23,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MahApps.Metro/Themes/MetroTabItem.xaml b/MahApps.Metro/Themes/MetroTabItem.xaml index 7e2b7ef498..f400ae09a1 100644 --- a/MahApps.Metro/Themes/MetroTabItem.xaml +++ b/MahApps.Metro/Themes/MetroTabItem.xaml @@ -22,9 +22,7 @@