Skip to content

Commit

Permalink
Merge pull request #520 from Amrykid/metro-tabitem-patch2
Browse files Browse the repository at this point in the history
[RFC] Metro TabItem/TabControl Patch #2
  • Loading branch information
AzureKitsune committed Jun 11, 2013
2 parents c1650b6 + 02e9cb3 commit 3e5273d
Show file tree
Hide file tree
Showing 9 changed files with 273 additions and 68 deletions.
27 changes: 2 additions & 25 deletions MahApps.Metro/Controls/MetroAnimatedSingleRowTabControl.cs
Original file line number Diff line number Diff line change
@@ -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()));
}
}
27 changes: 2 additions & 25 deletions MahApps.Metro/Controls/MetroAnimatedTabControl.cs
Original file line number Diff line number Diff line change
@@ -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()));
}
}
114 changes: 107 additions & 7 deletions MahApps.Metro/Controls/MetroTabControl.cs
Original file line number Diff line number Diff line change
@@ -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
{
Expand All @@ -19,24 +53,78 @@ 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); }
set { SetValue(CloseTabCommandProperty, value); }
}

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;
Expand All @@ -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<object, MetroTabItem> paramData = (Tuple<object, MetroTabItem>)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);
}
}
}
}
}
}
Expand Down
87 changes: 86 additions & 1 deletion MahApps.Metro/Controls/MetroTabItem.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace MahApps.Metro.Controls
{
Expand All @@ -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
Expand Down Expand Up @@ -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<object, MetroTabItem>(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);
}
}
}
Loading

0 comments on commit 3e5273d

Please sign in to comment.