From 5b6bcdc0ec7be9e7f6626876b80ab24b59d46410 Mon Sep 17 00:00:00 2001 From: Jan Karger Date: Mon, 15 Jan 2018 16:43:54 +0100 Subject: [PATCH 1/2] - Remove internal close command and use the CloseTabItemAction - Clear style and template for TabItem to prevent nasty binding expression errors - Allow TabControlHelper dependency properties for TabItem too (brushes and Underlined property) --- .../Actions/CloseTabItemAction.cs | 61 +++++++++++-------- .../Controls/Helper/TabControlHelper.cs | 57 +++++++++++++++-- .../Controls/MetroTabControl.cs | 2 + .../Controls/MetroTabItem.cs | 40 ------------ .../Styles/Controls.TabControl.xaml | 27 ++++---- .../MahApps.Metro/Themes/MetroTabItem.xaml | 33 +++++----- 6 files changed, 127 insertions(+), 93 deletions(-) diff --git a/src/MahApps.Metro/MahApps.Metro.Shared/Actions/CloseTabItemAction.cs b/src/MahApps.Metro/MahApps.Metro.Shared/Actions/CloseTabItemAction.cs index 2d12379f0c..516e19787b 100644 --- a/src/MahApps.Metro/MahApps.Metro.Shared/Actions/CloseTabItemAction.cs +++ b/src/MahApps.Metro/MahApps.Metro.Shared/Actions/CloseTabItemAction.cs @@ -117,35 +117,47 @@ protected override void Invoke(object parameter) } } - var closeAction = - new Action( - () => - { - // TODO Raise a closing event to cancel this action - - if (tabControl.ItemsSource == null) - { - // if the list is hard-coded (i.e. has no ItemsSource) - // then we remove the item from the collection - tabControl.Items.Remove(tabItem); - } - else + if (tabControl is MetroTabControl && tabItem is MetroTabItem) + { + // run the command handler for the TabControl + // see #555 + tabControl.BeginInvoke(() => ((MetroTabControl)tabControl).CloseThisTabItem((MetroTabItem)tabItem)); + } + else + { + var closeAction = + new Action( + () => { - // if ItemsSource is something we cannot work with, bail out - var collection = tabControl.ItemsSource as IList; - if (collection == null) + // TODO Raise a closing event to cancel this action + + if (tabControl.ItemsSource == null) { - return; + // if the list is hard-coded (i.e. has no ItemsSource) + // then we remove the item from the collection + tabItem.ClearStyle(); + tabControl.Items.Remove(tabItem); } - // find the item and kill it (I mean, remove it) - var item2Remove = collection.OfType().FirstOrDefault(item => tabItem == item || tabItem.DataContext == item); - if (item2Remove != null) + else { - collection.Remove(item2Remove); + // if ItemsSource is something we cannot work with, bail out + var collection = tabControl.ItemsSource as IList; + if (collection == null) + { + return; + } + + // find the item and kill it (I mean, remove it) + var item2Remove = collection.OfType().FirstOrDefault(item => tabItem == item || tabItem.DataContext == item); + if (item2Remove != null) + { + tabItem.ClearStyle(); + collection.Remove(item2Remove); + } } - } - }); - this.BeginInvoke(closeAction); + }); + this.BeginInvoke(closeAction); + } } private static void OnCommandChanged(CloseTabItemAction action, DependencyPropertyChangedEventArgs e) @@ -180,6 +192,7 @@ private void EnableDisableElement() { return; } + var command = this.Command; this.AssociatedObject.IsEnabled = command == null || command.CanExecute(this.GetCommandParameter()); } diff --git a/src/MahApps.Metro/MahApps.Metro.Shared/Controls/Helper/TabControlHelper.cs b/src/MahApps.Metro/MahApps.Metro.Shared/Controls/Helper/TabControlHelper.cs index 35f2b486c5..3260875448 100644 --- a/src/MahApps.Metro/MahApps.Metro.Shared/Controls/Helper/TabControlHelper.cs +++ b/src/MahApps.Metro/MahApps.Metro.Shared/Controls/Helper/TabControlHelper.cs @@ -21,6 +21,35 @@ public enum UnderlinedType public static class TabControlHelper { + /// Sets the Style and Template property to null. + /// + /// Removing a TabItem in code behind can produce such nasty output + /// System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TabControl', AncestorLevel='1''. BindingExpression:Path=Background; DataItem=null; target element is 'TabItem' (Name=''); target property is 'Background' (type 'Brush') + /// or + /// System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TabControl', AncestorLevel='1''. BindingExpression:Path=(0); DataItem=null; target element is 'TabItem' (Name=''); target property is 'UnderlineBrush' (type 'Brush') + /// + /// This is a timing problem in WPF of the binding mechanism itself. + /// + /// To avoid this, we can set the Style and Template to null. + public static void ClearStyle(this TabItem tabItem) + { + if (null == tabItem) + { + return; + } + + // Removing a TabItem in code behind can produce such nasty output + // System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TabControl', AncestorLevel='1''. BindingExpression:Path=Background; DataItem=null; target element is 'TabItem' (Name=''); target property is 'Background' (type 'Brush') + // or + // System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TabControl', AncestorLevel='1''. BindingExpression:Path=(0); DataItem=null; target element is 'TabItem' (Name=''); target property is 'UnderlineBrush' (type 'Brush') + // + // This is a timing problem in WPF of the binding mechanism itself. + // + // To avoid this, we can set the Style and Template to null. + tabItem.Template = null; + tabItem.Style = null; + } + /// /// Identifies the CloseButtonEnabled attached property. /// @@ -151,11 +180,15 @@ public static void SetIsUnderlined(UIElement element, bool value) [Category(AppName.MahApps)] [AttachedPropertyBrowsableForType(typeof(TabControl))] + [AttachedPropertyBrowsableForType(typeof(TabItem))] public static UnderlinedType GetUnderlined(UIElement element) { return (UnderlinedType)element.GetValue(UnderlinedProperty); } + [Category(AppName.MahApps)] + [AttachedPropertyBrowsableForType(typeof(TabControl))] + [AttachedPropertyBrowsableForType(typeof(TabItem))] public static void SetUnderlined(UIElement element, UnderlinedType value) { element.SetValue(UnderlinedProperty, value); @@ -168,15 +201,19 @@ public static void SetUnderlined(UIElement element, UnderlinedType value) DependencyProperty.RegisterAttached("UnderlineBrush", typeof(Brush), typeof(TabControlHelper), - new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender)); + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits)); [Category(AppName.MahApps)] [AttachedPropertyBrowsableForType(typeof(TabControl))] + [AttachedPropertyBrowsableForType(typeof(TabItem))] public static Brush GetUnderlineBrush(UIElement element) { return (Brush)element.GetValue(UnderlineBrushProperty); } + [Category(AppName.MahApps)] + [AttachedPropertyBrowsableForType(typeof(TabControl))] + [AttachedPropertyBrowsableForType(typeof(TabItem))] public static void SetUnderlineBrush(UIElement element, Brush value) { element.SetValue(UnderlineBrushProperty, value); @@ -189,15 +226,19 @@ public static void SetUnderlineBrush(UIElement element, Brush value) DependencyProperty.RegisterAttached("UnderlineSelectedBrush", typeof(Brush), typeof(TabControlHelper), - new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender)); + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits)); [Category(AppName.MahApps)] [AttachedPropertyBrowsableForType(typeof(TabControl))] + [AttachedPropertyBrowsableForType(typeof(TabItem))] public static Brush GetUnderlineSelectedBrush(UIElement element) { return (Brush)element.GetValue(UnderlineSelectedBrushProperty); } + [Category(AppName.MahApps)] + [AttachedPropertyBrowsableForType(typeof(TabControl))] + [AttachedPropertyBrowsableForType(typeof(TabItem))] public static void SetUnderlineSelectedBrush(UIElement element, Brush value) { element.SetValue(UnderlineSelectedBrushProperty, value); @@ -210,15 +251,19 @@ public static void SetUnderlineSelectedBrush(UIElement element, Brush value) DependencyProperty.RegisterAttached("UnderlineMouseOverBrush", typeof(Brush), typeof(TabControlHelper), - new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender)); + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits)); [Category(AppName.MahApps)] [AttachedPropertyBrowsableForType(typeof(TabControl))] + [AttachedPropertyBrowsableForType(typeof(TabItem))] public static Brush GetUnderlineMouseOverBrush(UIElement element) { return (Brush)element.GetValue(UnderlineMouseOverBrushProperty); } + [Category(AppName.MahApps)] + [AttachedPropertyBrowsableForType(typeof(TabControl))] + [AttachedPropertyBrowsableForType(typeof(TabItem))] public static void SetUnderlineMouseOverBrush(UIElement element, Brush value) { element.SetValue(UnderlineMouseOverBrushProperty, value); @@ -231,15 +276,19 @@ public static void SetUnderlineMouseOverBrush(UIElement element, Brush value) DependencyProperty.RegisterAttached("UnderlineMouseOverSelectedBrush", typeof(Brush), typeof(TabControlHelper), - new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender)); + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.Inherits)); [Category(AppName.MahApps)] [AttachedPropertyBrowsableForType(typeof(TabControl))] + [AttachedPropertyBrowsableForType(typeof(TabItem))] public static Brush GetUnderlineMouseOverSelectedBrush(UIElement element) { return (Brush)element.GetValue(UnderlineMouseOverSelectedBrushProperty); } + [Category(AppName.MahApps)] + [AttachedPropertyBrowsableForType(typeof(TabControl))] + [AttachedPropertyBrowsableForType(typeof(TabItem))] public static void SetUnderlineMouseOverSelectedBrush(UIElement element, Brush value) { element.SetValue(UnderlineMouseOverSelectedBrushProperty, value); diff --git a/src/MahApps.Metro/MahApps.Metro.Shared/Controls/MetroTabControl.cs b/src/MahApps.Metro/MahApps.Metro.Shared/Controls/MetroTabControl.cs index a6bf3b8066..3cf0aef535 100644 --- a/src/MahApps.Metro/MahApps.Metro.Shared/Controls/MetroTabControl.cs +++ b/src/MahApps.Metro/MahApps.Metro.Shared/Controls/MetroTabControl.cs @@ -145,6 +145,7 @@ internal void CloseThisTabItem([NotNull] MetroTabItem tabItem) { // if the list is hard-coded (i.e. has no ItemsSource) // then we remove the item from the collection + tabItem.ClearStyle(); this.Items.Remove(tabItem); } else @@ -160,6 +161,7 @@ internal void CloseThisTabItem([NotNull] MetroTabItem tabItem) var item2Remove = collection.OfType().FirstOrDefault(item => tabItem == item || tabItem.DataContext == item); if (item2Remove != null) { + tabItem.ClearStyle(); collection.Remove(item2Remove); } } diff --git a/src/MahApps.Metro/MahApps.Metro.Shared/Controls/MetroTabItem.cs b/src/MahApps.Metro/MahApps.Metro.Shared/Controls/MetroTabItem.cs index 44d260036f..20a1f6c20d 100644 --- a/src/MahApps.Metro/MahApps.Metro.Shared/Controls/MetroTabItem.cs +++ b/src/MahApps.Metro/MahApps.Metro.Shared/Controls/MetroTabItem.cs @@ -12,32 +12,6 @@ public class MetroTabItem : TabItem public MetroTabItem() { DefaultStyleKey = typeof(MetroTabItem); - this.InternalCloseTabCommand = new CloseCommand(InternalCloseTabCommandCanExecute, InternalCloseTabCommandExecuteAction); - } - - private void InternalCloseTabCommandExecuteAction(object o) - { - var closeTabCommand = this.CloseTabCommand; - if (closeTabCommand != null) - { - var closeTabCommandParameter = this.CloseTabCommandParameter ?? this; - if (closeTabCommand.CanExecute(closeTabCommandParameter)) - { - // force the command handler to run - closeTabCommand.Execute(closeTabCommandParameter); - } - } - - var owningTabControl = this.TryFindParent(); - // run the command handler for the TabControl - // see #555 - owningTabControl?.BeginInvoke(() => owningTabControl.CloseThisTabItem(this)); - } - - private bool InternalCloseTabCommandCanExecute(object o) - { - var closeTabCommand = this.CloseTabCommand; - return closeTabCommand == null || closeTabCommand.CanExecute(this.CloseTabCommandParameter ?? this); } public static readonly DependencyProperty CloseButtonEnabledProperty = @@ -55,20 +29,6 @@ public bool CloseButtonEnabled set { SetValue(CloseButtonEnabledProperty, value); } } - internal static readonly DependencyProperty InternalCloseTabCommandProperty = - DependencyProperty.Register("InternalCloseTabCommand", - typeof(ICommand), - typeof(MetroTabItem)); - - /// - /// Gets/sets the command that is executed when the Close Button is clicked. - /// - internal ICommand InternalCloseTabCommand - { - get { return (ICommand)GetValue(InternalCloseTabCommandProperty); } - set { SetValue(InternalCloseTabCommandProperty, value); } - } - public static readonly DependencyProperty CloseTabCommandProperty = DependencyProperty.Register("CloseTabCommand", typeof(ICommand), diff --git a/src/MahApps.Metro/MahApps.Metro/Styles/Controls.TabControl.xaml b/src/MahApps.Metro/MahApps.Metro/Styles/Controls.TabControl.xaml index fc782f0ccf..dcc07f2f72 100644 --- a/src/MahApps.Metro/MahApps.Metro/Styles/Controls.TabControl.xaml +++ b/src/MahApps.Metro/MahApps.Metro/Styles/Controls.TabControl.xaml @@ -103,6 +103,11 @@ + + + + + @@ -155,7 +160,7 @@ HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="{TemplateBinding Background}" - BorderBrush="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TabControl}}, Path=(Controls:TabControlHelper.UnderlineBrush), Mode=OneWay}" + BorderBrush="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=(Controls:TabControlHelper.UnderlineBrush), Mode=OneWay}" LineExtent="3" LineThickness="2" Placement="Bottom" @@ -208,20 +213,20 @@ - + - + - + - + @@ -229,14 +234,14 @@ - + - + @@ -244,12 +249,12 @@ - + - + @@ -257,7 +262,7 @@ - + @@ -270,7 +275,7 @@ - + diff --git a/src/MahApps.Metro/MahApps.Metro/Themes/MetroTabItem.xaml b/src/MahApps.Metro/MahApps.Metro/Themes/MetroTabItem.xaml index 1f614f14ba..06e071fe43 100644 --- a/src/MahApps.Metro/MahApps.Metro/Themes/MetroTabItem.xaml +++ b/src/MahApps.Metro/MahApps.Metro/Themes/MetroTabItem.xaml @@ -1,7 +1,9 @@  + xmlns:Converters="clr-namespace:MahApps.Metro.Converters" + xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"> @@ -61,11 +63,14 @@ Margin="{TemplateBinding CloseButtonMargin}" VerticalAlignment="Top" Background="{DynamicResource GrayNormalBrush}" - Command="{TemplateBinding InternalCloseTabCommand}" - CommandParameter="{TemplateBinding CloseTabCommandParameter}" IsTabStop="False" Style="{DynamicResource ChromelessButtonStyle}" Visibility="Collapsed"> + + + + + - + - + - + @@ -176,7 +181,7 @@ - + @@ -184,14 +189,14 @@ - + - + @@ -199,7 +204,7 @@ - + @@ -214,7 +219,7 @@ - + @@ -222,7 +227,7 @@ - + @@ -235,7 +240,7 @@ - + From fa49153e3411593add68b71a4f6f1b396118a7eb Mon Sep 17 00:00:00 2001 From: Jan Karger Date: Mon, 15 Jan 2018 18:05:35 +0100 Subject: [PATCH 2/2] TabControlHelper.Underlined makes only sense for TabControl --- .../Controls/Helper/TabControlHelper.cs | 10 ---------- .../MahApps.Metro/Styles/Controls.TabControl.xaml | 15 +++++++-------- .../MahApps.Metro/Themes/MetroTabItem.xaml | 14 +++++++------- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/src/MahApps.Metro/MahApps.Metro.Shared/Controls/Helper/TabControlHelper.cs b/src/MahApps.Metro/MahApps.Metro.Shared/Controls/Helper/TabControlHelper.cs index 3260875448..48fc303b51 100644 --- a/src/MahApps.Metro/MahApps.Metro.Shared/Controls/Helper/TabControlHelper.cs +++ b/src/MahApps.Metro/MahApps.Metro.Shared/Controls/Helper/TabControlHelper.cs @@ -38,14 +38,6 @@ public static void ClearStyle(this TabItem tabItem) return; } - // Removing a TabItem in code behind can produce such nasty output - // System.Windows.Data Warning: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TabControl', AncestorLevel='1''. BindingExpression:Path=Background; DataItem=null; target element is 'TabItem' (Name=''); target property is 'Background' (type 'Brush') - // or - // System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TabControl', AncestorLevel='1''. BindingExpression:Path=(0); DataItem=null; target element is 'TabItem' (Name=''); target property is 'UnderlineBrush' (type 'Brush') - // - // This is a timing problem in WPF of the binding mechanism itself. - // - // To avoid this, we can set the Style and Template to null. tabItem.Template = null; tabItem.Style = null; } @@ -180,7 +172,6 @@ public static void SetIsUnderlined(UIElement element, bool value) [Category(AppName.MahApps)] [AttachedPropertyBrowsableForType(typeof(TabControl))] - [AttachedPropertyBrowsableForType(typeof(TabItem))] public static UnderlinedType GetUnderlined(UIElement element) { return (UnderlinedType)element.GetValue(UnderlinedProperty); @@ -188,7 +179,6 @@ public static UnderlinedType GetUnderlined(UIElement element) [Category(AppName.MahApps)] [AttachedPropertyBrowsableForType(typeof(TabControl))] - [AttachedPropertyBrowsableForType(typeof(TabItem))] public static void SetUnderlined(UIElement element, UnderlinedType value) { element.SetValue(UnderlinedProperty, value); diff --git a/src/MahApps.Metro/MahApps.Metro/Styles/Controls.TabControl.xaml b/src/MahApps.Metro/MahApps.Metro/Styles/Controls.TabControl.xaml index dcc07f2f72..733e6336ec 100644 --- a/src/MahApps.Metro/MahApps.Metro/Styles/Controls.TabControl.xaml +++ b/src/MahApps.Metro/MahApps.Metro/Styles/Controls.TabControl.xaml @@ -107,7 +107,6 @@ - @@ -213,13 +212,13 @@ - + - + - + @@ -234,14 +233,14 @@ - + - + @@ -254,7 +253,7 @@ - + @@ -262,7 +261,7 @@ - + diff --git a/src/MahApps.Metro/MahApps.Metro/Themes/MetroTabItem.xaml b/src/MahApps.Metro/MahApps.Metro/Themes/MetroTabItem.xaml index 06e071fe43..88b34baa3f 100644 --- a/src/MahApps.Metro/MahApps.Metro/Themes/MetroTabItem.xaml +++ b/src/MahApps.Metro/MahApps.Metro/Themes/MetroTabItem.xaml @@ -153,13 +153,13 @@ - + - + - + @@ -189,14 +189,14 @@ - + - + @@ -219,7 +219,7 @@ - + @@ -227,7 +227,7 @@ - +