From 1dbd77c44440cf3a67e690493e16dcdde3c9aade Mon Sep 17 00:00:00 2001 From: Charles Weld Date: Sat, 18 Oct 2014 11:17:02 +1100 Subject: [PATCH 01/10] Initial attempt at MetroPopover --- MahApps.Metro/Controls/MetroPopover.cs | 159 ++++++++++++++++++ MahApps.Metro/Controls/MetroWindow.cs | 3 +- MahApps.Metro/Controls/PopoverManager.cs | 158 +++++++++++++++++ .../Controls/PopoverStateChangedEventArgs.cs | 11 ++ MahApps.Metro/MahApps.Metro.NET45.csproj | 7 + MahApps.Metro/MahApps.Metro.csproj | 7 + MahApps.Metro/Themes/Generic.xaml | 1 + MahApps.Metro/Themes/MetroPopover.xaml | 79 +++++++++ .../MetroDemo/ExampleViews/OtherExamples.xaml | 12 ++ .../ExampleViews/OtherExamples.xaml.cs | 8 +- 10 files changed, 443 insertions(+), 2 deletions(-) create mode 100644 MahApps.Metro/Controls/MetroPopover.cs create mode 100644 MahApps.Metro/Controls/PopoverManager.cs create mode 100644 MahApps.Metro/Controls/PopoverStateChangedEventArgs.cs create mode 100644 MahApps.Metro/Themes/MetroPopover.xaml diff --git a/MahApps.Metro/Controls/MetroPopover.cs b/MahApps.Metro/Controls/MetroPopover.cs new file mode 100644 index 0000000000..b17ddf7734 --- /dev/null +++ b/MahApps.Metro/Controls/MetroPopover.cs @@ -0,0 +1,159 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using System.Windows.Markup; +using System.Windows.Media.Animation; + +namespace MahApps.Metro.Controls +{ + + [ContentProperty("Content")] + [TemplatePart(Name = PART_ContentPresenter, Type = typeof(UIElement))] + public class MetroPopover : Control + { + private const string PART_ContentPresenter = "PART_ContentPresenter"; + + static MetroPopover() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(MetroPopover), new FrameworkPropertyMetadata(typeof(MetroPopover))); + } + + public MetroPopover(UIElement target) + { + Target = target; + } + + public MetroWindow Owner { get; internal set; } + public UIElement Target { get; private set; } + + public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(MetroPopover), new PropertyMetadata(null)); + public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register("IsOpen", typeof(bool), typeof(MetroPopover), new PropertyMetadata(false)); + + public object Content + { + get { return (object)GetValue(ContentProperty); } + set { SetValue(ContentProperty, value); } + } + + public bool IsOpen + { + get { return (bool)GetValue(IsOpenProperty); } + set { SetValue(IsOpenProperty, value); } + } + + public event EventHandler Shown; + public event EventHandler Closed; + + public Task OpenAsync() + { + return WaitForLoadAsync() + .ContinueWith(t => { + OnOpened(); + }, TaskScheduler.FromCurrentSynchronizationContext()); + } + + public Task RequestCloseAsync() + { + if (OnRequestClose()) { + return WaitForCloseAsync() + .ContinueWith(t => { + OnClosed(); + return true; + }, TaskScheduler.FromCurrentSynchronizationContext()); + } + return Task.Factory.StartNew(() => false); + } + + protected virtual void OnOpened() + { + this.IsOpen = true; + if (Shown != null) { + Shown(this, EventArgs.Empty); + } + } + + /// + /// A last chance virtual method for stopping an popover from closing. + /// + /// + protected virtual bool OnRequestClose() + { + return true; //allow the dialog to close. + } + + protected virtual void OnClosed() + { + this.IsOpen = false; + if (Closed != null) { + Closed(this, EventArgs.Empty); + } + + } + + /// + /// Waits for the popover to become ready for interaction. + /// + /// A task that represents the operation and it's status. + Task WaitForLoadAsync() + { + Dispatcher.VerifyAccess(); + + if (this.IsLoaded) return new Task(() => { }); + + if (!false) + this.Opacity = 1.0; //skip the animation + + TaskCompletionSource tcs = new TaskCompletionSource(); + + RoutedEventHandler handler = null; + handler = new RoutedEventHandler((sender, args) => { + this.Loaded -= handler; + + tcs.TrySetResult(null); + }); + + this.Loaded += handler; + + return tcs.Task; + } + + Task WaitForCloseAsync() + { + TaskCompletionSource tcs = new TaskCompletionSource(); + + if (false) { + Storyboard closingStoryboard = this.Resources["PopoverCloseStoryboard"] as Storyboard; + + if (closingStoryboard == null) + throw new InvalidOperationException("Unable to find the dialog closing storyboard. Did you forget to add MetroPopoverDialog.xaml to your merged dictionaries?"); + + EventHandler handler = null; + handler = new EventHandler((sender, args) => { + closingStoryboard.Completed -= handler; + + tcs.TrySetResult(null); + }); + + closingStoryboard = closingStoryboard.Clone(); + + closingStoryboard.Completed += handler; + + closingStoryboard.Begin(this); + } else { + this.Opacity = 0.0; + tcs.TrySetResult(null); //skip the animation + } + + return tcs.Task; + } + + + } +} diff --git a/MahApps.Metro/Controls/MetroWindow.cs b/MahApps.Metro/Controls/MetroWindow.cs index 1817854ecd..7e139187a3 100644 --- a/MahApps.Metro/Controls/MetroWindow.cs +++ b/MahApps.Metro/Controls/MetroWindow.cs @@ -500,7 +500,7 @@ public string WindowTitle { get { return TitleCaps ? Title.ToUpper() : Title; } } - + /// /// Begins to show the MetroWindow's overlay effect. /// @@ -541,6 +541,7 @@ public System.Threading.Tasks.Task ShowOverlayAsync() return tcs.Task; } + /// /// Begins to hide the MetroWindow's overlay effect. /// diff --git a/MahApps.Metro/Controls/PopoverManager.cs b/MahApps.Metro/Controls/PopoverManager.cs new file mode 100644 index 0000000000..6052cf0bf6 --- /dev/null +++ b/MahApps.Metro/Controls/PopoverManager.cs @@ -0,0 +1,158 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; + +namespace MahApps.Metro.Controls +{ + public static class PopoverManager + { + #region Popover adorner + + // see tech.pro/tutorial/856/wpf-tutorial-using-a-visual-collection by Michael Kuehl + + class PopoverAdorner : Adorner + { + readonly MetroWindow _window; + readonly VisualCollection _visuals; + readonly MetroPopover _popover; + + public PopoverAdorner(MetroWindow window, UIElement adornedElement, MetroPopover popover) + : base(adornedElement) + { + _window = window; + _popover = popover; + _visuals = new VisualCollection(this); + _visuals.Add(popover); + + _popover.Closed += OnPopoverClosed; + _window.PreviewMouseDown += OnPreviewMouseDownForWindow; + } + + void OnPreviewMouseDownForWindow(object sender, MouseButtonEventArgs e) + { + var target = e.OriginalSource as DependencyObject; + if (!IsDescendant(_popover, target)) { + _popover.RequestCloseAsync(); + } + } + + private void OnPopoverClosed(object sender, EventArgs e) + { + _popover.Closed -= OnPopoverClosed; + _window.PreviewMouseDown -= OnPreviewMouseDownForWindow; + + var adornerLayer = AdornerLayer.GetAdornerLayer(this.AdornedElement); + if (adornerLayer != null) { + adornerLayer.Remove(this); + } + } + + public MetroWindow Window + { + get { return _window; } + } + + public MetroPopover Popover + { + get { return _popover; } + } + + protected override Size MeasureOverride(Size constraint) + { + _popover.Measure(constraint); + return _popover.DesiredSize; + } + + protected override Size ArrangeOverride(Size finalSize) + { + var offsetY = AdornedElement.RenderSize.Height; + if (AdornedElement is Control) { + offsetY -= ((Control)AdornedElement).Margin.Bottom; + } + _popover.Arrange(new Rect(0, offsetY, finalSize.Width, finalSize.Height)); + return _popover.RenderSize; + } + + protected override Visual GetVisualChild(int index) + { + return _visuals[index]; + } + + protected override int VisualChildrenCount + { + get { return _visuals.Count; } + } + + + static bool IsDescendant(DependencyObject reference, DependencyObject node) + { + bool result = false; + DependencyObject dependencyObject = node; + while (dependencyObject != null) { + if (dependencyObject == reference) { + result = true; + break; + } + + dependencyObject = dependencyObject.GetParentObject(); + } + return result; + } + } + + #endregion + + /// + /// Displays a MetroPopover inside of the specified window, attached to the given control. + /// Note that this method returns as soon as the dialog is loaded and won't wait on a call of . + /// + /// The owning window of the dialog. + /// The target element to attach the popover to. + /// The popover content to be shown. + /// A task representing the operation. + public static Task ShowMetroPopoverAsync(this MetroWindow window, UIElement target, object popoverContent) + { + window.Dispatcher.VerifyAccess(); + var popover = new MetroPopover(target) { + Content = popoverContent + }; + SetupAndOpenPopup(window, target, popover); + + return popover.OpenAsync() + .ContinueWith(x => { + if (PopoverOpened != null) { + PopoverOpened(window, new PopoverStateChangedEventArgs() { }); + } + return popover; + }, TaskScheduler.FromCurrentSynchronizationContext()); + } + + + private static void SetupAndOpenPopup(MetroWindow window, UIElement target, MetroPopover popover) + { + var adornedLayer = AdornerLayer.GetAdornerLayer(target); + if (adornedLayer == null) { + throw new InvalidOperationException("Couldn't find adorner layer."); + } + + var popooverAdorner = new PopoverAdorner(window, target, popover); + adornedLayer.Add(popooverAdorner); + popover.Owner = window; + } + + + public delegate void PopoverStateChangedHandler(object sender, PopoverStateChangedEventArgs args); + + public static event PopoverStateChangedHandler PopoverOpened; + public static event PopoverStateChangedHandler PopoverClosed; + + } +} diff --git a/MahApps.Metro/Controls/PopoverStateChangedEventArgs.cs b/MahApps.Metro/Controls/PopoverStateChangedEventArgs.cs new file mode 100644 index 0000000000..89fd88d883 --- /dev/null +++ b/MahApps.Metro/Controls/PopoverStateChangedEventArgs.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MahApps.Metro.Controls +{ + public class PopoverStateChangedEventArgs : EventArgs + { + } +} diff --git a/MahApps.Metro/MahApps.Metro.NET45.csproj b/MahApps.Metro/MahApps.Metro.NET45.csproj index ae492d40c5..abe156ae10 100644 --- a/MahApps.Metro/MahApps.Metro.NET45.csproj +++ b/MahApps.Metro/MahApps.Metro.NET45.csproj @@ -113,6 +113,7 @@ + @@ -122,6 +123,8 @@ + + @@ -515,6 +518,10 @@ MSBuild:Compile Designer + + MSBuild:Compile + Designer + Designer MSBuild:Compile diff --git a/MahApps.Metro/MahApps.Metro.csproj b/MahApps.Metro/MahApps.Metro.csproj index bd32c1c71f..137d41e27c 100644 --- a/MahApps.Metro/MahApps.Metro.csproj +++ b/MahApps.Metro/MahApps.Metro.csproj @@ -125,6 +125,9 @@ + + + @@ -452,6 +455,10 @@ Designer MSBuild:Compile + + MSBuild:Compile + Designer + Designer MSBuild:Compile diff --git a/MahApps.Metro/Themes/Generic.xaml b/MahApps.Metro/Themes/Generic.xaml index 2377b24c10..65f4fd7eed 100644 --- a/MahApps.Metro/Themes/Generic.xaml +++ b/MahApps.Metro/Themes/Generic.xaml @@ -24,6 +24,7 @@ + diff --git a/MahApps.Metro/Themes/MetroPopover.xaml b/MahApps.Metro/Themes/MetroPopover.xaml new file mode 100644 index 0000000000..c9dfab8af5 --- /dev/null +++ b/MahApps.Metro/Themes/MetroPopover.xaml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/samples/MetroDemo/ExampleViews/OtherExamples.xaml b/samples/MetroDemo/ExampleViews/OtherExamples.xaml index 7f233676ff..3f5d62cb5d 100644 --- a/samples/MetroDemo/ExampleViews/OtherExamples.xaml +++ b/samples/MetroDemo/ExampleViews/OtherExamples.xaml @@ -23,6 +23,7 @@ + @@ -191,6 +192,17 @@ Margin="0, 0, 10, 0"> + + + + + + + + + + + diff --git a/samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs b/samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs index 14604a2c12..8cf260f970 100644 --- a/samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs +++ b/samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs @@ -27,7 +27,7 @@ public OtherExamples() var t = new DispatcherTimer(TimeSpan.FromSeconds(2), DispatcherPriority.Normal, Tick, this.Dispatcher); } - + void Tick(object sender, EventArgs e) { var dateTime = DateTime.Now; @@ -51,5 +51,11 @@ private void FlipView_SelectionChanged(object sender, SelectionChangedEventArgs break; } } + + private void ShowPopover(object sender, RoutedEventArgs e) + { + var mainWindow = Application.Current.MainWindow as MetroWindow; + mainWindow.ShowMetroPopoverAsync((UIElement)sender, new TextBox()); + } } } From 07e7aa78a2f5fccd930b0de8ec6bf35d34cd1d8f Mon Sep 17 00:00:00 2001 From: Charles Weld Date: Thu, 23 Oct 2014 17:05:14 +1100 Subject: [PATCH 02/10] Support for popovers with example. --- MahApps.Metro/Controls/MetroPopover.cs | 352 ++++++++++++---- MahApps.Metro/Controls/MetroPopoverWindow.cs | 50 +++ MahApps.Metro/Controls/PopoverManager.cs | 158 -------- MahApps.Metro/MahApps.Metro.NET45.csproj | 2 +- MahApps.Metro/MahApps.Metro.csproj | 2 +- MahApps.Metro/Themes/MetroPopover.xaml | 29 +- .../MetroDemo/ExampleViews/OtherExamples.xaml | 377 +++++++++--------- .../ExampleViews/OtherExamples.xaml.cs | 3 +- 8 files changed, 536 insertions(+), 437 deletions(-) create mode 100644 MahApps.Metro/Controls/MetroPopoverWindow.cs delete mode 100644 MahApps.Metro/Controls/PopoverManager.cs diff --git a/MahApps.Metro/Controls/MetroPopover.cs b/MahApps.Metro/Controls/MetroPopover.cs index b17ddf7734..ea8b9075c0 100644 --- a/MahApps.Metro/Controls/MetroPopover.cs +++ b/MahApps.Metro/Controls/MetroPopover.cs @@ -7,34 +7,191 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Documents; using System.Windows.Input; using System.Windows.Markup; +using System.Windows.Media; using System.Windows.Media.Animation; namespace MahApps.Metro.Controls { - + [ContentProperty("Content")] - [TemplatePart(Name = PART_ContentPresenter, Type = typeof(UIElement))] - public class MetroPopover : Control + public class MetroPopover : FrameworkElement { - private const string PART_ContentPresenter = "PART_ContentPresenter"; + #region Popover adorner + + // see tech.pro/tutorial/856/wpf-tutorial-using-a-visual-collection by Michael Kuehl + + class PopoverAdorner : Adorner + { + readonly VisualCollection _visuals; + readonly MetroPopover _popover; + readonly MetroPopoverWindow _popoverWindow; + + public PopoverAdorner(UIElement adornedElement, MetroPopover popover) + : base(adornedElement) + { + _popover = popover; + _visuals = new VisualCollection(this); + _popoverWindow = new MetroPopoverWindow(popover) { + Content = popover.Content + }; + + // bind the popover windows horizontal alignement property to the popovers + var horizontalAlignmentBinding = new Binding("HorizontalAlignment") { + Source = popover, + Mode = BindingMode.OneWay, + + }; + _popoverWindow.SetBinding(MetroPopoverWindow.HorizontalAlignmentProperty, horizontalAlignmentBinding); + + _visuals.Add(_popoverWindow); + } + + public MetroPopover Popover + { + get { return _popover; } + } + + public void UpdateContent(object content) + { + _popoverWindow.Content = content; + } + + public void ShowWindow() + { + _popoverWindow.Show(); + } + + public void HideWindow() + { + _popoverWindow.Hide(); + } + + public bool IsWindowOpen() + { + return _popoverWindow.IsLoaded && _popoverWindow.Opacity > 0; + } + + public void Detach() + { + var adornerLayer = AdornerLayer.GetAdornerLayer(AdornedElement); + if (adornerLayer != null) { + adornerLayer.Remove(this); + } + } + + public void Attach() + { + var adornerLayer = AdornerLayer.GetAdornerLayer(AdornedElement); + if (adornerLayer != null && !IsAttachedTo(adornerLayer)) { + adornerLayer.Add(this); + } + } + + bool IsAttachedTo(AdornerLayer adornerLayer) + { + if (adornerLayer != null) { + var adorners = adornerLayer.GetAdorners(AdornedElement); + return adorners != null && adorners.Contains(this); + } else { + return false; + } + } + + bool IsAdornedElementLoaded() + { + if (AdornedElement is FrameworkElement) { + return ((FrameworkElement)AdornedElement).IsLoaded; + } else { + return true; + } + } + + protected override Size MeasureOverride(Size constraint) + { + _popoverWindow.Measure(constraint); + return _popoverWindow.DesiredSize; + } + + protected override Size ArrangeOverride(Size finalSize) + { + var targetSize = AdornedElement.RenderSize; + + double offsetX; + if (Popover.HorizontalAlignment == System.Windows.HorizontalAlignment.Left) { + offsetX = 0; + } else if (Popover.HorizontalAlignment == System.Windows.HorizontalAlignment.Right) { + offsetX = targetSize.Width - finalSize.Width; + } else if (Popover.HorizontalAlignment == System.Windows.HorizontalAlignment.Center || Popover.HorizontalAlignment == System.Windows.HorizontalAlignment.Stretch) { + offsetX = (targetSize.Width - finalSize.Width) / 2.0; + } else { + offsetX = 0; + } + + var offsetY = AdornedElement.RenderSize.Height; + if (AdornedElement is Control) { + offsetY -= ((Control)AdornedElement).Margin.Bottom; + } + + _popoverWindow.Arrange(new Rect(offsetX, offsetY, finalSize.Width, finalSize.Height)); + return _popoverWindow.RenderSize; + } + + protected override Visual GetVisualChild(int index) + { + return _visuals[index]; + } + + protected override int VisualChildrenCount + { + get { return _visuals.Count; } + } + + + static bool IsDescendant(DependencyObject reference, DependencyObject node) + { + bool result = false; + DependencyObject dependencyObject = node; + while (dependencyObject != null) { + if (dependencyObject == reference) { + result = true; + break; + } + + dependencyObject = dependencyObject.GetParentObject(); + } + return result; + } + } + + #endregion + + PopoverAdorner _adorner; static MetroPopover() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MetroPopover), new FrameworkPropertyMetadata(typeof(MetroPopover))); } - public MetroPopover(UIElement target) + public MetroPopover() { - Target = target; + this.Loaded += OnLoaded; + this.Unloaded += OnUnloaded; } + + public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(MetroPopover), new PropertyMetadata(null, OnContentChanged)); + public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register("IsOpen", typeof(bool), typeof(MetroPopover), new PropertyMetadata(false, OnIsOpenChanged)); + public static readonly DependencyProperty TargetProperty = DependencyProperty.Register("Target", typeof(UIElement), typeof(MetroPopover), new PropertyMetadata(null, OnTargetChanged)); + public static readonly DependencyProperty AutoCloseProperty = DependencyProperty.Register("AutoClose", typeof(bool), typeof(MetroPopover), new PropertyMetadata(true)); - public MetroWindow Owner { get; internal set; } - public UIElement Target { get; private set; } - - public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(MetroPopover), new PropertyMetadata(null)); - public static readonly DependencyProperty IsOpenProperty = DependencyProperty.Register("IsOpen", typeof(bool), typeof(MetroPopover), new PropertyMetadata(false)); + public UIElement Target + { + get { return (UIElement)GetValue(TargetProperty); } + set { SetValue(TargetProperty, value); } + } public object Content { @@ -48,35 +205,20 @@ public bool IsOpen set { SetValue(IsOpenProperty, value); } } - public event EventHandler Shown; - public event EventHandler Closed; - - public Task OpenAsync() + public bool AutoClose { - return WaitForLoadAsync() - .ContinueWith(t => { - OnOpened(); - }, TaskScheduler.FromCurrentSynchronizationContext()); + get { return (bool)GetValue(AutoCloseProperty); } + set { SetValue(AutoCloseProperty, value); } } - public Task RequestCloseAsync() + public void Open() { - if (OnRequestClose()) { - return WaitForCloseAsync() - .ContinueWith(t => { - OnClosed(); - return true; - }, TaskScheduler.FromCurrentSynchronizationContext()); - } - return Task.Factory.StartNew(() => false); + IsOpen = true; } - protected virtual void OnOpened() + public void Close() { - this.IsOpen = true; - if (Shown != null) { - Shown(this, EventArgs.Empty); - } + IsOpen = false; } /// @@ -88,70 +230,116 @@ protected virtual bool OnRequestClose() return true; //allow the dialog to close. } - protected virtual void OnClosed() + private static void OnTargetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - this.IsOpen = false; - if (Closed != null) { - Closed(this, EventArgs.Empty); - } - + var popover = (MetroPopover)d; + popover.SetupAdorner(); } - /// - /// Waits for the popover to become ready for interaction. - /// - /// A task that represents the operation and it's status. - Task WaitForLoadAsync() + private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { - Dispatcher.VerifyAccess(); - - if (this.IsLoaded) return new Task(() => { }); - - if (!false) - this.Opacity = 1.0; //skip the animation - - TaskCompletionSource tcs = new TaskCompletionSource(); - - RoutedEventHandler handler = null; - handler = new RoutedEventHandler((sender, args) => { - this.Loaded -= handler; - - tcs.TrySetResult(null); - }); - - this.Loaded += handler; - - return tcs.Task; + var popover = (MetroPopover)d; + var oldChild = e.OldValue; + var newChild = e.NewValue; + popover.RemoveLogicalChild(oldChild); + popover.AddLogicalChild(newChild); + + // rebuild + if (popover._adorner != null) { + popover._adorner.UpdateContent(newChild); + } + } + + private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var popover = (MetroPopover)d; + var isOpen = (bool)e.NewValue; + + if (popover._adorner != null) { + if (isOpen) { + popover._adorner.ShowWindow(); + } else { + popover._adorner.HideWindow(); + } + } } - Task WaitForCloseAsync() + // the adorner can only attach to loaded targets so call attach and detach when the target is loaded and unloaded respecitively + void OnTargetLoaded(object sender, RoutedEventArgs e) { - TaskCompletionSource tcs = new TaskCompletionSource(); + _adorner.Attach(); + } - if (false) { - Storyboard closingStoryboard = this.Resources["PopoverCloseStoryboard"] as Storyboard; + private void OnTargetUnloaded(object sender, RoutedEventArgs e) + { + _adorner.Detach(); + } - if (closingStoryboard == null) - throw new InvalidOperationException("Unable to find the dialog closing storyboard. Did you forget to add MetroPopoverDialog.xaml to your merged dictionaries?"); + // Attach to parent window. - EventHandler handler = null; - handler = new EventHandler((sender, args) => { - closingStoryboard.Completed -= handler; + void OnUnloaded(object sender, RoutedEventArgs e) + { + Window owner = this.TryFindParent(); + if(owner != null) { + owner.PreviewMouseDown -= OnPreviewOwningWindowMouseDown; + } + } - tcs.TrySetResult(null); - }); + void OnLoaded(object sender, RoutedEventArgs e) + { + Window owner = this.TryFindParent(); + if (owner != null) { + owner.PreviewMouseDown += OnPreviewOwningWindowMouseDown; + } + } - closingStoryboard = closingStoryboard.Clone(); + private void OnPreviewOwningWindowMouseDown(object sender, MouseButtonEventArgs e) + { + var originalSource = e.OriginalSource as Visual; + if (AutoClose && IsPopoverOpen() && + !Target.IsAncestorOf(originalSource) && + !_adorner.IsAncestorOf(originalSource)) { + Close(); + } + } - closingStoryboard.Completed += handler; + /// + /// Programically determines if the popover is actually open & visible. + /// + /// + bool IsPopoverOpen() + { + return _adorner != null && _adorner.IsWindowOpen(); + } - closingStoryboard.Begin(this); - } else { - this.Opacity = 0.0; - tcs.TrySetResult(null); //skip the animation + private void SetupAdorner() + { + if (_adorner == null || _adorner.AdornedElement != Target) { + if (_adorner != null) { + _adorner.Detach(); + + var targetFrameworkElement = _adorner.AdornedElement as FrameworkElement; + if (targetFrameworkElement != null) { + targetFrameworkElement.Loaded -= OnTargetLoaded; + targetFrameworkElement.Unloaded -= OnTargetUnloaded; + } + + _adorner = null; + } + + if (Target != null) { + _adorner = new PopoverAdorner(Target, this); + _adorner.Attach(); + + var targetFrameworkElement = Target as FrameworkElement; + if (targetFrameworkElement != null) { + targetFrameworkElement.Loaded += OnTargetLoaded; + targetFrameworkElement.Unloaded += OnTargetUnloaded; + } + } + } else if (_adorner != null) { + _adorner.Attach(); } - - return tcs.Task; } diff --git a/MahApps.Metro/Controls/MetroPopoverWindow.cs b/MahApps.Metro/Controls/MetroPopoverWindow.cs new file mode 100644 index 0000000000..ca4f1c362a --- /dev/null +++ b/MahApps.Metro/Controls/MetroPopoverWindow.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media.Animation; + +namespace MahApps.Metro.Controls +{ + public class MetroPopoverWindow : ContentControl + { + readonly MetroPopover _popover; + + static MetroPopoverWindow() + { + DefaultStyleKeyProperty.OverrideMetadata(typeof(MetroPopoverWindow), new FrameworkPropertyMetadata(typeof(MetroPopoverWindow))); + } + + public MetroPopoverWindow(MetroPopover popover) + { + _popover = popover; + } + + + /// + /// Waits for the popover to become ready for interaction. + /// + /// A task that represents the operation and it's status. + public void Show() + { + Dispatcher.VerifyAccess(); + + + this.Opacity = 1.0; + this.Visibility = System.Windows.Visibility.Visible; + } + + public void Hide() + { + Dispatcher.VerifyAccess(); + + this.Opacity = 0.0; + this.Visibility = System.Windows.Visibility.Collapsed; + } + + } + +} diff --git a/MahApps.Metro/Controls/PopoverManager.cs b/MahApps.Metro/Controls/PopoverManager.cs deleted file mode 100644 index 6052cf0bf6..0000000000 --- a/MahApps.Metro/Controls/PopoverManager.cs +++ /dev/null @@ -1,158 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; - -namespace MahApps.Metro.Controls -{ - public static class PopoverManager - { - #region Popover adorner - - // see tech.pro/tutorial/856/wpf-tutorial-using-a-visual-collection by Michael Kuehl - - class PopoverAdorner : Adorner - { - readonly MetroWindow _window; - readonly VisualCollection _visuals; - readonly MetroPopover _popover; - - public PopoverAdorner(MetroWindow window, UIElement adornedElement, MetroPopover popover) - : base(adornedElement) - { - _window = window; - _popover = popover; - _visuals = new VisualCollection(this); - _visuals.Add(popover); - - _popover.Closed += OnPopoverClosed; - _window.PreviewMouseDown += OnPreviewMouseDownForWindow; - } - - void OnPreviewMouseDownForWindow(object sender, MouseButtonEventArgs e) - { - var target = e.OriginalSource as DependencyObject; - if (!IsDescendant(_popover, target)) { - _popover.RequestCloseAsync(); - } - } - - private void OnPopoverClosed(object sender, EventArgs e) - { - _popover.Closed -= OnPopoverClosed; - _window.PreviewMouseDown -= OnPreviewMouseDownForWindow; - - var adornerLayer = AdornerLayer.GetAdornerLayer(this.AdornedElement); - if (adornerLayer != null) { - adornerLayer.Remove(this); - } - } - - public MetroWindow Window - { - get { return _window; } - } - - public MetroPopover Popover - { - get { return _popover; } - } - - protected override Size MeasureOverride(Size constraint) - { - _popover.Measure(constraint); - return _popover.DesiredSize; - } - - protected override Size ArrangeOverride(Size finalSize) - { - var offsetY = AdornedElement.RenderSize.Height; - if (AdornedElement is Control) { - offsetY -= ((Control)AdornedElement).Margin.Bottom; - } - _popover.Arrange(new Rect(0, offsetY, finalSize.Width, finalSize.Height)); - return _popover.RenderSize; - } - - protected override Visual GetVisualChild(int index) - { - return _visuals[index]; - } - - protected override int VisualChildrenCount - { - get { return _visuals.Count; } - } - - - static bool IsDescendant(DependencyObject reference, DependencyObject node) - { - bool result = false; - DependencyObject dependencyObject = node; - while (dependencyObject != null) { - if (dependencyObject == reference) { - result = true; - break; - } - - dependencyObject = dependencyObject.GetParentObject(); - } - return result; - } - } - - #endregion - - /// - /// Displays a MetroPopover inside of the specified window, attached to the given control. - /// Note that this method returns as soon as the dialog is loaded and won't wait on a call of . - /// - /// The owning window of the dialog. - /// The target element to attach the popover to. - /// The popover content to be shown. - /// A task representing the operation. - public static Task ShowMetroPopoverAsync(this MetroWindow window, UIElement target, object popoverContent) - { - window.Dispatcher.VerifyAccess(); - var popover = new MetroPopover(target) { - Content = popoverContent - }; - SetupAndOpenPopup(window, target, popover); - - return popover.OpenAsync() - .ContinueWith(x => { - if (PopoverOpened != null) { - PopoverOpened(window, new PopoverStateChangedEventArgs() { }); - } - return popover; - }, TaskScheduler.FromCurrentSynchronizationContext()); - } - - - private static void SetupAndOpenPopup(MetroWindow window, UIElement target, MetroPopover popover) - { - var adornedLayer = AdornerLayer.GetAdornerLayer(target); - if (adornedLayer == null) { - throw new InvalidOperationException("Couldn't find adorner layer."); - } - - var popooverAdorner = new PopoverAdorner(window, target, popover); - adornedLayer.Add(popooverAdorner); - popover.Owner = window; - } - - - public delegate void PopoverStateChangedHandler(object sender, PopoverStateChangedEventArgs args); - - public static event PopoverStateChangedHandler PopoverOpened; - public static event PopoverStateChangedHandler PopoverClosed; - - } -} diff --git a/MahApps.Metro/MahApps.Metro.NET45.csproj b/MahApps.Metro/MahApps.Metro.NET45.csproj index abe156ae10..658871a978 100644 --- a/MahApps.Metro/MahApps.Metro.NET45.csproj +++ b/MahApps.Metro/MahApps.Metro.NET45.csproj @@ -114,6 +114,7 @@ + @@ -123,7 +124,6 @@ - diff --git a/MahApps.Metro/MahApps.Metro.csproj b/MahApps.Metro/MahApps.Metro.csproj index 137d41e27c..d75f3d4f75 100644 --- a/MahApps.Metro/MahApps.Metro.csproj +++ b/MahApps.Metro/MahApps.Metro.csproj @@ -116,6 +116,7 @@ + @@ -126,7 +127,6 @@ - diff --git a/MahApps.Metro/Themes/MetroPopover.xaml b/MahApps.Metro/Themes/MetroPopover.xaml index c9dfab8af5..f195ad8c18 100644 --- a/MahApps.Metro/Themes/MetroPopover.xaml +++ b/MahApps.Metro/Themes/MetroPopover.xaml @@ -22,8 +22,8 @@ To="0" /> - + @@ -32,8 +32,8 @@ - - + + @@ -56,24 +56,27 @@ - - - - - + + + + + + + + - \ No newline at end of file diff --git a/samples/MetroDemo/ExampleViews/OtherExamples.xaml b/samples/MetroDemo/ExampleViews/OtherExamples.xaml index 3f5d62cb5d..c63a843d99 100644 --- a/samples/MetroDemo/ExampleViews/OtherExamples.xaml +++ b/samples/MetroDemo/ExampleViews/OtherExamples.xaml @@ -18,191 +18,208 @@ - - - - - - - - - - - - - - - + + - - - - - - - - - - + + + + + + + + + + + + + + + + + Show Popover + + This popover is aligned to the bottom left. + + Show Popover + + This popover is aligned to the bottom center. + + Show Popover + + This popover is aligned to the bottom right. + + - + diff --git a/samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs b/samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs index 8cf260f970..be7b4743de 100644 --- a/samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs +++ b/samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs @@ -54,8 +54,7 @@ private void FlipView_SelectionChanged(object sender, SelectionChangedEventArgs private void ShowPopover(object sender, RoutedEventArgs e) { - var mainWindow = Application.Current.MainWindow as MetroWindow; - mainWindow.ShowMetroPopoverAsync((UIElement)sender, new TextBox()); + } } } From 272bd6cc59c552cb3c315d0735b60e40a19b2b91 Mon Sep 17 00:00:00 2001 From: Charles Weld Date: Thu, 23 Oct 2014 17:38:04 +1100 Subject: [PATCH 03/10] Style updates --- MahApps.Metro/Themes/MetroPopover.xaml | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/MahApps.Metro/Themes/MetroPopover.xaml b/MahApps.Metro/Themes/MetroPopover.xaml index f195ad8c18..3140474cbf 100644 --- a/MahApps.Metro/Themes/MetroPopover.xaml +++ b/MahApps.Metro/Themes/MetroPopover.xaml @@ -6,27 +6,11 @@ - - - - - - - - - + From 2f6946d74c8c7e1c124f8a064fd1343cbac5c000 Mon Sep 17 00:00:00 2001 From: Charles Weld Date: Fri, 24 Oct 2014 12:27:34 +1100 Subject: [PATCH 04/10] Style fixes for MetroPopover. --- MahApps.Metro/Themes/MetroPopover.xaml | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/MahApps.Metro/Themes/MetroPopover.xaml b/MahApps.Metro/Themes/MetroPopover.xaml index 3140474cbf..5325f7ce72 100644 --- a/MahApps.Metro/Themes/MetroPopover.xaml +++ b/MahApps.Metro/Themes/MetroPopover.xaml @@ -16,17 +16,12 @@ - - - - - - - + + - + From f67ef91faca5814dfca34c70c0310d0031e62cd4 Mon Sep 17 00:00:00 2001 From: Charles Weld Date: Fri, 24 Oct 2014 12:43:43 +1100 Subject: [PATCH 05/10] Removed unused classes and methods from popover implementation. --- .../Controls/PopoverStateChangedEventArgs.cs | 11 ----------- MahApps.Metro/MahApps.Metro.NET45.csproj | 1 - MahApps.Metro/MahApps.Metro.csproj | 1 - samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs | 5 ----- 4 files changed, 18 deletions(-) delete mode 100644 MahApps.Metro/Controls/PopoverStateChangedEventArgs.cs diff --git a/MahApps.Metro/Controls/PopoverStateChangedEventArgs.cs b/MahApps.Metro/Controls/PopoverStateChangedEventArgs.cs deleted file mode 100644 index 89fd88d883..0000000000 --- a/MahApps.Metro/Controls/PopoverStateChangedEventArgs.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace MahApps.Metro.Controls -{ - public class PopoverStateChangedEventArgs : EventArgs - { - } -} diff --git a/MahApps.Metro/MahApps.Metro.NET45.csproj b/MahApps.Metro/MahApps.Metro.NET45.csproj index 4afd471d72..dc2ca05239 100644 --- a/MahApps.Metro/MahApps.Metro.NET45.csproj +++ b/MahApps.Metro/MahApps.Metro.NET45.csproj @@ -126,7 +126,6 @@ - diff --git a/MahApps.Metro/MahApps.Metro.csproj b/MahApps.Metro/MahApps.Metro.csproj index ed0ed83267..46704ed92e 100644 --- a/MahApps.Metro/MahApps.Metro.csproj +++ b/MahApps.Metro/MahApps.Metro.csproj @@ -129,7 +129,6 @@ - diff --git a/samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs b/samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs index be7b4743de..f38aebcbbf 100644 --- a/samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs +++ b/samples/MetroDemo/ExampleViews/OtherExamples.xaml.cs @@ -51,10 +51,5 @@ private void FlipView_SelectionChanged(object sender, SelectionChangedEventArgs break; } } - - private void ShowPopover(object sender, RoutedEventArgs e) - { - - } } } From b17f5eedd6946ffeca52781c20cef63f70acb080 Mon Sep 17 00:00:00 2001 From: Charles Weld Date: Fri, 24 Oct 2014 16:43:33 +1100 Subject: [PATCH 06/10] Fixed popup event handling bug Previously the owning window's PreviewMouseDown event, used to auto close the popover, was being subscribed to on the popover's load event and unsubscribed on unload however this didn't take into account that the Load could be called multiple times. The popover now keeps track of if it's subscribe to the given event using a disposable handler, which is also used to unsubscribe. --- MahApps.Metro/Controls/MetroPopover.cs | 69 +++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/MahApps.Metro/Controls/MetroPopover.cs b/MahApps.Metro/Controls/MetroPopover.cs index ea8b9075c0..25a94880c5 100644 --- a/MahApps.Metro/Controls/MetroPopover.cs +++ b/MahApps.Metro/Controls/MetroPopover.cs @@ -20,6 +20,39 @@ namespace MahApps.Metro.Controls [ContentProperty("Content")] public class MetroPopover : FrameworkElement { + #region Disposable event handler + + static class Disposable + { + public static IDisposable Subscription(Action subscribe, Action unsubscribe, TEventHandler handler) + { + subscribe(handler); + return new DisposableSubscription(unsubscribe, handler); + } + } + + /// + /// Unsubscribes the given event handler on disposal. + /// + class DisposableSubscription : IDisposable + { + readonly Action description; + readonly TEventHandler handler; + + public DisposableSubscription(Action unsubscribe, TEventHandler handler) + { + this.description = unsubscribe; + this.handler = handler; + } + + public void Dispose() + { + description(handler); + } + } + + #endregion + #region Popover adorner // see tech.pro/tutorial/856/wpf-tutorial-using-a-visual-collection by Michael Kuehl @@ -170,6 +203,7 @@ static bool IsDescendant(DependencyObject reference, DependencyObject node) #endregion PopoverAdorner _adorner; + IDisposable _previewOwningWindowClickSubscription; static MetroPopover() { @@ -187,35 +221,53 @@ public MetroPopover() public static readonly DependencyProperty TargetProperty = DependencyProperty.Register("Target", typeof(UIElement), typeof(MetroPopover), new PropertyMetadata(null, OnTargetChanged)); public static readonly DependencyProperty AutoCloseProperty = DependencyProperty.Register("AutoClose", typeof(bool), typeof(MetroPopover), new PropertyMetadata(true)); + /// + /// The element to place the popover under. Note horizontal placement is controlled via the property. + /// public UIElement Target { get { return (UIElement)GetValue(TargetProperty); } set { SetValue(TargetProperty, value); } } + /// + /// Gets or sets the content to be displayed in the popover. + /// public object Content { get { return (object)GetValue(ContentProperty); } set { SetValue(ContentProperty, value); } } + /// + /// Gets or sets a value indicating if the popover open. + /// public bool IsOpen { get { return (bool)GetValue(IsOpenProperty); } set { SetValue(IsOpenProperty, value); } } + /// + /// Gets or sets a value that indicates if the popover should be automatically closed when the user clicks else where in the owning window. + /// public bool AutoClose { get { return (bool)GetValue(AutoCloseProperty); } set { SetValue(AutoCloseProperty, value); } } + /// + /// Opens the popover. + /// public void Open() { IsOpen = true; } + /// + /// Closes the popover. + /// public void Close() { IsOpen = false; @@ -277,26 +329,27 @@ private void OnTargetUnloaded(object sender, RoutedEventArgs e) // Attach to parent window. - void OnUnloaded(object sender, RoutedEventArgs e) + void OnLoaded(object sender, RoutedEventArgs e) { Window owner = this.TryFindParent(); - if(owner != null) { - owner.PreviewMouseDown -= OnPreviewOwningWindowMouseDown; + if (owner != null && _previewOwningWindowClickSubscription == null) { + _previewOwningWindowClickSubscription = Disposable.Subscription(handler => owner.PreviewMouseDown += handler, handler => owner.PreviewMouseDown -= handler, OnPreviewOwningWindowMouseDown); } } - void OnLoaded(object sender, RoutedEventArgs e) + void OnUnloaded(object sender, RoutedEventArgs e) { - Window owner = this.TryFindParent(); - if (owner != null) { - owner.PreviewMouseDown += OnPreviewOwningWindowMouseDown; + if (_previewOwningWindowClickSubscription != null) { + _previewOwningWindowClickSubscription.Dispose(); + _previewOwningWindowClickSubscription = null; } } - + private void OnPreviewOwningWindowMouseDown(object sender, MouseButtonEventArgs e) { var originalSource = e.OriginalSource as Visual; if (AutoClose && IsPopoverOpen() && + originalSource != null && // didn't click on target or popover (adorner) !Target.IsAncestorOf(originalSource) && !_adorner.IsAncestorOf(originalSource)) { Close(); From 5e292f74f312dba3f969a2603e0a0462faf16101 Mon Sep 17 00:00:00 2001 From: Charles Weld Date: Fri, 24 Oct 2014 21:12:12 +1100 Subject: [PATCH 07/10] MetroPopover fixes * Background and Foreground colors are now dynamically applied using the WhiteBrush and BlackBrush respectively. * MetroPopover doesn't partake in layout (Visibility is always Collapsed). * Popover adorner is only attached when open. --- MahApps.Metro/Controls/MetroPopover.cs | 52 +++++++++++-------- MahApps.Metro/Themes/MetroPopover.xaml | 12 +++-- .../MetroDemo/ExampleViews/OtherExamples.xaml | 11 ++-- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/MahApps.Metro/Controls/MetroPopover.cs b/MahApps.Metro/Controls/MetroPopover.cs index 25a94880c5..283d38be1b 100644 --- a/MahApps.Metro/Controls/MetroPopover.cs +++ b/MahApps.Metro/Controls/MetroPopover.cs @@ -72,22 +72,25 @@ public PopoverAdorner(UIElement adornedElement, MetroPopover popover) Content = popover.Content }; - // bind the popover windows horizontal alignement property to the popovers - var horizontalAlignmentBinding = new Binding("HorizontalAlignment") { - Source = popover, - Mode = BindingMode.OneWay, - - }; - _popoverWindow.SetBinding(MetroPopoverWindow.HorizontalAlignmentProperty, horizontalAlignmentBinding); - + // bind key popover window properties to the popovers + _popoverWindow.SetBinding(MetroPopoverWindow.HorizontalAlignmentProperty, new Binding("HorizontalAlignment") { Source = popover, Mode = BindingMode.OneWay }); + //_popoverWindow.SetBinding(MetroPopoverWindow.WidthProperty, new Binding("Width") { Source = popover, Mode = BindingMode.OneWay }); + //_popoverWindow.SetBinding(MetroPopoverWindow.MinWidthProperty, new Binding("MinWidth") { Source = popover, Mode = BindingMode.OneWay }); + //_popoverWindow.SetBinding(MetroPopoverWindow.MaxWidthProperty, new Binding("MaxWidth") { Source = popover, Mode = BindingMode.OneWay }); _visuals.Add(_popoverWindow); + this.AddLogicalChild(_popoverWindow); } - + public MetroPopover Popover { get { return _popover; } } + public MetroPopoverWindow PopoverWindow + { + get { return _popoverWindow; } + } + public void UpdateContent(object content) { _popoverWindow.Content = content; @@ -95,12 +98,14 @@ public void UpdateContent(object content) public void ShowWindow() { + Attach(); _popoverWindow.Show(); } public void HideWindow() { _popoverWindow.Hide(); + Detach(); } public bool IsWindowOpen() @@ -144,7 +149,7 @@ bool IsAdornedElementLoaded() } protected override Size MeasureOverride(Size constraint) - { + { _popoverWindow.Measure(constraint); return _popoverWindow.DesiredSize; } @@ -208,6 +213,14 @@ static bool IsDescendant(DependencyObject reference, DependencyObject node) static MetroPopover() { DefaultStyleKeyProperty.OverrideMetadata(typeof(MetroPopover), new FrameworkPropertyMetadata(typeof(MetroPopover))); + + // Ensure visibility is always collapsed + UIElement.VisibilityProperty.OverrideMetadata(typeof(MetroPopover), new FrameworkPropertyMetadata(Visibility.Collapsed, null, new CoerceValueCallback(MetroPopover.CoerceCollapsedVisibility))); + } + + private static object CoerceCollapsedVisibility(DependencyObject d, object baseValue) + { + return Visibility.Collapsed; } public MetroPopover() @@ -293,8 +306,6 @@ private static void OnContentChanged(DependencyObject d, DependencyPropertyChang var popover = (MetroPopover)d; var oldChild = e.OldValue; var newChild = e.NewValue; - popover.RemoveLogicalChild(oldChild); - popover.AddLogicalChild(newChild); // rebuild if (popover._adorner != null) { @@ -317,11 +328,7 @@ private static void OnIsOpenChanged(DependencyObject d, DependencyPropertyChange } // the adorner can only attach to loaded targets so call attach and detach when the target is loaded and unloaded respecitively - void OnTargetLoaded(object sender, RoutedEventArgs e) - { - _adorner.Attach(); - } - + private void OnTargetUnloaded(object sender, RoutedEventArgs e) { _adorner.Detach(); @@ -356,6 +363,12 @@ private void OnPreviewOwningWindowMouseDown(object sender, MouseButtonEventArgs } } + protected override Size MeasureOverride(Size availableSize) + { + // The MetroPopover control itself isn't ever rendered on screen so set the measure to 0. + return default(Size); + } + /// /// Programically determines if the popover is actually open & visible. /// @@ -373,7 +386,6 @@ private void SetupAdorner() var targetFrameworkElement = _adorner.AdornedElement as FrameworkElement; if (targetFrameworkElement != null) { - targetFrameworkElement.Loaded -= OnTargetLoaded; targetFrameworkElement.Unloaded -= OnTargetUnloaded; } @@ -382,16 +394,12 @@ private void SetupAdorner() if (Target != null) { _adorner = new PopoverAdorner(Target, this); - _adorner.Attach(); var targetFrameworkElement = Target as FrameworkElement; if (targetFrameworkElement != null) { - targetFrameworkElement.Loaded += OnTargetLoaded; targetFrameworkElement.Unloaded += OnTargetUnloaded; } } - } else if (_adorner != null) { - _adorner.Attach(); } } diff --git a/MahApps.Metro/Themes/MetroPopover.xaml b/MahApps.Metro/Themes/MetroPopover.xaml index 5325f7ce72..6db0ed7daf 100644 --- a/MahApps.Metro/Themes/MetroPopover.xaml +++ b/MahApps.Metro/Themes/MetroPopover.xaml @@ -8,9 +8,9 @@ - + - + @@ -21,13 +21,13 @@ - + - +